From 39fff0e9d7252382bf3975e50ec69471c208139c Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 15:56:42 +0100 Subject: [PATCH 01/11] Bump required symfony version --- CHANGELOG.md | 6 ++++++ composer.json | 26 +++++++++++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0c6550..38d211e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +2.7.0 (unreleased) +===== + +* (improvement) Bump required Symfony. + + 2.6.1 ===== diff --git a/composer.json b/composer.json index e2e059b..92b7f6b 100644 --- a/composer.json +++ b/composer.json @@ -16,24 +16,24 @@ "21torr/bundle-helpers": "^2.1", "psr/log": "^3.0", "symfony/dependency-injection": "^6.2", - "symfony/deprecation-contracts": "^3.1", - "symfony/framework-bundle": "^6.2", - "symfony/http-foundation": "^6.2", - "symfony/http-kernel": "^6.2" + "symfony/deprecation-contracts": "^3.4", + "symfony/framework-bundle": "^6.4", + "symfony/http-foundation": "^6.4", + "symfony/http-kernel": "^6.4" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8", "doctrine/dbal": "^2.13", - "doctrine/orm": "^2.13", - "phpunit/phpunit": "^9.5", + "doctrine/orm": "^2.16", + "phpunit/phpunit": "^9.6", "roave/security-advisories": "dev-latest", - "symfony/console": "^6.2", - "symfony/form": "^6.2", - "symfony/phpunit-bridge": "^6.2", - "symfony/routing": "^6.2", - "symfony/security-bundle": "^6.2", - "symfony/translation": "^6.2", - "twig/twig": "^3.4" + "symfony/console": "^6.4", + "symfony/form": "^6.4", + "symfony/phpunit-bridge": "^6.4", + "symfony/routing": "^6.4", + "symfony/security-bundle": "^6.4", + "symfony/translation": "^6.4", + "twig/twig": "^3.8" }, "suggest": { "doctrine/dbal": "To add the SerializedType", From 6b2132d4879b11ae74f7df98684f54e8da52543f Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 15:57:24 +0100 Subject: [PATCH 02/11] Bump vendor-dir --- vendor-bin/c-norm/composer.json | 2 +- vendor-bin/cs-fixer/composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor-bin/c-norm/composer.json b/vendor-bin/c-norm/composer.json index 301acba..383c341 100644 --- a/vendor-bin/c-norm/composer.json +++ b/vendor-bin/c-norm/composer.json @@ -1,6 +1,6 @@ { "require-dev": { - "ergebnis/composer-normalize": "^2.28" + "ergebnis/composer-normalize": "^2.39" }, "config": { "allow-plugins": { diff --git a/vendor-bin/cs-fixer/composer.json b/vendor-bin/cs-fixer/composer.json index d9765e2..ad7f46c 100644 --- a/vendor-bin/cs-fixer/composer.json +++ b/vendor-bin/cs-fixer/composer.json @@ -1,5 +1,5 @@ { "require-dev": { - "21torr/php-cs-fixer": "^1.0.1" + "21torr/php-cs-fixer": "^1.0.2" } } From d445f5b712b67d5ca4d63eeb987dc8e4997470f6 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 16:00:33 +0100 Subject: [PATCH 03/11] Use constants + attributes-only --- src/Entity/Traits/IdTrait.php | 14 ++++---------- src/Entity/Traits/TimestampsTrait.php | 4 ++-- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Entity/Traits/IdTrait.php b/src/Entity/Traits/IdTrait.php index 5f685eb..534cdbb 100644 --- a/src/Entity/Traits/IdTrait.php +++ b/src/Entity/Traits/IdTrait.php @@ -2,6 +2,7 @@ namespace Torr\Rad\Entity\Traits; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; use Torr\Rad\Entity\Interfaces\EntityInterface; @@ -11,17 +12,10 @@ trait IdTrait { /** - * @ORM\Id() - * - * @ORM\GeneratedValue(strategy="AUTO") - * - * @ORM\Column(name="id", type="integer") */ - #[ - ORM\Id(), - ORM\GeneratedValue(strategy: "AUTO"), - ORM\Column(name: "id", type: "integer") - ] + #[ORM\Id] + #[ORM\GeneratedValue(strategy: "AUTO")] + #[ORM\Column(name: "id", type: Types::INTEGER)] private ?int $id = null; diff --git a/src/Entity/Traits/TimestampsTrait.php b/src/Entity/Traits/TimestampsTrait.php index 32298f3..3a46e6a 100644 --- a/src/Entity/Traits/TimestampsTrait.php +++ b/src/Entity/Traits/TimestampsTrait.php @@ -2,6 +2,7 @@ namespace Torr\Rad\Entity\Traits; +use Doctrine\DBAL\Types\Types; use Doctrine\ORM\Mapping as ORM; /** @@ -16,9 +17,8 @@ trait TimestampsTrait private \DateTimeImmutable $timeCreated; /** - * @ORM\Column(name="time_modified", type="datetimetz_immutable", nullable=true) */ - #[ORM\Column(name: "time_modified", type: "datetimetz_immutable", nullable: true)] + #[ORM\Column(name: "time_modified", type: Types::DATETIMETZ_IMMUTABLE, nullable: true)] private ?\DateTimeImmutable $timeModified = null; /** From cb2f195f87b431ab1b3f9f73b40defbc8df524cc Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 16:12:31 +0100 Subject: [PATCH 04/11] Remove sortable handlers --- CHANGELOG.md | 1 + UPGRADE.md | 1 + .../Interfaces/SortableEntityInterface.php | 18 -- src/Entity/Traits/SortOrderTrait.php | 36 ---- src/Sorting/PropertiesSortableHandler.php | 111 ------------ src/Sorting/SortableHandler.php | 171 ------------------ 6 files changed, 2 insertions(+), 336 deletions(-) delete mode 100644 src/Entity/Interfaces/SortableEntityInterface.php delete mode 100644 src/Entity/Traits/SortOrderTrait.php delete mode 100644 src/Sorting/PropertiesSortableHandler.php delete mode 100644 src/Sorting/SortableHandler.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 38d211e..d8a1e21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ===== * (improvement) Bump required Symfony. +* (bc) Remove sortable handlers. 2.6.1 diff --git a/UPGRADE.md b/UPGRADE.md index 265160a..db6822f 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -3,6 +3,7 @@ * `Routable` was removed in favor of `Linkable` and `LinkableInterface`. * The `BaseController` methods `::normalizeFormErrors()`, `::getLogger()` and `::fetchJsonRequestBody()` have been changed from `public` to `protected`. Please make sure that these methods are not being used externally within your controllers. +* The sortable handlers were removed. There is no replacement. 1.x to 2.0 diff --git a/src/Entity/Interfaces/SortableEntityInterface.php b/src/Entity/Interfaces/SortableEntityInterface.php deleted file mode 100644 index 54a4a44..0000000 --- a/src/Entity/Interfaces/SortableEntityInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -sortOrder; - } - - - /** - * {@see SortableEntityInterface::setSortOrder()} - */ - public function setSortOrder (int $sortOrder) : void - { - $this->sortOrder = $sortOrder; - } -} diff --git a/src/Sorting/PropertiesSortableHandler.php b/src/Sorting/PropertiesSortableHandler.php deleted file mode 100644 index 70bef59..0000000 --- a/src/Sorting/PropertiesSortableHandler.php +++ /dev/null @@ -1,111 +0,0 @@ -sortable = new SortableHandler($repository); - $this->properties = $properties; - $this->accessor = PropertyAccess::createPropertyAccessor(); - } - - - /** - * Sets the next sort order value in the given entity. - */ - public function setNextSortOrder (SortableEntityInterface $entity) : void - { - $entity->setSortOrder( - $this->sortable->getNextSortOrder($this->generateWhere($entity)), - ); - } - - - /** - * Analog to {@see SortableHandler::sortBefore()}. - */ - public function sortBefore (SortableEntityInterface $entity, ?SortableEntityInterface $before) : bool - { - return $this->areEntitiesCompatible($entity, $before) - ? $this->sortable->sortBefore($entity, $before, $this->generateWhere($entity)) - : false; - } - - - /** - * Removes the given entity from the sort order and fixes the sort order for the remaining items. - */ - public function removeFromSortOrder (SortableEntityInterface $entity) : void - { - $this->sortable->fixSortOrder([$entity], $this->generateWhere($entity)); - } - - - /** - * Checks whether the two given entities are compatible, i.e. they can be sorted with each other. - */ - private function areEntitiesCompatible (SortableEntityInterface $left, ?SortableEntityInterface $right) : bool - { - // a single entity is always valid - if (null === $right) - { - return true; - } - - // the same entity is never valid for sorting - if ($left === $right) - { - return false; - } - - // compare the value of each property - foreach ($this->properties as $property) - { - $leftValue = $this->accessor->getValue($left, $property); - $rightValue = $this->accessor->getValue($right, $property); - - if ($leftValue !== $rightValue) - { - return false; - } - } - - return true; - } - - - /** - * Generates the $where array, used in the sortable handler - */ - private function generateWhere (SortableEntityInterface $entity) : array - { - $where = []; - - foreach ($this->properties as $property) - { - $where[$property] = $this->accessor->getValue($entity, $property); - } - - return $where; - } -} diff --git a/src/Sorting/SortableHandler.php b/src/Sorting/SortableHandler.php deleted file mode 100644 index c0740c3..0000000 --- a/src/Sorting/SortableHandler.php +++ /dev/null @@ -1,171 +0,0 @@ -repository = $repository; - } - - - /** - * Returns the next sort order value. - */ - public function getNextSortOrder (array $where = []) : int - { - $current = $this->createQueryBuilder($where) - ->select("MAX(entity.sortOrder)") - ->getQuery() - ->getSingleScalarResult(); - - return null !== $current - ? 1 + (int) $current - : 0; - } - - - /** - * Changes the sort order, so that the $entity is before the $before entity. - * If $before is null, the $entity will be added at the end. - * - * @return bool whether the sort operation was successful - */ - public function sortBefore ( - SortableEntityInterface $entity, - ?SortableEntityInterface $before, - array $where = [], - ) : bool - { - if ($entity === $before) - { - return false; - } - - $entities = $this->createQueryBuilder($where) - ->select("entity") - ->addOrderBy("entity.sortOrder", "asc") - ->getQuery() - ->toIterable(); - - $sortOrder = 0; - - /** @var SortableEntityInterface $existing */ - foreach ($entities as $existing) - { - // the entity itself should be skipped (if it already exists), - // as both cases will be handled below. - if ($existing === $entity) - { - continue; - } - - // The $before entity was found, so insert $entity before it. - if ($existing === $before) - { - $entity->setSortOrder($sortOrder); - ++$sortOrder; - } - - $existing->setSortOrder($sortOrder); - ++$sortOrder; - } - - // if $before is null, the $entity was not added above and will be added here now - if (null === $before) - { - $entity->setSortOrder($sortOrder); - } - - return true; - } - - - /** - * Fixes the sort order of all element with the given $where. - * It will excluded all given elements. - * - * So eg. with this method, you can fix the sort order before removing an entity. - */ - public function fixSortOrder (array $excludedEntities, array $where = []) : void - { - $entities = $this->createQueryBuilder($where) - ->select("entity") - ->addOrderBy("entity.sortOrder", "asc") - ->getQuery() - ->toIterable(); - - $excludedIds = []; - - foreach ($excludedEntities as $entity) - { - $excludedIds[(string) $entity->getId()] = true; - } - - $sortOrder = 0; - - /** @var SortableEntityInterface $entity */ - foreach ($entities as $entity) - { - if (\array_key_exists((string) $entity->getId(), $excludedIds)) - { - continue; - } - - $entity->setSortOrder($sortOrder); - ++$sortOrder; - } - } - - - /** - * Creates a query build incorporating the $where clause - */ - private function createQueryBuilder (array $where) : QueryBuilder - { - $queryBuilder = $this->repository->createQueryBuilder("entity"); - - // apply the where clauses - if (!empty($where)) - { - $constraint = $queryBuilder->expr()->andX(); - $index = 0; - - foreach ($where as $parameter => $value) - { - if (null === $value) - { - $constraint->add("entity.{$parameter} IS NULL"); - } - else - { - $constraint->add("entity.{$parameter} = :_where_{$index}"); - $queryBuilder->setParameter("_where_{$index}", $value); - } - - ++$index; - } - - $queryBuilder->andWhere($constraint); - } - - return $queryBuilder; - } -} From 973f763a40781c2dfd97b9da05454d13e192b26a Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 16:16:03 +0100 Subject: [PATCH 05/11] Remove entity search handlers --- CHANGELOG.md | 3 +- UPGRADE.md | 1 + src/Search/SimpleEntitySearchHandler.php | 56 ------------------- src/Search/Tokenizer/SimpleTokenizer.php | 49 ---------------- .../Search/Tokenizer/SimpleTokenizerTest.php | 38 ------------- 5 files changed, 3 insertions(+), 144 deletions(-) delete mode 100644 src/Search/SimpleEntitySearchHandler.php delete mode 100644 src/Search/Tokenizer/SimpleTokenizer.php delete mode 100644 tests/Search/Tokenizer/SimpleTokenizerTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d8a1e21..c9391a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ -2.7.0 (unreleased) +3.0.0 (unreleased) ===== * (improvement) Bump required Symfony. * (bc) Remove sortable handlers. +* (bc) Remove simple entity search handler. 2.6.1 diff --git a/UPGRADE.md b/UPGRADE.md index db6822f..e14c58f 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -4,6 +4,7 @@ * `Routable` was removed in favor of `Linkable` and `LinkableInterface`. * The `BaseController` methods `::normalizeFormErrors()`, `::getLogger()` and `::fetchJsonRequestBody()` have been changed from `public` to `protected`. Please make sure that these methods are not being used externally within your controllers. * The sortable handlers were removed. There is no replacement. +* The simple entity search handlers were removed. There is no replacement. 1.x to 2.0 diff --git a/src/Search/SimpleEntitySearchHandler.php b/src/Search/SimpleEntitySearchHandler.php deleted file mode 100644 index f9705c5..0000000 --- a/src/Search/SimpleEntitySearchHandler.php +++ /dev/null @@ -1,56 +0,0 @@ -tokenizer = $tokenizer; - } - - - /** - * Applies the search constraint to the given query build - */ - public function applySearch ( - QueryBuilder $queryBuilder, - ?string $input, - array $fieldsToSearch, - bool $mode = SimpleTokenizer::MODE_PREFIX, - ) : QueryBuilder - { - $query = $this->tokenizer->transformInput($input, $mode); - - if ("" === $query) - { - return $queryBuilder; - } - - $constraint = new Orx(); - - foreach ($fieldsToSearch as $field) - { - $constraint->add( - new Comparison($field, "LIKE", ":__searchQueryTerm"), - ); - } - - return $queryBuilder - ->andWhere($constraint) - ->setParameter("__searchQueryTerm", $query); - } -} diff --git a/src/Search/Tokenizer/SimpleTokenizer.php b/src/Search/Tokenizer/SimpleTokenizer.php deleted file mode 100644 index 8c4257d..0000000 --- a/src/Search/Tokenizer/SimpleTokenizer.php +++ /dev/null @@ -1,49 +0,0 @@ - [null, ""]; - yield "empty" => ["", ""]; - yield "simple" => ["query", "query%"]; - yield "trim" => [" query ", "query%"]; - yield "multiple words" => ["query 123 abc", "query% 123% abc%"]; - yield "collapse spaces" => [" 1 2 3 ", "1% 2% 3%"]; - yield "prevent wildcard injection: beginning" => ["%query", "\\%query%"]; - yield "prevent wildcard injection: middle" => ["query%query", "query\\%query%"]; - yield "prevent wildcard injection: end" => ["query%", "query\\%%"]; - yield "full: simple" => ["query", "%query%", SimpleTokenizer::MODE_FULL]; - yield "full: trim" => [" query ", "%query%", SimpleTokenizer::MODE_FULL]; - yield "full: multiple words" => ["query 123 abc", "%query% %123% %abc%", SimpleTokenizer::MODE_FULL]; - yield "full: collapse spaces" => [" 1 2 3 ", "%1% %2% %3%", SimpleTokenizer::MODE_FULL]; - } - - - /** - * @dataProvider provideTokenize - */ - public function testTokenize (?string $input, string $expected, bool $mode = SimpleTokenizer::MODE_PREFIX) : void - { - $tokenizer = new SimpleTokenizer(); - self::assertSame($expected, $tokenizer->transformInput($input, $mode)); - } -} From 97cec5a445a55d8ea91ebcfe3e9a20a5000c1568 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 16:25:19 +0100 Subject: [PATCH 06/11] Remove model --- CHANGELOG.md | 2 + UPGRADE.md | 2 + src/Model/Model.php | 71 ------------------- src/Model/ModelInterface.php | 8 +-- .../{ModelTest.php => EntityModelTest.php} | 45 ++++-------- 5 files changed, 21 insertions(+), 107 deletions(-) delete mode 100644 src/Model/Model.php rename tests/Model/{ModelTest.php => EntityModelTest.php} (63%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9391a9..6a4b414 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ * (improvement) Bump required Symfony. * (bc) Remove sortable handlers. * (bc) Remove simple entity search handler. +* (bc) Remove `Model`. +* (bc) Fix missing return types in `ModelInterface`. 2.6.1 diff --git a/UPGRADE.md b/UPGRADE.md index e14c58f..31676d6 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -5,6 +5,8 @@ * The `BaseController` methods `::normalizeFormErrors()`, `::getLogger()` and `::fetchJsonRequestBody()` have been changed from `public` to `protected`. Please make sure that these methods are not being used externally within your controllers. * The sortable handlers were removed. There is no replacement. * The simple entity search handlers were removed. There is no replacement. +* The `Model` class was removed. Use `EntityModel` instead. +* The missing return types in `ModelInterface` were added. 1.x to 2.0 diff --git a/src/Model/Model.php b/src/Model/Model.php deleted file mode 100644 index 989b636..0000000 --- a/src/Model/Model.php +++ /dev/null @@ -1,71 +0,0 @@ -getManager(); - \assert($entityManager instanceof EntityManagerInterface); - $this->entityManager = $entityManager; - } - - - /** - * @inheritDoc - */ - public function add (EntityInterface $entity) - { - $this->entityManager->persist($entity); - return $this; - } - - - /** - * @inheritDoc - */ - public function update (EntityInterface $entity) - { - // automatic integration for entities that use the TimestampsTrait - if (\method_exists($entity, 'markAsModified')) - { - $entity->markAsModified(); - } - - return $this; - } - - - /** - * @inheritDoc - */ - public function remove (EntityInterface $entity) - { - $this->entityManager->remove($entity); - return $this; - } - - - /** - * @inheritDoc - */ - public function flush () - { - $this->entityManager->flush(); - return $this; - } -} diff --git a/src/Model/ModelInterface.php b/src/Model/ModelInterface.php index 8c0389b..89fdc44 100644 --- a/src/Model/ModelInterface.php +++ b/src/Model/ModelInterface.php @@ -14,7 +14,7 @@ interface ModelInterface * * @return $this */ - public function add (EntityInterface $entity); + public function add (EntityInterface $entity) : static; /** @@ -22,7 +22,7 @@ public function add (EntityInterface $entity); * * @return $this */ - public function update (EntityInterface $entity); + public function update (EntityInterface $entity) : static; /** @@ -30,7 +30,7 @@ public function update (EntityInterface $entity); * * @return $this */ - public function remove (EntityInterface $entity); + public function remove (EntityInterface $entity) : static; /** @@ -38,5 +38,5 @@ public function remove (EntityInterface $entity); * * @return $this */ - public function flush (); + public function flush () : static; } diff --git a/tests/Model/ModelTest.php b/tests/Model/EntityModelTest.php similarity index 63% rename from tests/Model/ModelTest.php rename to tests/Model/EntityModelTest.php index 92b4161..066f310 100644 --- a/tests/Model/ModelTest.php +++ b/tests/Model/EntityModelTest.php @@ -9,22 +9,16 @@ use Torr\Rad\Entity\Interfaces\EntityInterface; use Torr\Rad\Entity\Traits\IdTrait; use Torr\Rad\Entity\Traits\TimestampsTrait; -use Torr\Rad\Model\Model; +use Torr\Rad\Model\EntityModel; -final class ModelTest extends TestCase +final class EntityModelTest extends TestCase { /** - * Tests integration of {@see Model::add()} method + * Tests integration of {@see EntityModel::add()} method */ public function testAdd () : void { $manager = $this->createMock(EntityManagerInterface::class); - $registry = $this->createMock(ManagerRegistry::class); - - $registry - ->method("getManager") - ->willReturn($manager); - $entity = new ExampleEntity(); $manager @@ -32,24 +26,18 @@ public function testAdd () : void ->method("persist") ->with($entity); - $model = new class ($registry) extends Model {}; + $model = new class ($manager) extends EntityModel {}; $model->add($entity); } /** - * Tests integration of {@see Model::update()} method + * Tests integration of {@see EntityModel::update()} method */ public function testUpdate () : void { $manager = $this->createMock(EntityManagerInterface::class); - $registry = $this->createMock(ManagerRegistry::class); - - $registry - ->method("getManager") - ->willReturn($manager); - - $model = new class ($registry) extends Model {}; + $model = new class ($manager) extends EntityModel {}; // test with timestamps $entityWithTimestamps = $this->createMock(ExampleEntity::class); @@ -65,16 +53,11 @@ public function testUpdate () : void /** - * Tests integration of {@see Model::remove()} method + * Tests integration of {@see EntityModel::remove()} method */ public function testRemove () : void { $manager = $this->createMock(EntityManagerInterface::class); - $registry = $this->createMock(ManagerRegistry::class); - - $registry - ->method("getManager") - ->willReturn($manager); $entity = new ExampleEntity(); @@ -83,31 +66,29 @@ public function testRemove () : void ->method("remove") ->with($entity); - $model = new class ($registry) extends Model {}; + $model = new class ($manager) extends EntityModel {}; $model->remove($entity); } /** - * Tests integration of {@see Model::flush()} method + * Tests integration of {@see EntityModel::flush()} method */ public function testFlush () : void { $manager = $this->createMock(EntityManagerInterface::class); - $registry = $this->createMock(ManagerRegistry::class); - - $registry - ->method("getManager") - ->willReturn($manager); $manager ->expects(self::once()) ->method("flush"); - $model = new class ($registry) extends Model {}; + $model = new class ($manager) extends EntityModel {}; $model->flush(); } + /** + * + */ private function createEntity () : EntityInterface { return new class implements EntityInterface From 38dffb868b78b2c678846c763dd9ba28ba7a4f28 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 17:58:31 +0100 Subject: [PATCH 07/11] Use HtmlBuilder for `DataContainer` --- CHANGELOG.md | 1 + composer.json | 1 + src/Html/DataContainer.php | 31 ++++++++++++++++++++++--------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a4b414..9c2f662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * (bc) Remove simple entity search handler. * (bc) Remove `Model`. * (bc) Fix missing return types in `ModelInterface`. +* (internal) Use HtmlBuilder for `DataContainer`. 2.6.1 diff --git a/composer.json b/composer.json index 92b7f6b..a918a40 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,7 @@ "php": ">= 8.2", "ext-json": "*", "21torr/bundle-helpers": "^2.1", + "21torr/html-builder": "^2.1", "psr/log": "^3.0", "symfony/dependency-injection": "^6.2", "symfony/deprecation-contracts": "^3.4", diff --git a/src/Html/DataContainer.php b/src/Html/DataContainer.php index c6db25e..0abd756 100644 --- a/src/Html/DataContainer.php +++ b/src/Html/DataContainer.php @@ -3,9 +3,22 @@ namespace Torr\Rad\Html; use Symfony\Component\HttpFoundation\Response; +use Torr\HtmlBuilder\Builder\HtmlBuilder; +use Torr\HtmlBuilder\Node\HtmlElement; class DataContainer { + private HtmlBuilder $htmlBuilder; + + /** + * + */ + public function __construct () + { + $this->htmlBuilder = new HtmlBuilder(); + } + + /** * Renders the HTML of the data container. */ @@ -16,15 +29,15 @@ public function renderToHtml (?array $data, string $class, ?string $id = null) : return ""; } - return \sprintf( - '%s', - null !== $id ? ' id="' . \htmlspecialchars($id, \ENT_QUOTES) . '"' : "", - \htmlspecialchars($class, \ENT_QUOTES), - \htmlspecialchars( - \json_encode($data, \JSON_THROW_ON_ERROR), - \ENT_NOQUOTES, - ), - ); + $element = new HtmlElement("script", [ + "id" => $id, + "class" => "_data-container {$class}", + "type" => "application/json", + ], [ + \json_encode($data, \JSON_THROW_ON_ERROR), + ]); + + return $this->htmlBuilder->build($element); } From 0d714ecb086ea97123e7f71a4b26ba8ca9a74e3d Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 18:08:42 +0100 Subject: [PATCH 08/11] Replace entities --- CHANGELOG.md | 2 + UPGRADE.md | 2 + src/Entity/EntityFieldsTrait.php | 58 +++++++++++++++++++ .../{Interfaces => }/EntityInterface.php | 8 ++- ...it.php => ModifiableEntityFieldsTrait.php} | 26 ++++----- src/Entity/Traits/IdTrait.php | 39 ------------- src/Model/EntityModel.php | 2 +- src/Model/ModelInterface.php | 2 +- src/Route/Linkable.php | 2 +- tests/Fixtures/ExampleEntity.php | 8 +-- tests/Model/EntityModelTest.php | 18 +----- 11 files changed, 88 insertions(+), 79 deletions(-) create mode 100644 src/Entity/EntityFieldsTrait.php rename src/Entity/{Interfaces => }/EntityInterface.php (71%) rename src/Entity/{Traits/TimestampsTrait.php => ModifiableEntityFieldsTrait.php} (58%) delete mode 100644 src/Entity/Traits/IdTrait.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c2f662..8505a40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * (bc) Remove `Model`. * (bc) Fix missing return types in `ModelInterface`. * (internal) Use HtmlBuilder for `DataContainer`. +* (bc) Replace `IdTrait` + `TimestampsTrait` to `EntityFieldsTrait` and `ModifiableEntityFieldsTrait`. +* (bc) Moved `EntityInterface` to new namespace and add time created getter. 2.6.1 diff --git a/UPGRADE.md b/UPGRADE.md index 31676d6..c2fdec7 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -7,6 +7,8 @@ * The simple entity search handlers were removed. There is no replacement. * The `Model` class was removed. Use `EntityModel` instead. * The missing return types in `ModelInterface` were added. +* `IdTrait` and `TimestampsTrait` were removed. Migrate to `EntityFieldsTrait` and `ModifiableEntityFieldsTrait` instead. +* `EntityInterface` was moved to a new namespace and has an additional method. 1.x to 2.0 diff --git a/src/Entity/EntityFieldsTrait.php b/src/Entity/EntityFieldsTrait.php new file mode 100644 index 0000000..5053fdb --- /dev/null +++ b/src/Entity/EntityFieldsTrait.php @@ -0,0 +1,58 @@ +id; + } + + + /** + * + */ + public function isNew () : bool + { + return null === $this->id; + } + + + /** + * + */ + public function getTimeCreated () : \DateTimeImmutable + { + return $this->timeCreated; + } +} diff --git a/src/Entity/Interfaces/EntityInterface.php b/src/Entity/EntityInterface.php similarity index 71% rename from src/Entity/Interfaces/EntityInterface.php rename to src/Entity/EntityInterface.php index d4cb193..d2cb17d 100644 --- a/src/Entity/Interfaces/EntityInterface.php +++ b/src/Entity/EntityInterface.php @@ -1,6 +1,6 @@ timeCreated; - } /** + * */ public function getTimeModified () : ?\DateTimeImmutable { @@ -36,10 +31,11 @@ public function getTimeModified () : ?\DateTimeImmutable } /** + * */ public function markAsModified () : void { - $this->timeModified = new \DateTimeImmutable(); + $this->timeModified = now(); } /** diff --git a/src/Entity/Traits/IdTrait.php b/src/Entity/Traits/IdTrait.php deleted file mode 100644 index 534cdbb..0000000 --- a/src/Entity/Traits/IdTrait.php +++ /dev/null @@ -1,39 +0,0 @@ -id; - } - - - /** - * {@see EntityInterface::isNew()} - */ - public function isNew () : bool - { - return null === $this->id; - } -} diff --git a/src/Model/EntityModel.php b/src/Model/EntityModel.php index 67039fe..4b9b7e0 100644 --- a/src/Model/EntityModel.php +++ b/src/Model/EntityModel.php @@ -3,7 +3,7 @@ namespace Torr\Rad\Model; use Doctrine\ORM\EntityManagerInterface; -use Torr\Rad\Entity\Interfaces\EntityInterface; +use Torr\Rad\Entity\EntityInterface; abstract class EntityModel implements ModelInterface { diff --git a/src/Model/ModelInterface.php b/src/Model/ModelInterface.php index 89fdc44..6e088a9 100644 --- a/src/Model/ModelInterface.php +++ b/src/Model/ModelInterface.php @@ -2,7 +2,7 @@ namespace Torr\Rad\Model; -use Torr\Rad\Entity\Interfaces\EntityInterface; +use Torr\Rad\Entity\EntityInterface; /** * Interface for all models in the app diff --git a/src/Route/Linkable.php b/src/Route/Linkable.php index cb09ee9..778fc99 100644 --- a/src/Route/Linkable.php +++ b/src/Route/Linkable.php @@ -6,7 +6,7 @@ use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Torr\Rad\Entity\Interfaces\EntityInterface; +use Torr\Rad\Entity\EntityInterface; /** * A route configuration VO, that defers the actual generation of the route to a later point. diff --git a/tests/Fixtures/ExampleEntity.php b/tests/Fixtures/ExampleEntity.php index 4bc034e..003f691 100644 --- a/tests/Fixtures/ExampleEntity.php +++ b/tests/Fixtures/ExampleEntity.php @@ -2,16 +2,14 @@ namespace Tests\Torr\Rad\Fixtures; -use Torr\Rad\Entity\Interfaces\EntityInterface; -use Torr\Rad\Entity\Traits\IdTrait; -use Torr\Rad\Entity\Traits\TimestampsTrait; +use Torr\Rad\Entity\EntityInterface; +use Torr\Rad\Entity\ModifiableEntityFieldsTrait; /** */ class ExampleEntity implements EntityInterface { - use IdTrait; - use TimestampsTrait; + use ModifiableEntityFieldsTrait; public function __construct (?int $id = null) { diff --git a/tests/Model/EntityModelTest.php b/tests/Model/EntityModelTest.php index 066f310..054a5e0 100644 --- a/tests/Model/EntityModelTest.php +++ b/tests/Model/EntityModelTest.php @@ -3,12 +3,10 @@ namespace Tests\Torr\Rad\Model; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\Persistence\ManagerRegistry; use PHPUnit\Framework\TestCase; use Tests\Torr\Rad\Fixtures\ExampleEntity; -use Torr\Rad\Entity\Interfaces\EntityInterface; -use Torr\Rad\Entity\Traits\IdTrait; -use Torr\Rad\Entity\Traits\TimestampsTrait; +use Torr\Rad\Entity\EntityInterface; +use Torr\Rad\Entity\ModifiableEntityFieldsTrait; use Torr\Rad\Model\EntityModel; final class EntityModelTest extends TestCase @@ -85,16 +83,4 @@ public function testFlush () : void $model = new class ($manager) extends EntityModel {}; $model->flush(); } - - /** - * - */ - private function createEntity () : EntityInterface - { - return new class implements EntityInterface - { - use IdTrait; - use TimestampsTrait; - }; - } } From 986cac51945fd98e39496df9c6403ea007f70fec Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 18:09:52 +0100 Subject: [PATCH 09/11] Remove routable --- CHANGELOG.md | 1 + .../Route/InvalidRoutableException.php | 9 - src/Route/Routable.php | 168 ------------------ tests/Route/RoutableTest.php | 158 ---------------- 4 files changed, 1 insertion(+), 335 deletions(-) delete mode 100644 src/Exception/Route/InvalidRoutableException.php delete mode 100644 src/Route/Routable.php delete mode 100644 tests/Route/RoutableTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 8505a40..0b7f497 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * (internal) Use HtmlBuilder for `DataContainer`. * (bc) Replace `IdTrait` + `TimestampsTrait` to `EntityFieldsTrait` and `ModifiableEntityFieldsTrait`. * (bc) Moved `EntityInterface` to new namespace and add time created getter. +* (bc) Remove `Routable`. 2.6.1 diff --git a/src/Exception/Route/InvalidRoutableException.php b/src/Exception/Route/InvalidRoutableException.php deleted file mode 100644 index 017c4d3..0000000 --- a/src/Exception/Route/InvalidRoutableException.php +++ /dev/null @@ -1,9 +0,0 @@ -route = $route; - $this->parameters = $this->normalizeParameters($parameters); - $this->referenceType = $referenceType; - } - - - /** - */ - public function getRoute () : string - { - return $this->route; - } - - - /** - */ - public function getParameters () : array - { - return $this->parameters; - } - - - /** - */ - public function getReferenceType () : int - { - return $this->referenceType; - } - - - /** - * Normalizes the parameters. - * - * Parameters support some special forms, currently only objects that implement - * {@see EntityInterface} are supported as special case. - */ - private function normalizeParameters (array $parameters) : array - { - $normalized = []; - - foreach ($parameters as $key => $value) - { - $normalized[$key] = $value instanceof EntityInterface - ? $value->getId() - : $value; - } - - return $normalized; - } - - - /** - * Generates the URL. - * - * Same as {@see UrlGeneratorInterface::generate()}. - * - * @throws RouteNotFoundException - * @throws MissingMandatoryParametersException - * @throws InvalidParameterException - */ - public function generate (UrlGeneratorInterface $generator) : string - { - return $generator->generate( - $this->route, - $this->parameters, - $this->referenceType, - ); - } - - - /** - * Clones the route and merges the given parameters. - */ - public function withParameters (array $additionalParameters) : self - { - $clone = clone $this; - $clone->parameters = \array_replace($clone->parameters, $this->normalizeParameters($additionalParameters)); - return $clone; - } - - - /** - * Generates the url for any given value - * - * @param string|static|null $value - */ - public static function generateUrl ($value, UrlGeneratorInterface $generator) : ?string - { - self::ensureValidValue($value, self::OPTIONAL); - - if (null === $value || \is_string($value)) - { - return $value; - } - - \assert($value instanceof self); - return $value->generate($generator); - } - - - /** - * Returns whether the given value is valid for URL generation in {@see self::generateUrl()}. - * - * @param mixed $value - */ - public static function isValidValue ($value, bool $isRequired = self::REQUIRED) : bool - { - return null !== $value - ? (\is_string($value) || $value instanceof self) - : !$isRequired; - } - - - /** - * Ensures that the given value is a valid routable target, that can be handled in - * {@see self::generateUrl()}. - * - * @param mixed $value - * - * @throws InvalidRoutableException - */ - public static function ensureValidValue ($value, bool $isRequired = self::REQUIRED) : void - { - if (!self::isValidValue($value, $isRequired)) - { - throw new InvalidRoutableException( - $value, - $isRequired - ? \sprintf("string or %s", self::class) - : \sprintf("string, %s or null", self::class), - ); - } - } -} diff --git a/tests/Route/RoutableTest.php b/tests/Route/RoutableTest.php deleted file mode 100644 index 75c8be9..0000000 --- a/tests/Route/RoutableTest.php +++ /dev/null @@ -1,158 +0,0 @@ -withParameters()` - public function provideWithParameters () : iterable - { - yield [["a" => 1, "b" => 2], ["a" => 1], ["b" => 2]]; - yield [["a" => 2], ["a" => 1], ["a" => 2]]; - yield [["b" => 2], [], ["b" => 2]]; - yield [["a" => 1], ["a" => 1], []]; - yield [["a" => 123], ["a" => 1], ["a" => new ExampleEntity(123)]]; - yield [["a" => 1], ["a" => new ExampleEntity(123)], ["a" => 1]]; - yield [["a" => 234], ["a" => new ExampleEntity(123)], ["a" => new ExampleEntity(234)]]; - } - - - /** - * @dataProvider provideWithParameters - */ - public function testWithParameters (array $expected, array $initialParameters, array $withParameters) : void - { - $routable = new Routable("route", $initialParameters); - $newRoutable = $routable->withParameters($withParameters); - - self::assertNotSame($routable, $newRoutable); - self::assertSame($expected, $newRoutable->getParameters()); - } - // endregion - - - //region Test `->normalizeParameters()` - public function provideParameterNormalization () : iterable - { - yield [["a" => 1], ["a" => 1]]; - yield [["a" => 1, "b" => 2], ["a" => 1, "b" => 2]]; - yield [["a" => 1, "b" => 123], ["a" => 1, "b" => new ExampleEntity(123)]]; - } - - - /** - * @dataProvider provideParameterNormalization - */ - public function testParameterNormalization (array $expected, array $input) : void - { - $routable = new Routable("route", $input); - self::assertSame($expected, $routable->getParameters()); - } - //endregion - - - //region Test `::generateUrl()` - /** - * - */ - public function provideGenerateUrlSimple () : iterable - { - yield ["test", "test"]; - yield [null, null]; - } - - /** - * @dataProvider provideGenerateUrlSimple - */ - public function testGenerateUrlSimple (?string $expectedUrl, $value) : void - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - self::assertSame($expectedUrl, Routable::generateUrl($value, $urlGenerator)); - } - - /** - * - */ - public function testGenerateUrlComplex () : void - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - $route = "test"; - $parameters = ["a" => 1]; - $referenceType = UrlGeneratorInterface::NETWORK_PATH; - - $value = new Routable($route, $parameters, $referenceType); - - $urlGenerator - ->expects(self::exactly(2)) - ->method("generate") - ->with($route, $parameters, $referenceType) - ->willReturn("url"); - - self::assertSame("url", Routable::generateUrl($value, $urlGenerator)); - self::assertSame("url", $value->generate($urlGenerator)); - } - //endregion - - - //region Test `::isValidValue()` and `::ensureValidValue()` - /** - */ - public function provideValidValues () : iterable - { - // null - yield [true, null, false]; - yield [false, null, true]; - - // string - yield [true, "some url", false]; - yield [true, "some url", true]; - - // Routable - $routable = new Routable("test"); - yield [true, $routable, false]; - yield [true, $routable, true]; - - // invalid types - yield [false, 3, false]; - yield [false, 3, true]; - yield [false, 3.0, false]; - yield [false, 3.0, true]; - yield [false, true, false]; - yield [false, true, true]; - } - - - /** - * @dataProvider provideValidValues - */ - public function testIsValidValue (bool $expectedValid, $value, bool $isRequired) : void - { - self::assertSame($expectedValid, Routable::isValidValue($value, $isRequired)); - } - - - /** - * @dataProvider provideValidValues - */ - public function testEnsureValidValue (bool $expectedValid, $value, bool $isRequired) : void - { - if (!$expectedValid) - { - $this->expectException(InvalidRoutableException::class); - } - - Routable::ensureValidValue($value, $isRequired); - - if ($expectedValid) - { - self::assertTrue(true, "should not throw"); - } - } - //endregion -} From 3d8466905d2b584ce2c16544d9fd2e3232650a39 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 18:15:08 +0100 Subject: [PATCH 10/11] Add missing changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b7f497..6be14a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * (bc) Replace `IdTrait` + `TimestampsTrait` to `EntityFieldsTrait` and `ModifiableEntityFieldsTrait`. * (bc) Moved `EntityInterface` to new namespace and add time created getter. * (bc) Remove `Routable`. +* (improvement) Use the clock for setting date values. 2.6.1 From 611638dc9c1af08691852593e3830712fa8329c4 Mon Sep 17 00:00:00 2001 From: Jannik Zschiesche Date: Thu, 21 Dec 2023 18:15:12 +0100 Subject: [PATCH 11/11] Use constant --- src/Entity/EntityFieldsTrait.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entity/EntityFieldsTrait.php b/src/Entity/EntityFieldsTrait.php index 5053fdb..a5f14f6 100644 --- a/src/Entity/EntityFieldsTrait.php +++ b/src/Entity/EntityFieldsTrait.php @@ -26,7 +26,7 @@ trait EntityFieldsTrait /** * */ - #[ORM\Column(name: "time_created", type: "datetimetz_immutable")] + #[ORM\Column(name: "time_created", type: Types::DATETIMETZ_IMMUTABLE)] private \DateTimeImmutable $timeCreated;