diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f3787a25..a86aa0c2a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,12 +13,13 @@ on: jobs: tests: runs-on: ubuntu-latest - name: "PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}" + name: "PHP ${{ matrix.php }}, Symfony ${{ matrix.symfony }}, ORM ${{ matrix.orm }}" env: APP_ENV: ${{ matrix.app_env }} strategy: fail-fast: false matrix: + orm: ['2.*', '3.*'] php: ["8.1", "8.2", "8.3"] composer-flags: ['--no-scripts --prefer-stable --prefer-dist'] symfony: ["^6.4", "^7.1"] @@ -47,6 +48,13 @@ jobs: composer config extra.symfony.require "${{ matrix.symfony }}" (cd src/Component && composer config extra.symfony.require "${{ matrix.symfony }}") + - + name: Restrict ORM version + if: matrix.orm != '' + run: | + composer require --dev doctrine/orm "${{ matrix.orm }}" --no-update --no-scripts + (cd src/Component && composer require --dev doctrine/orm "${{ matrix.orm }}" --no-update --no-scripts) + - name: Remove hateoas on Symfony 7 if: matrix.symfony == '^7.0' @@ -55,13 +63,12 @@ jobs: - name: Install dependencies run: | - composer update --no-scripts + composer update ${{ matrix.composer-flags }} (cd src/Component && composer update ${{ matrix.composer-flags }}) - name: Prepare test application run: | - (cd tests/Application && bin/console doctrine:database:create) (cd tests/Application && bin/console doctrine:schema:create) - diff --git a/Dockerfile b/Dockerfile index f95848270..1b9442870 100644 --- a/Dockerfile +++ b/Dockerfile @@ -21,6 +21,6 @@ RUN composer update --with-all-dependencies --no-interaction --no-progress WORKDIR /app/tests/Application -RUN php bin/console doctrine:database:create && php bin/console doctrine:schema:update --force +RUN php bin/console doctrine:schema:create WORKDIR /app diff --git a/composer.json b/composer.json index 58bc55da8..0bbb06db2 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ }, "require-dev": { "doctrine/doctrine-bundle": "^2.13", - "doctrine/orm": "^2.18", + "doctrine/orm": "^2.18 || ^3.3", "friendsofsymfony/rest-bundle": "^3.7", "jms/serializer-bundle": "^3.5 || ^4.0 || ^5.0", "lchrusciel/api-test-case": "^5.0", @@ -89,7 +89,7 @@ "winzou/state-machine-bundle": "^0.6.2" }, "conflict": { - "doctrine/orm": "<2.18 || ^3.0", + "doctrine/orm": "<2.18", "doctrine/doctrine-bundle": "<2.0 || ^3.0", "friendsofsymfony/rest-bundle": "<3.0", "jms/serializer-bundle": "<3.5", diff --git a/phpstan.neon b/phpstan.neon index 46baf608b..e73d8ed81 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -40,6 +40,7 @@ parameters: - %currentWorkingDirectory%/src/Component/vendor/* ignoreErrors: + - '/Call to method type\(\) on an unknown class Doctrine\\ORM\\Mapping\\AssociationMapping./' - '/Call to method getArguments\(\) on an unknown class ReflectionAttribute./' - '/Call to method isChangeTrackingDeferredExplicit\(\) on an unknown class Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata./' - '/Call to an undefined method ReflectionClass::getAttributes\(\)./' @@ -75,6 +76,7 @@ parameters: - '/Parameter \#2 \$class of static method Webmozart\\Assert\\Assert::isInstanceOf\(\) expects class-string, string given./' - '/Parameter \#1 \$objectOrClass of class ReflectionClass constructor expects class-string\|object, object\|string given./' - '/Parameter \#1 \$package of method Symfony\\Component\\DependencyInjection\\Alias::setDeprecated\(\)/' + - '/PHPDoc tag @var for variable \$value contains unknown class Doctrine\\ORM\\Mapping\\AssociationMapping./' - '/Return typehint of method Sylius\\Bundle\\ResourceBundle\\Routing\\CrudRoutesAttributesLoader::getClassAttributes\(\) has invalid type ReflectionAttribute./' - '/Return typehint of method Sylius\\Bundle\\ResourceBundle\\Routing\\RoutesAttributesLoader::getClassAttributes\(\) has invalid type ReflectionAttribute./' - '/Unable to resolve the template type ExpectedType in call to method static method Webmozart\\Assert\\Assert::isInstanceOf\(\)/' diff --git a/psalm.xml b/psalm.xml index 66acac3f6..2defa899f 100644 --- a/psalm.xml +++ b/psalm.xml @@ -95,6 +95,12 @@ + + + + + + @@ -281,7 +287,8 @@ - + + diff --git a/src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php b/src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php index 2d0fc21b8..334ba3267 100644 --- a/src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php +++ b/src/Bundle/Doctrine/ORM/ContainerRepositoryFactory.php @@ -14,9 +14,9 @@ namespace Sylius\Bundle\ResourceBundle\Doctrine\ORM; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository as DoctrineEntityRepository; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Repository\RepositoryFactory; -use Doctrine\Persistence\ObjectRepository; final class ContainerRepositoryFactory implements RepositoryFactory { @@ -25,7 +25,7 @@ final class ContainerRepositoryFactory implements RepositoryFactory /** @var string[] */ private array $genericEntities; - /** @var ObjectRepository[] */ + /** @var DoctrineEntityRepository[] */ private array $managedRepositories = []; /** @@ -37,8 +37,11 @@ public function __construct(RepositoryFactory $doctrineFactory, array $genericEn $this->genericEntities = $genericEntities; } - /** @psalm-suppress InvalidReturnType */ - public function getRepository(EntityManagerInterface $entityManager, $entityName): ObjectRepository + /** + * @psalm-suppress InvalidReturnStatement + * @psalm-suppress InvalidReturnType + */ + public function getRepository(EntityManagerInterface $entityManager, $entityName): DoctrineEntityRepository { $metadata = $entityManager->getClassMetadata($entityName); @@ -47,13 +50,16 @@ public function getRepository(EntityManagerInterface $entityManager, $entityName return $this->getOrCreateRepository($entityManager, $metadata); } - return $this->doctrineFactory->getRepository($entityManager, $entityName); + /** @var DoctrineEntityRepository $repository */ + $repository = $this->doctrineFactory->getRepository($entityManager, $entityName); + + return $repository; } private function getOrCreateRepository( EntityManagerInterface $entityManager, ClassMetadata $metadata, - ): ObjectRepository { + ): DoctrineEntityRepository { $repositoryHash = $metadata->getName() . spl_object_hash($entityManager); if (!isset($this->managedRepositories[$repositoryHash])) { diff --git a/src/Bundle/Doctrine/ORM/ResourceRepositoryTrait.php b/src/Bundle/Doctrine/ORM/ResourceRepositoryTrait.php index 1ad59dded..abd6fad6c 100644 --- a/src/Bundle/Doctrine/ORM/ResourceRepositoryTrait.php +++ b/src/Bundle/Doctrine/ORM/ResourceRepositoryTrait.php @@ -32,15 +32,15 @@ trait ResourceRepositoryTrait { public function add(ResourceInterface $resource): void { - $this->_em->persist($resource); - $this->_em->flush(); + $this->getEntityManager()->persist($resource); + $this->getEntityManager()->flush(); } public function remove(ResourceInterface $resource): void { if (null !== $this->find($resource->getId())) { - $this->_em->remove($resource); - $this->_em->flush(); + $this->getEntityManager()->remove($resource); + $this->getEntityManager()->flush(); } } @@ -78,7 +78,7 @@ protected function getArrayPaginator($objects): Pagerfanta protected function applyCriteria(QueryBuilder $queryBuilder, array $criteria = []): void { foreach ($criteria as $property => $value) { - if (!in_array($property, array_merge($this->_class->getAssociationNames(), $this->_class->getFieldNames()), true)) { + if (!in_array($property, array_merge($this->getClassMetadata()->getAssociationNames(), $this->getClassMetadata()->getFieldNames()), true)) { continue; } @@ -101,7 +101,7 @@ protected function applyCriteria(QueryBuilder $queryBuilder, array $criteria = [ protected function applySorting(QueryBuilder $queryBuilder, array $sorting = []): void { foreach ($sorting as $property => $order) { - if (!in_array($property, array_merge($this->_class->getAssociationNames(), $this->_class->getFieldNames()), true)) { + if (!in_array($property, array_merge($this->getClassMetadata()->getAssociationNames(), $this->getClassMetadata()->getFieldNames()), true)) { continue; } diff --git a/src/Bundle/EventListener/ORMMappedSuperClassSubscriber.php b/src/Bundle/EventListener/ORMMappedSuperClassSubscriber.php index 11a434613..585dfd316 100644 --- a/src/Bundle/EventListener/ORMMappedSuperClassSubscriber.php +++ b/src/Bundle/EventListener/ORMMappedSuperClassSubscriber.php @@ -17,6 +17,7 @@ use Doctrine\ORM\Configuration; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; use Doctrine\ORM\Events; +use Doctrine\ORM\Mapping\AssociationMapping; use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Persistence\Mapping\Driver\MappingDriver; use Webmozart\Assert\Assert; @@ -80,9 +81,13 @@ private function setAssociationMappings(ClassMetadata $metadata, Configuration $ } if ($parentMetadata->isMappedSuperclass) { + /** + * @var AssociationMapping|array{type: int} $value + */ foreach ($parentMetadata->getAssociationMappings() as $key => $value) { - if ($this->isRelation($value['type']) && !isset($metadata->associationMappings[$key])) { - $metadata->associationMappings[$key] = $value; + $type = \is_array($value) ? $value['type'] : $value->type(); + if ($this->isRelation($type) && !isset($metadata->associationMappings[$key])) { + $metadata->associationMappings[$key] = $value; /** @phpstan-ignore-line */ } } } @@ -96,8 +101,12 @@ private function unsetAssociationMappings(ClassMetadata $metadata): void return; } + /** + * @var AssociationMapping|array{type: int} $value + */ foreach ($metadata->getAssociationMappings() as $key => $value) { - if ($this->isRelation($value['type'])) { + $type = \is_array($value) ? $value['type'] : $value->type(); + if ($this->isRelation($type)) { unset($metadata->associationMappings[$key]); } } diff --git a/src/Bundle/EventListener/ORMTranslatableListener.php b/src/Bundle/EventListener/ORMTranslatableListener.php index b64e43867..5b95d861b 100644 --- a/src/Bundle/EventListener/ORMTranslatableListener.php +++ b/src/Bundle/EventListener/ORMTranslatableListener.php @@ -58,6 +58,7 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs): void $classMetadata = $eventArgs->getClassMetadata(); $reflection = $classMetadata->getReflectionClass(); + /** @psalm-suppress PossiblyNullReference */ if ($reflection->isAbstract()) { return; } @@ -109,7 +110,7 @@ private function mapTranslatable(ClassMetadata $metadata): void 'mappedBy' => 'translatable', 'fetch' => ClassMetadata::FETCH_EXTRA_LAZY, 'indexBy' => 'locale', - 'cascade' => ['persist', 'merge', 'remove'], + 'cascade' => ['persist', 'remove'], 'orphanRemoval' => true, ]); } diff --git a/src/Component/composer.json b/src/Component/composer.json index 800ef515e..4cc4d95f5 100644 --- a/src/Component/composer.json +++ b/src/Component/composer.json @@ -48,7 +48,7 @@ }, "require-dev": { "behat/transliterator": "^1.3", - "doctrine/orm": "^2.18", + "doctrine/orm": "^2.18 || ^3.3", "matthiasnoback/symfony-dependency-injection-test": "^4.2.1 || ^5.1", "phpspec/phpspec": "^7.3", "phpspec/prophecy-phpunit": "^2.0", @@ -59,7 +59,7 @@ "twig/twig": "^3.0" }, "conflict": { - "doctrine/orm": "<2.18 || ^3.0", + "doctrine/orm": "<2.18", "twig/twig": "<3.0" }, "extra": { diff --git a/tests/Application/src/Entity/GedmoBaseExample.php b/tests/Application/src/Entity/GedmoBaseExample.php index 224b3a3b3..29eb16a81 100644 --- a/tests/Application/src/Entity/GedmoBaseExample.php +++ b/tests/Application/src/Entity/GedmoBaseExample.php @@ -17,9 +17,7 @@ use Gedmo\Mapping\Annotation as Gedmo; use Sylius\Resource\Model\ResourceInterface; -#[ORM\Entity] #[ORM\MappedSuperclass] -#[ORM\Table(name: 'gedmo')] class GedmoBaseExample implements ResourceInterface { #[ORM\Id] diff --git a/tests/Application/src/Entity/GedmoExtendedExample.php b/tests/Application/src/Entity/GedmoExtendedExample.php index 265fce55b..4461e25af 100644 --- a/tests/Application/src/Entity/GedmoExtendedExample.php +++ b/tests/Application/src/Entity/GedmoExtendedExample.php @@ -16,8 +16,6 @@ use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] -#[ORM\MappedSuperclass] - class GedmoExtendedExample extends GedmoBaseExample { #[ORM\Column(length: 255)]