Skip to content

Commit

Permalink
added support for entity
Browse files Browse the repository at this point in the history
  • Loading branch information
janatjak committed Jul 25, 2017
1 parent 4385d7f commit 72d2b16
Show file tree
Hide file tree
Showing 28 changed files with 898 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ before_script:
- mysql -e 'SET PASSWORD FOR "root"@"localhost" = PASSWORD("");'

script:
- php vendor/bin/phpstan analyse ./src ./tests --level 6
- php vendor/bin/phpstan analyse ./src ./tests --level 7
- php vendor/bin/phpcs --standard=PSR2 ./src ./tests
- php vendor/bin/tester tests -p phpdbg --coverage ./coverage.xml --coverage-src ./src

Expand Down
4 changes: 4 additions & 0 deletions client-side/dist/inline.css
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
background-color: rgba(0, 182, 109, 0.1);
border-color: #00b66d;
}
.inline-editing.inline-content-warning {
background-color: rgba(182, 160, 12, 0.1);
border-color: #b6b357;
}
.inline-editing.inline-content-error {
background-color: rgba(216, 32, 43, 0.1);
border-color: #d8202b;
Expand Down
42 changes: 25 additions & 17 deletions client-side/dist/inline.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ var Inline = (function () {
]
}
};
this.lastSpecificUniqueId = 0;
this.updateSpecificContent = function (evt) {
var el = evt.target;
var key = el.id;
Expand Down Expand Up @@ -162,24 +161,36 @@ var Inline = (function () {
statusData = JSON.parse(xhr.responseText);
}
catch (e) {
alert('Server error');
alert('Server error (json)');
}
var _loop_1 = function (editableId) {
var el = document.getElementById(editableId);
if (el && statusData[editableId].status === 0) {
delete _this.changes[editableId];
el.classList.add('inline-content-success');
setTimeout(function () { return el.classList.remove('inline-content-success'); }, 500);
console.log(editableId);
console.log(el);
if (el) {
switch (statusData[editableId].status) {
case 0:
delete _this.changes[editableId];
el.classList.add('inline-content-success');
setTimeout(function () { return el.classList.remove('inline-content-success'); }, 500);
break;
case 1:
el.classList.add('inline-content-warning');
break;
default:
el.classList.add('inline-content-error');
var errMsg = document.createElement('span');
errMsg.classList.add('inline-error-msg');
errMsg.innerHTML = statusData[editableId].message;
el.parentNode.insertBefore(errMsg, el.nextSibling);
break;
}
}
else if (el) {
el.classList.add('inline-content-error');
var errMsg = document.createElement('span');
errMsg.classList.add('inline-error-msg');
errMsg.innerHTML = statusData[editableId].message;
el.parentNode.insertBefore(errMsg, el.nextSibling);
else if (statusData[editableId].message) {
alert(statusData[editableId].message);
}
else {
alert(statusData[editableId].message);
alert('Server error (unknown)');
}
};
for (var editableId in statusData) {
Expand All @@ -190,7 +201,7 @@ var Inline = (function () {
xhr.send(JSON.stringify(this.changes));
};
Inline.prototype.clearErrors = function () {
this.editablesForeach(function (el) { return el.classList.remove('inline-content-error'); });
this.editablesForeach(function (el) { return el.classList.remove('inline-content-error', 'inline-content-warning'); });
var msgElems = document.querySelectorAll('.inline-error-msg');
for (var i = 0; i < msgElems.length; i++) {
msgElems[i].remove();
Expand Down Expand Up @@ -248,9 +259,6 @@ var Inline = (function () {
var _this = this;
this.editablesForeach(function (el) {
if (el.classList.contains('inline-editing-specific')) {
if (!el.id) {
el.id = 'mcex_' + _this.lastSpecificUniqueId++;
}
el.addEventListener('keypress', function (evt) {
if (evt.which === 13) {
evt.preventDefault();
Expand Down
5 changes: 5 additions & 0 deletions client-side/src/less/inline.less
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@
border-color: #00b66d;
}

&.inline-content-warning {
background-color: rgba(182, 160, 12, 0.1);
border-color: #b6b357;
}

&.inline-content-error {
background-color: rgba(216, 32, 43, 0.1);
border-color: #d8202b;
Expand Down
49 changes: 27 additions & 22 deletions client-side/src/ts/inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,6 @@ class Inline {
}
}

// counter for specific entity elements
private lastSpecificUniqueId = 0

/*
// flatpickr main instance
private flatpickr: any
Expand Down Expand Up @@ -216,24 +213,36 @@ class Inline {
try {
statusData = JSON.parse(xhr.responseText)
} catch (e) {
alert('Server error');
alert('Server error (json)');
}

for (let editableId in statusData) {
let el = document.getElementById(editableId)
if (el && statusData[editableId].status === 0) {
// remove from changes
delete this.changes[editableId]
el.classList.add('inline-content-success')
setTimeout(() => el.classList.remove('inline-content-success'), 500)
} else if (el) {
el.classList.add('inline-content-error')
let errMsg = document.createElement('span')
errMsg.classList.add('inline-error-msg')
errMsg.innerHTML = statusData[editableId].message
el.parentNode.insertBefore(errMsg, el.nextSibling);
} else {

if (el) {
switch (statusData[editableId].status) {
case 0:
// ok - remove from changes
delete this.changes[editableId]
el.classList.add('inline-content-success')
setTimeout(() => el.classList.remove('inline-content-success'), 500)
break;
case 1:
// warning
el.classList.add('inline-content-warning')
break;
default:
el.classList.add('inline-content-error')
let errMsg = document.createElement('span')
errMsg.classList.add('inline-error-msg')
errMsg.innerHTML = statusData[editableId].message
el.parentNode.insertBefore(errMsg, el.nextSibling);
break;
}
} else if (statusData[editableId].message) {
alert(statusData[editableId].message);
} else {
alert('Server error (unknown)')
}
}

Expand All @@ -246,7 +255,7 @@ class Inline {
// clear error messages and red colors
public clearErrors() {
// clear error content styles
this.editablesForeach((el) => el.classList.remove('inline-content-error'))
this.editablesForeach((el) => el.classList.remove('inline-content-error', 'inline-content-warning'))

// clear error messages
let msgElems = document.querySelectorAll('.inline-error-msg')
Expand Down Expand Up @@ -322,12 +331,8 @@ class Inline {

// register specific entity listeners and generate unique ids
private applySpecificEditable() {
this.editablesForeach((el) => {
this.editablesForeach((el: HTMLBodyElement) => {
if (el.classList.contains('inline-editing-specific')) {
if (!el.id) {
el.id = 'mcex_' + this.lastSpecificUniqueId++;
}

el.addEventListener('keypress', (evt: KeyboardEvent) => {
if (evt.which === 13) {
evt.preventDefault()
Expand Down
15 changes: 11 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,24 @@
"psr/cache": "^1.0"
},
"require-dev": {
"doctrine/dbal": "^2.5",
"doctrine/orm": "^2.5",
"nette/database": "^2.4",
"dibi/dibi": "^3.0",
"symfony/property-access": "^3.0",
"symfony/validator": "^3.0",
"squizlabs/php_codesniffer": "^3.0",
"nette/tester": "dev-master",
"phpstan/phpstan": "dev-master"
"tracy/tracy": "^2.4",
"nette/tester": "@dev",
"phpstan/phpstan": "@dev",
"mockery/mockery": "dev-master"
},
"suggest": {
"doctrine/dbal": "If you wanna use DBAL persistence layer",
"nette/database": "If you wanna use Nette Database persistence layer",
"dibi/dibi": "If you wanna use Dibi persistence layer"
"dibi/dibi": "If you wanna use Dibi persistence layer",
"doctrine/orm": "If you wanna use entity features",
"symfony/validator": "If you wanna use entity validation features",
"symfony/property-access": "If you wanna use entity features"
},
"autoload": {
"psr-4": {
Expand Down
13 changes: 13 additions & 0 deletions src/Exception/InvalidDataException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);

namespace XcoreCMS\InlineEditing\Exception;

use Exception;

/**
* @author Jakub Janata <jakubjanata@gmail.com>
*/
class InvalidDataException extends Exception
{
}
132 changes: 132 additions & 0 deletions src/Model/Entity/EntityPersister.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);

namespace XcoreCMS\InlineEditing\Model\Entity;

use Doctrine\ORM\EntityManager;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use XcoreCMS\InlineEditing\Exception\InvalidDataException;
use XcoreCMS\InlineEditing\Model\Entity\HtmlEntityElement\Element;
use XcoreCMS\InlineEditing\Model\Entity\HtmlEntityElement\ElementEntityBaseContainer;
use XcoreCMS\InlineEditing\Model\Entity\HtmlEntityElement\ElementEntityContainer;
use XcoreCMS\InlineEditing\Model\Entity\Mapper\InlineMapperInterface;

/**
* @author Jakub Janata <jakubjanata@gmail.com>
*/
class EntityPersister
{
/** @var EntityManager */
private $entityManager;

/** @var ValidatorInterface|null */
private $validator;

/** @var ElementEntityContainer[] */
private $entityElementContainers;

/** @var PropertyAccessor */
private $propertyAccessor;

/**
* @param EntityManager $entityManager
* @param ValidatorInterface $validator
*/
public function __construct(EntityManager $entityManager, ValidatorInterface $validator = null)
{
$this->entityManager = $entityManager;
$this->validator = $validator;
$this->entityElementContainers = [];
$this->propertyAccessor = new PropertyAccessor;
}

/**
* @param Element $element
*/
public function update(Element $element)
{
$key = $element->getEntityHash();
$container = $this->entityElementContainers[$key] ?? null;

if ($container === null) {
// load entity from database
$e = $this->entityManager->find($element->getClassName(), $element->getId());
$container = new ElementEntityContainer($e);
$this->entityElementContainers[$key] = $container;
}

$container->addElement($element);

$entity = $container->getEntity();
$property = $element->getProperty();
$value = $element->getValue();

// validate symfony
if ($this->validator !== null) {
$violations = $this->validator->validatePropertyValue($entity, $property, $value);
if (count($violations)) {
$element->setError(2, $violations[0]->getMessage());
}
}

// validate and set user custom
if ($entity instanceof InlineMapperInterface) {
try {
$entity->setInlineData($property, $value);
} catch (InvalidDataException $exception) {
$element->setError(2, $exception->getMessage());
}
} else {
$this->propertyAccessor->setValue($entity, $property, $value);
}
}

/**
* @return ElementEntityBaseContainer
*/
public function flush(): ElementEntityBaseContainer
{
foreach ($this->entityElementContainers as $container) {
$entity = $container->getEntity();

// validate symfony
if ($this->validator !== null) {
$violations = $this->validator->validate($entity);

if (count($violations)) {
/** @var ConstraintViolationInterface $violation */
$violation = $violations[0];

trigger_error(sprintf(
'Constraint violation in %s::$%s. Returned message "%s" for given %s %s.',
get_class($violation->getRoot()),
$violation->getPropertyPath(),
$violation->getMessage(),
gettype($violation->getInvalidValue()),
var_export($violation->getInvalidValue(), true)
));

$container->setInvalid($violation->getMessage());
$this->entityManager->detach($entity);
continue;
}
}

// revalidate
foreach ($container->getElements() as $element) {
if ($element->isValid() === false) {
$container->setInvalid();
$this->entityManager->detach($entity);
continue 2;
}
}

// ok
$this->entityManager->flush($entity);
}

return new ElementEntityBaseContainer($this->entityElementContainers);
}
}
Loading

0 comments on commit 72d2b16

Please sign in to comment.