diff --git a/.github/workflows/code_checks.yaml b/.github/workflows/code_checks.yaml index edfab030..84482123 100644 --- a/.github/workflows/code_checks.yaml +++ b/.github/workflows/code_checks.yaml @@ -31,10 +31,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: shivammathur/setup-php@v1 + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.1 extensions: pdo_sqlite, pdo_mysql, pdo_pgsql coverage: none diff --git a/.github/workflows/linter.yaml b/.github/workflows/linter.yaml index f9cbe802..dca2086e 100644 --- a/.github/workflows/linter.yaml +++ b/.github/workflows/linter.yaml @@ -11,11 +11,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - uses: shivammathur/setup-php@v1 + - uses: shivammathur/setup-php@v2 with: - php-version: 8.0 + php-version: 8.1 extensions: pdo_sqlite, pdo_mysql, pdo_pgsql coverage: none diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index b536d238..8ae397b9 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -7,16 +7,16 @@ jobs: tests: strategy: matrix: - php: - - 8.0 + operating-system: [ ubuntu-latest ] + php-versions: [ '8.1', '8.3' ] name: Test ${{ matrix.php }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - uses: shivammathur/setup-php@v1 + - uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} extensions: pdo_sqlite, pdo_mysql, pdo_pgsql diff --git a/composer.json b/composer.json index e975e124..0d3712cc 100644 --- a/composer.json +++ b/composer.json @@ -11,20 +11,20 @@ { "name": "Knplabs", "homepage": "http://knplabs.com" } ], "require": { - "php": ">=8.0", + "php": ">=8.1", "doctrine/common": "^3.3", - "doctrine/persistence": "^2.5|^3.0", + "doctrine/persistence": "^3.0", "doctrine/dbal": "^3.3", - "doctrine/orm": "^2.12", - "doctrine/doctrine-bundle": "^2.6", - "symfony/cache": "^5.4|^6.0", - "symfony/dependency-injection": "^5.4|^6.0", - "symfony/http-kernel": "^5.4|^6.0", - "symfony/security-core": "^5.4|^6.0", - "symfony/framework-bundle": "^5.4|^6.0", - "symfony/string": "^5.4|^6.0", - "symfony/translation-contracts": "^2.4|^3.0", - "nette/utils": "^3.2", + "doctrine/orm": "^3.3", + "doctrine/doctrine-bundle": "^2.7.2", + "symfony/cache": "^7.0", + "symfony/dependency-injection": "^7.0", + "symfony/http-kernel": "^7.0", + "symfony/security-bundle": "^7.0", + "symfony/framework-bundle": "^7.0", + "symfony/string": "^7.0", + "symfony/translation-contracts": "^3.0", + "nette/utils": "^4.0", "ramsey/uuid": "^4.2" }, "require-dev": { @@ -36,7 +36,7 @@ "php-parallel-lint/php-parallel-lint": "^1.3", "phpstan/phpstan": "^1.7.10", "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.4", + "rector/rector": "^1.0.0", "symplify/easy-coding-standard": "^10.2.9", "symplify/phpstan-extensions": "^10.2.9", "phpstan/phpstan-doctrine": "^1.3", diff --git a/rector.php b/rector.php index 53b657ec..9d0f1423 100644 --- a/rector.php +++ b/rector.php @@ -5,10 +5,9 @@ use Rector\Config\RectorConfig; use Rector\Doctrine\Set\DoctrineSetList; use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector; -use Rector\Nette\Set\NetteSetList; -use Rector\Php80\Rector\FunctionLike\UnionTypesRector; use Rector\Set\ValueObject\LevelSetList; use Rector\Set\ValueObject\SetList; +use Rector\TypeDeclaration\Rector\Property\TypedPropertyFromAssignsRector; return static function (RectorConfig $rectorConfig): void { $rectorConfig->paths([__DIR__ . '/src', __DIR__ . '/tests', __DIR__ . '/utils']); @@ -17,12 +16,7 @@ $rectorConfig->skip([ RenamePropertyToMatchTypeRector::class => [__DIR__ . '/tests/ORM/'], - - UnionTypesRector::class => [ - // to keep BC return types - __DIR__ . '/src/Contract/Entity', - 'src/Model/*/*Trait.php', - ], + TypedPropertyFromAssignsRector::class => [__DIR__ . '/tests/Repository/'], ]); // doctrine annotations to attributes @@ -31,7 +25,6 @@ SetList::DEAD_CODE, SetList::CODE_QUALITY, SetList::CODING_STYLE, - NetteSetList::NETTE_CODE_QUALITY, SetList::NAMING, LevelSetList::UP_TO_PHP_80, ]); diff --git a/src/Bundle/DependencyInjection/DoctrineBehaviorsExtension.php b/src/Bundle/DependencyInjection/DoctrineBehaviorsExtension.php index 93ff3f56..7aa41421 100644 --- a/src/Bundle/DependencyInjection/DoctrineBehaviorsExtension.php +++ b/src/Bundle/DependencyInjection/DoctrineBehaviorsExtension.php @@ -12,7 +12,7 @@ final class DoctrineBehaviorsExtension extends Extension { /** - * @param string[] $configs + * @param array> $configs */ public function load(array $configs, ContainerBuilder $containerBuilder): void { diff --git a/src/EventSubscriber/BlameableEventSubscriber.php b/src/EventSubscriber/BlameableEventSubscriber.php index 968ea57f..887b90c9 100644 --- a/src/EventSubscriber/BlameableEventSubscriber.php +++ b/src/EventSubscriber/BlameableEventSubscriber.php @@ -4,17 +4,23 @@ namespace Knp\DoctrineBehaviors\EventSubscriber; -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\PrePersistEventArgs; +use Doctrine\ORM\Event\PreRemoveEventArgs; +use Doctrine\ORM\Event\PreUpdateEventArgs; use Doctrine\ORM\Events; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\UnitOfWork; use Knp\DoctrineBehaviors\Contract\Entity\BlameableInterface; use Knp\DoctrineBehaviors\Contract\Provider\UserProviderInterface; -final class BlameableEventSubscriber implements EventSubscriberInterface +#[AsDoctrineListener(event: Events::loadClassMetadata)] +#[AsDoctrineListener(event: Events::prePersist)] +#[AsDoctrineListener(event: Events::preUpdate)] +#[AsDoctrineListener(event: Events::preRemove)] +final class BlameableEventSubscriber { /** * @var string @@ -59,10 +65,10 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataE /** * Stores the current user into createdBy and updatedBy properties */ - public function prePersist(LifecycleEventArgs $lifecycleEventArgs): void + public function prePersist(PrePersistEventArgs $prePersistEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); - if (! $entity instanceof BlameableInterface) { + $object = $prePersistEventArgs->getObject(); + if (! $object instanceof BlameableInterface) { return; } @@ -72,28 +78,28 @@ public function prePersist(LifecycleEventArgs $lifecycleEventArgs): void return; } - if (! $entity->getCreatedBy()) { - $entity->setCreatedBy($user); + if (! $object->getCreatedBy()) { + $object->setCreatedBy($user); $this->getUnitOfWork() - ->propertyChanged($entity, self::CREATED_BY, null, $user); + ->propertyChanged($object, self::CREATED_BY, null, $user); } - if (! $entity->getUpdatedBy()) { - $entity->setUpdatedBy($user); + if (! $object->getUpdatedBy()) { + $object->setUpdatedBy($user); $this->getUnitOfWork() - ->propertyChanged($entity, self::UPDATED_BY, null, $user); + ->propertyChanged($object, self::UPDATED_BY, null, $user); } } /** * Stores the current user into updatedBy property */ - public function preUpdate(LifecycleEventArgs $lifecycleEventArgs): void + public function preUpdate(PreUpdateEventArgs $preUpdateEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); - if (! $entity instanceof BlameableInterface) { + $object = $preUpdateEventArgs->getObject(); + if (! $object instanceof BlameableInterface) { return; } @@ -102,20 +108,20 @@ public function preUpdate(LifecycleEventArgs $lifecycleEventArgs): void return; } - $oldValue = $entity->getUpdatedBy(); - $entity->setUpdatedBy($user); + $updatedBy = $object->getUpdatedBy(); + $object->setUpdatedBy($user); $this->getUnitOfWork() - ->propertyChanged($entity, self::UPDATED_BY, $oldValue, $user); + ->propertyChanged($object, self::UPDATED_BY, $updatedBy, $user); } /** * Stores the current user into deletedBy property */ - public function preRemove(LifecycleEventArgs $lifecycleEventArgs): void + public function preRemove(PreRemoveEventArgs $preRemoveEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); - if (! $entity instanceof BlameableInterface) { + $object = $preRemoveEventArgs->getObject(); + if (! $object instanceof BlameableInterface) { return; } @@ -124,27 +130,19 @@ public function preRemove(LifecycleEventArgs $lifecycleEventArgs): void return; } - $oldDeletedBy = $entity->getDeletedBy(); - $entity->setDeletedBy($user); + $oldDeletedBy = $object->getDeletedBy(); + $object->setDeletedBy($user); $this->getUnitOfWork() - ->propertyChanged($entity, self::DELETED_BY, $oldDeletedBy, $user); + ->propertyChanged($object, self::DELETED_BY, $oldDeletedBy, $user); } - /** - * @return string[] - */ - public function getSubscribedEvents(): array - { - return [Events::prePersist, Events::preUpdate, Events::preRemove, Events::loadClassMetadata]; - } - - private function mapEntity(ClassMetadataInfo $classMetadataInfo): void + private function mapEntity(ClassMetadata $classMetadata): void { if ($this->blameableUserEntity !== null && class_exists($this->blameableUserEntity)) { - $this->mapManyToOneUser($classMetadataInfo); + $this->mapManyToOneUser($classMetadata); } else { - $this->mapStringUser($classMetadataInfo); + $this->mapStringUser($classMetadata); } } @@ -153,27 +151,27 @@ private function getUnitOfWork(): UnitOfWork return $this->entityManager->getUnitOfWork(); } - private function mapManyToOneUser(ClassMetadataInfo $classMetadataInfo): void + private function mapManyToOneUser(ClassMetadata $classMetadata): void { - $this->mapManyToOneWithTargetEntity($classMetadataInfo, self::CREATED_BY); - $this->mapManyToOneWithTargetEntity($classMetadataInfo, self::UPDATED_BY); - $this->mapManyToOneWithTargetEntity($classMetadataInfo, self::DELETED_BY); + $this->mapManyToOneWithTargetEntity($classMetadata, self::CREATED_BY); + $this->mapManyToOneWithTargetEntity($classMetadata, self::UPDATED_BY); + $this->mapManyToOneWithTargetEntity($classMetadata, self::DELETED_BY); } - private function mapStringUser(ClassMetadataInfo $classMetadataInfo): void + private function mapStringUser(ClassMetadata $classMetadata): void { - $this->mapStringNullableField($classMetadataInfo, self::CREATED_BY); - $this->mapStringNullableField($classMetadataInfo, self::UPDATED_BY); - $this->mapStringNullableField($classMetadataInfo, self::DELETED_BY); + $this->mapStringNullableField($classMetadata, self::CREATED_BY); + $this->mapStringNullableField($classMetadata, self::UPDATED_BY); + $this->mapStringNullableField($classMetadata, self::DELETED_BY); } - private function mapManyToOneWithTargetEntity(ClassMetadataInfo $classMetadataInfo, string $fieldName): void + private function mapManyToOneWithTargetEntity(ClassMetadata $classMetadata, string $fieldName): void { - if ($classMetadataInfo->hasAssociation($fieldName)) { + if ($classMetadata->hasAssociation($fieldName)) { return; } - $classMetadataInfo->mapManyToOne([ + $classMetadata->mapManyToOne([ 'fieldName' => $fieldName, 'targetEntity' => $this->blameableUserEntity, 'joinColumns' => [ @@ -184,13 +182,13 @@ private function mapManyToOneWithTargetEntity(ClassMetadataInfo $classMetadataIn ]); } - private function mapStringNullableField(ClassMetadataInfo $classMetadataInfo, string $fieldName): void + private function mapStringNullableField(ClassMetadata $classMetadata, string $fieldName): void { - if ($classMetadataInfo->hasField($fieldName)) { + if ($classMetadata->hasField($fieldName)) { return; } - $classMetadataInfo->mapField([ + $classMetadata->mapField([ 'fieldName' => $fieldName, 'type' => 'string', 'nullable' => true, diff --git a/src/EventSubscriber/LoggableEventSubscriber.php b/src/EventSubscriber/LoggableEventSubscriber.php index f21907e7..bc41f4c4 100644 --- a/src/EventSubscriber/LoggableEventSubscriber.php +++ b/src/EventSubscriber/LoggableEventSubscriber.php @@ -4,77 +4,74 @@ namespace Knp\DoctrineBehaviors\EventSubscriber; -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; -use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; +use Doctrine\ORM\Event\PostPersistEventArgs; +use Doctrine\ORM\Event\PostUpdateEventArgs; +use Doctrine\ORM\Event\PreRemoveEventArgs; use Doctrine\ORM\Events; use Knp\DoctrineBehaviors\Contract\Entity\LoggableInterface; use Psr\Log\LoggerInterface; use Psr\Log\LogLevel; -final class LoggableEventSubscriber implements EventSubscriberInterface +#[AsDoctrineListener(event: Events::postPersist)] +#[AsDoctrineListener(event: Events::postUpdate)] +#[AsDoctrineListener(event: Events::preRemove)] +final class LoggableEventSubscriber { public function __construct( private LoggerInterface $logger ) { } - public function postPersist(LifecycleEventArgs $lifecycleEventArgs): void + public function postPersist(PostPersistEventArgs $postPersistEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); - if (! $entity instanceof LoggableInterface) { + $object = $postPersistEventArgs->getObject(); + if (! $object instanceof LoggableInterface) { return; } - $createLogMessage = $entity->getCreateLogMessage(); + $createLogMessage = $object->getCreateLogMessage(); $this->logger->log(LogLevel::INFO, $createLogMessage); - $this->logChangeSet($lifecycleEventArgs); + $this->logChangeSet($postPersistEventArgs); } - public function postUpdate(LifecycleEventArgs $lifecycleEventArgs): void + public function postUpdate(PostUpdateEventArgs $postUpdateEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); - if (! $entity instanceof LoggableInterface) { + $object = $postUpdateEventArgs->getObject(); + if (! $object instanceof LoggableInterface) { return; } - $this->logChangeSet($lifecycleEventArgs); + $this->logChangeSet($postUpdateEventArgs); } - public function preRemove(LifecycleEventArgs $lifecycleEventArgs): void + public function preRemove(PreRemoveEventArgs $preRemoveEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); + $object = $preRemoveEventArgs->getObject(); - if ($entity instanceof LoggableInterface) { - $this->logger->log(LogLevel::INFO, $entity->getRemoveLogMessage()); + if ($object instanceof LoggableInterface) { + $this->logger->log(LogLevel::INFO, $object->getRemoveLogMessage()); } } - /** - * @return string[] - */ - public function getSubscribedEvents(): array - { - return [Events::postPersist, Events::postUpdate, Events::preRemove]; - } - /** * Logs entity changeset */ - private function logChangeSet(LifecycleEventArgs $lifecycleEventArgs): void + private function logChangeSet(PostPersistEventArgs|PostUpdateEventArgs $lifecycleEventArgs): void { - $entityManager = $lifecycleEventArgs->getEntityManager(); + $entityManager = $lifecycleEventArgs->getObjectManager(); $unitOfWork = $entityManager->getUnitOfWork(); - $entity = $lifecycleEventArgs->getEntity(); + $object = $lifecycleEventArgs->getObject(); - $entityClass = $entity::class; + $entityClass = $object::class; $classMetadata = $entityManager->getClassMetadata($entityClass); - /** @var LoggableInterface $entity */ - $unitOfWork->computeChangeSet($classMetadata, $entity); - $changeSet = $unitOfWork->getEntityChangeSet($entity); + /** @var LoggableInterface $object */ + $unitOfWork->computeChangeSet($classMetadata, $object); + $changeSet = $unitOfWork->getEntityChangeSet($object); - $message = $entity->getUpdateLogMessage($changeSet); + $message = $object->getUpdateLogMessage($changeSet); if ($message === '') { return; diff --git a/src/EventSubscriber/SluggableEventSubscriber.php b/src/EventSubscriber/SluggableEventSubscriber.php index 6d0b3b9b..0d9b6f2d 100644 --- a/src/EventSubscriber/SluggableEventSubscriber.php +++ b/src/EventSubscriber/SluggableEventSubscriber.php @@ -4,16 +4,20 @@ namespace Knp\DoctrineBehaviors\EventSubscriber; -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\EntityManagerInterface; -use Doctrine\ORM\Event\LifecycleEventArgs; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\PrePersistEventArgs; +use Doctrine\ORM\Event\PreUpdateEventArgs; use Doctrine\ORM\Events; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\ClassMetadata; use Knp\DoctrineBehaviors\Contract\Entity\SluggableInterface; use Knp\DoctrineBehaviors\Repository\DefaultSluggableRepository; -final class SluggableEventSubscriber implements EventSubscriberInterface +#[AsDoctrineListener(event: Events::loadClassMetadata)] +#[AsDoctrineListener(event: Events::prePersist)] +#[AsDoctrineListener(event: Events::preUpdate)] +final class SluggableEventSubscriber { /** * @var string @@ -40,36 +44,28 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataE ]); } - public function prePersist(LifecycleEventArgs $lifecycleEventArgs): void + public function prePersist(PrePersistEventArgs $prePersistEventArgs): void { - $this->processLifecycleEventArgs($lifecycleEventArgs); + $this->processLifecycleEventArgs($prePersistEventArgs); } - public function preUpdate(LifecycleEventArgs $lifecycleEventArgs): void + public function preUpdate(PreUpdateEventArgs $preUpdateEventArgs): void { - $this->processLifecycleEventArgs($lifecycleEventArgs); + $this->processLifecycleEventArgs($preUpdateEventArgs); } - /** - * @return string[] - */ - public function getSubscribedEvents(): array - { - return [Events::loadClassMetadata, Events::prePersist, Events::preUpdate]; - } - - private function shouldSkip(ClassMetadataInfo $classMetadataInfo): bool + private function shouldSkip(ClassMetadata $classMetadata): bool { - if (! is_a($classMetadataInfo->getName(), SluggableInterface::class, true)) { + if (! is_a($classMetadata->getName(), SluggableInterface::class, true)) { return true; } - return $classMetadataInfo->hasField(self::SLUG); + return $classMetadata->hasField(self::SLUG); } - private function processLifecycleEventArgs(LifecycleEventArgs $lifecycleEventArgs): void + private function processLifecycleEventArgs(PrePersistEventArgs|PreUpdateEventArgs $lifecycleEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); + $entity = $lifecycleEventArgs->getObject(); if (! $entity instanceof SluggableInterface) { return; } diff --git a/src/EventSubscriber/SoftDeletableEventSubscriber.php b/src/EventSubscriber/SoftDeletableEventSubscriber.php index e8ba92e2..fac35168 100644 --- a/src/EventSubscriber/SoftDeletableEventSubscriber.php +++ b/src/EventSubscriber/SoftDeletableEventSubscriber.php @@ -4,13 +4,15 @@ namespace Knp\DoctrineBehaviors\EventSubscriber; -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; use Doctrine\ORM\Event\OnFlushEventArgs; use Doctrine\ORM\Events; use Knp\DoctrineBehaviors\Contract\Entity\SoftDeletableInterface; -final class SoftDeletableEventSubscriber implements EventSubscriberInterface +#[AsDoctrineListener(event: Events::onFlush)] +#[AsDoctrineListener(event: Events::loadClassMetadata)] +final class SoftDeletableEventSubscriber { /** * @var string @@ -19,7 +21,7 @@ final class SoftDeletableEventSubscriber implements EventSubscriberInterface public function onFlush(OnFlushEventArgs $onFlushEventArgs): void { - $entityManager = $onFlushEventArgs->getEntityManager(); + $entityManager = $onFlushEventArgs->getObjectManager(); $unitOfWork = $entityManager->getUnitOfWork(); foreach ($unitOfWork->getScheduledEntityDeletions() as $entity) { @@ -61,12 +63,4 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataE 'nullable' => true, ]); } - - /** - * @return string[] - */ - public function getSubscribedEvents(): array - { - return [Events::onFlush, Events::loadClassMetadata]; - } } diff --git a/src/EventSubscriber/TimestampableEventSubscriber.php b/src/EventSubscriber/TimestampableEventSubscriber.php index 292e058b..d39bb3a7 100644 --- a/src/EventSubscriber/TimestampableEventSubscriber.php +++ b/src/EventSubscriber/TimestampableEventSubscriber.php @@ -4,12 +4,13 @@ namespace Knp\DoctrineBehaviors\EventSubscriber; -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; use Doctrine\ORM\Events; use Knp\DoctrineBehaviors\Contract\Entity\TimestampableInterface; -final class TimestampableEventSubscriber implements EventSubscriberInterface +#[AsDoctrineListener(event: Events::loadClassMetadata)] +final class TimestampableEventSubscriber { public function __construct( private string $timestampableDateFieldType @@ -45,12 +46,4 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataE } } } - - /** - * @return string[] - */ - public function getSubscribedEvents(): array - { - return [Events::loadClassMetadata]; - } } diff --git a/src/EventSubscriber/TranslatableEventSubscriber.php b/src/EventSubscriber/TranslatableEventSubscriber.php index 2df494de..51e898e8 100644 --- a/src/EventSubscriber/TranslatableEventSubscriber.php +++ b/src/EventSubscriber/TranslatableEventSubscriber.php @@ -4,18 +4,22 @@ namespace Knp\DoctrineBehaviors\EventSubscriber; -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; -use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\PostLoadEventArgs; +use Doctrine\ORM\Event\PrePersistEventArgs; use Doctrine\ORM\Events; -use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\Persistence\ObjectManager; use Knp\DoctrineBehaviors\Contract\Entity\TranslatableInterface; use Knp\DoctrineBehaviors\Contract\Entity\TranslationInterface; use Knp\DoctrineBehaviors\Contract\Provider\LocaleProviderInterface; use ReflectionClass; -final class TranslatableEventSubscriber implements EventSubscriberInterface +#[AsDoctrineListener(event: Events::loadClassMetadata)] +#[AsDoctrineListener(event: Events::postLoad)] +#[AsDoctrineListener(event: Events::prePersist)] +final class TranslatableEventSubscriber { /** * @var string @@ -59,22 +63,14 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataE } } - public function postLoad(LifecycleEventArgs $lifecycleEventArgs): void + public function postLoad(PostLoadEventArgs $postLoadEventArgs): void { - $this->setLocales($lifecycleEventArgs); + $this->setLocales($postLoadEventArgs); } - public function prePersist(LifecycleEventArgs $lifecycleEventArgs): void + public function prePersist(PrePersistEventArgs $prePersistEventArgs): void { - $this->setLocales($lifecycleEventArgs); - } - - /** - * @return string[] - */ - public function getSubscribedEvents(): array - { - return [Events::loadClassMetadata, Events::postLoad, Events::prePersist]; + $this->setLocales($prePersistEventArgs); } /** @@ -87,51 +83,51 @@ private function convertFetchString(string|int $fetchMode): int } if ($fetchMode === 'EAGER') { - return ClassMetadataInfo::FETCH_EAGER; + return ClassMetadata::FETCH_EAGER; } if ($fetchMode === 'EXTRA_LAZY') { - return ClassMetadataInfo::FETCH_EXTRA_LAZY; + return ClassMetadata::FETCH_EXTRA_LAZY; } - return ClassMetadataInfo::FETCH_LAZY; + return ClassMetadata::FETCH_LAZY; } - private function mapTranslatable(ClassMetadataInfo $classMetadataInfo): void + private function mapTranslatable(ClassMetadata $classMetadata): void { - if ($classMetadataInfo->hasAssociation('translations')) { + if ($classMetadata->hasAssociation('translations')) { return; } - $classMetadataInfo->mapOneToMany([ + $classMetadata->mapOneToMany([ 'fieldName' => 'translations', 'mappedBy' => 'translatable', 'indexBy' => self::LOCALE, - 'cascade' => ['persist', 'merge', 'remove'], + 'cascade' => ['persist', 'remove'], 'fetch' => $this->translatableFetchMode, - 'targetEntity' => $classMetadataInfo->getReflectionClass() + 'targetEntity' => $classMetadata->getReflectionClass() ->getMethod('getTranslationEntityClass') ->invoke(null), 'orphanRemoval' => true, ]); } - private function mapTranslation(ClassMetadataInfo $classMetadataInfo, ObjectManager $objectManager): void + private function mapTranslation(ClassMetadata $classMetadata, ObjectManager $objectManager): void { - if (! $classMetadataInfo->hasAssociation('translatable')) { - $targetEntity = $classMetadataInfo->getReflectionClass() + if (! $classMetadata->hasAssociation('translatable')) { + $targetEntity = $classMetadata->getReflectionClass() ->getMethod('getTranslatableEntityClass') ->invoke(null); - /** @var ClassMetadataInfo $classMetadata */ - $classMetadata = $objectManager->getClassMetadata($targetEntity); + /** @var ClassMetadata $classPersistenceMetadata */ + $classPersistenceMetadata = $objectManager->getClassMetadata($targetEntity); - $singleIdentifierFieldName = $classMetadata->getSingleIdentifierFieldName(); + $singleIdentifierFieldName = $classPersistenceMetadata->getSingleIdentifierFieldName(); - $classMetadataInfo->mapManyToOne([ + $classMetadata->mapManyToOne([ 'fieldName' => 'translatable', 'inversedBy' => 'translations', - 'cascade' => ['persist', 'merge'], + 'cascade' => ['persist'], 'fetch' => $this->translationFetchMode, 'joinColumns' => [[ 'name' => 'translatable_id', @@ -142,16 +138,16 @@ private function mapTranslation(ClassMetadataInfo $classMetadataInfo, ObjectMana ]); } - $name = $classMetadataInfo->getTableName() . '_unique_translation'; - if (! $this->hasUniqueTranslationConstraint($classMetadataInfo, $name) && - $classMetadataInfo->getName() === $classMetadataInfo->rootEntityName) { - $classMetadataInfo->table['uniqueConstraints'][$name] = [ + $name = $classMetadata->getTableName() . '_unique_translation'; + if (! $this->hasUniqueTranslationConstraint($classMetadata, $name) && + $classMetadata->getName() === $classMetadata->rootEntityName) { + $classMetadata->table['uniqueConstraints'][$name] = [ 'columns' => ['translatable_id', self::LOCALE], ]; } - if (! $classMetadataInfo->hasField(self::LOCALE) && ! $classMetadataInfo->hasAssociation(self::LOCALE)) { - $classMetadataInfo->mapField([ + if (! $classMetadata->hasField(self::LOCALE) && ! $classMetadata->hasAssociation(self::LOCALE)) { + $classMetadata->mapField([ 'fieldName' => self::LOCALE, 'type' => 'string', 'length' => 5, @@ -159,26 +155,26 @@ private function mapTranslation(ClassMetadataInfo $classMetadataInfo, ObjectMana } } - private function setLocales(LifecycleEventArgs $lifecycleEventArgs): void + private function setLocales(PostLoadEventArgs|PrePersistEventArgs $lifecycleEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); - if (! $entity instanceof TranslatableInterface) { + $object = $lifecycleEventArgs->getObject(); + if (! $object instanceof TranslatableInterface) { return; } $currentLocale = $this->localeProvider->provideCurrentLocale(); if ($currentLocale) { - $entity->setCurrentLocale($currentLocale); + $object->setCurrentLocale($currentLocale); } $fallbackLocale = $this->localeProvider->provideFallbackLocale(); if ($fallbackLocale) { - $entity->setDefaultLocale($fallbackLocale); + $object->setDefaultLocale($fallbackLocale); } } - private function hasUniqueTranslationConstraint(ClassMetadataInfo $classMetadataInfo, string $name): bool + private function hasUniqueTranslationConstraint(ClassMetadata $classMetadata, string $name): bool { - return isset($classMetadataInfo->table['uniqueConstraints'][$name]); + return isset($classMetadata->table['uniqueConstraints'][$name]); } } diff --git a/src/EventSubscriber/TreeEventSubscriber.php b/src/EventSubscriber/TreeEventSubscriber.php index 15ea5595..8471eaa3 100644 --- a/src/EventSubscriber/TreeEventSubscriber.php +++ b/src/EventSubscriber/TreeEventSubscriber.php @@ -4,12 +4,13 @@ namespace Knp\DoctrineBehaviors\EventSubscriber; -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; use Doctrine\ORM\Events; use Knp\DoctrineBehaviors\Contract\Entity\TreeNodeInterface; -final class TreeEventSubscriber implements EventSubscriberInterface +#[AsDoctrineListener(event: Events::loadClassMetadata)] +final class TreeEventSubscriber { public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataEventArgs): void { @@ -33,12 +34,4 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataE 'length' => 255, ]); } - - /** - * @return string[] - */ - public function getSubscribedEvents(): array - { - return [Events::loadClassMetadata]; - } } diff --git a/src/EventSubscriber/UuidableEventSubscriber.php b/src/EventSubscriber/UuidableEventSubscriber.php index 525eb4dc..4008c2e6 100644 --- a/src/EventSubscriber/UuidableEventSubscriber.php +++ b/src/EventSubscriber/UuidableEventSubscriber.php @@ -4,13 +4,15 @@ namespace Knp\DoctrineBehaviors\EventSubscriber; -use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface; -use Doctrine\ORM\Event\LifecycleEventArgs; +use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener; use Doctrine\ORM\Event\LoadClassMetadataEventArgs; +use Doctrine\ORM\Event\PrePersistEventArgs; use Doctrine\ORM\Events; use Knp\DoctrineBehaviors\Contract\Entity\UuidableInterface; -final class UuidableEventSubscriber implements EventSubscriberInterface +#[AsDoctrineListener(event: Events::loadClassMetadata)] +#[AsDoctrineListener(event: Events::prePersist)] +final class UuidableEventSubscriber { public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataEventArgs): void { @@ -35,21 +37,13 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $loadClassMetadataE ]); } - public function prePersist(LifecycleEventArgs $lifecycleEventArgs): void + public function prePersist(PrePersistEventArgs $prePersistEventArgs): void { - $entity = $lifecycleEventArgs->getEntity(); - if (! $entity instanceof UuidableInterface) { + $object = $prePersistEventArgs->getObject(); + if (! $object instanceof UuidableInterface) { return; } - $entity->generateUuid(); - } - - /** - * @return string[] - */ - public function getSubscribedEvents(): array - { - return [Events::loadClassMetadata, Events::prePersist]; + $object->generateUuid(); } } diff --git a/src/Model/SoftDeletable/SoftDeletableMethodsTrait.php b/src/Model/SoftDeletable/SoftDeletableMethodsTrait.php index 6b8b6b41..378b0d11 100644 --- a/src/Model/SoftDeletable/SoftDeletableMethodsTrait.php +++ b/src/Model/SoftDeletable/SoftDeletableMethodsTrait.php @@ -39,7 +39,7 @@ public function willBeDeleted(?DateTimeInterface $deletedAt = null): bool return false; } - if ($deletedAt === null) { + if (! $deletedAt instanceof DateTimeInterface) { return true; } diff --git a/src/Model/Translatable/TranslatableMethodsTrait.php b/src/Model/Translatable/TranslatableMethodsTrait.php index a1374b20..be2464dc 100644 --- a/src/Model/Translatable/TranslatableMethodsTrait.php +++ b/src/Model/Translatable/TranslatableMethodsTrait.php @@ -12,9 +12,9 @@ trait TranslatableMethodsTrait { /** - * @return Collection + * @return ArrayCollection|Collection */ - public function getTranslations() + public function getTranslations(): ArrayCollection|Collection { // initialize collection, usually in ctor if ($this->translations === null) { @@ -27,6 +27,7 @@ public function getTranslations() /** * @param Collection $translations * @phpstan-param iterable $translations + * @throws TranslatableException */ public function setTranslations(iterable $translations): void { diff --git a/src/Model/Translatable/TranslationPropertiesTrait.php b/src/Model/Translatable/TranslationPropertiesTrait.php index c3773e27..124c0be6 100644 --- a/src/Model/Translatable/TranslationPropertiesTrait.php +++ b/src/Model/Translatable/TranslationPropertiesTrait.php @@ -14,7 +14,7 @@ trait TranslationPropertiesTrait protected $locale; /** - * Will be mapped to translatable entity by TranslatableSubscriber + * Will be mapped to translatable entity by TranslatableEventSubscriber * * @var TranslatableInterface */ diff --git a/src/Model/Tree/TreeNodeMethodsTrait.php b/src/Model/Tree/TreeNodeMethodsTrait.php index e195838f..2b8580aa 100644 --- a/src/Model/Tree/TreeNodeMethodsTrait.php +++ b/src/Model/Tree/TreeNodeMethodsTrait.php @@ -120,7 +120,7 @@ public function setChildNodeOf(?TreeNodeInterface $treeNode = null): void throw new TreeException('You must provide an id for this node if you want it to be part of a tree.'); } - $path = $treeNode !== null + $path = $treeNode instanceof TreeNodeInterface ? rtrim($treeNode->getRealMaterializedPath(), static::getMaterializedPathSeparator()) : static::getMaterializedPathSeparator(); $this->setMaterializedPath($path); @@ -132,7 +132,7 @@ public function setChildNodeOf(?TreeNodeInterface $treeNode = null): void $this->parentNode = $treeNode; - if ($treeNode !== null) { + if ($treeNode instanceof TreeNodeInterface) { $this->parentNode->addChildNode($this); } @@ -196,8 +196,8 @@ public function toJson(?Closure $prepare = null): string */ public function toArray(?Closure $prepare = null, ?array &$tree = null): array { - if ($prepare === null) { - $prepare = static fn (TreeNodeInterface $node): string => (string) $node; + if (! $prepare instanceof Closure) { + $prepare = static fn (TreeNodeInterface $treeNode): string => (string) $treeNode; } if ($tree === null) { @@ -228,7 +228,7 @@ public function toArray(?Closure $prepare = null, ?array &$tree = null): array */ public function toFlatArray(?Closure $prepare = null, ?array &$tree = null): array { - if ($prepare === null) { + if (! $prepare instanceof Closure) { $prepare = static function (TreeNodeInterface $treeNode) { $pre = $treeNode->getNodeLevel() > 1 ? implode('', array_fill(0, $treeNode->getNodeLevel(), '--')) : ''; return $pre . $treeNode; @@ -268,10 +268,7 @@ public function offsetUnset(mixed $offset): void unset($this->getChildNodes()[$offset]); } - /** - * @return mixed - */ - public function offsetGet(mixed $offset) + public function offsetGet(mixed $offset): mixed { return $this->getChildNodes()[$offset]; } @@ -281,12 +278,12 @@ public function offsetGet(mixed $offset) */ protected function getExplodedPath(): array { - $separator = static::getMaterializedPathSeparator(); - if ($separator === '') { + $materializedPathSeparator = static::getMaterializedPathSeparator(); + if ($materializedPathSeparator === '') { throw new ShouldNotHappenException(); } - $path = explode($separator, $this->getRealMaterializedPath()); + $path = explode($materializedPathSeparator, $this->getRealMaterializedPath()); return array_filter($path, static fn ($item): bool => $item !== ''); } diff --git a/src/Provider/LocaleProvider.php b/src/Provider/LocaleProvider.php index f3c472b0..e6017311 100644 --- a/src/Provider/LocaleProvider.php +++ b/src/Provider/LocaleProvider.php @@ -33,7 +33,7 @@ public function provideCurrentLocale(): ?string return $currentLocale; } - if ($this->translator !== null) { + if ($this->translator instanceof TranslatorInterface) { return $this->translator->getLocale(); } @@ -43,7 +43,7 @@ public function provideCurrentLocale(): ?string public function provideFallbackLocale(): ?string { $currentRequest = $this->requestStack->getCurrentRequest(); - if ($currentRequest !== null) { + if ($currentRequest instanceof Request) { return $currentRequest->getDefaultLocale(); } diff --git a/src/Provider/UserProvider.php b/src/Provider/UserProvider.php index 5811cabd..eaad653e 100644 --- a/src/Provider/UserProvider.php +++ b/src/Provider/UserProvider.php @@ -5,7 +5,8 @@ namespace Knp\DoctrineBehaviors\Provider; use Knp\DoctrineBehaviors\Contract\Provider\UserProviderInterface; -use Symfony\Component\Security\Core\Security; +use Symfony\Bundle\SecurityBundle\Security; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; final class UserProvider implements UserProviderInterface { @@ -18,7 +19,7 @@ public function __construct( public function provideUser() { $token = $this->security->getToken(); - if ($token !== null) { + if ($token instanceof TokenInterface) { $user = $token->getUser(); if ($this->blameableUserEntity) { if ($user instanceof $this->blameableUserEntity) { diff --git a/tests/Fixtures/Entity/LoggableEntity.php b/tests/Fixtures/Entity/LoggableEntity.php index 2b756d19..c52be512 100644 --- a/tests/Fixtures/Entity/LoggableEntity.php +++ b/tests/Fixtures/Entity/LoggableEntity.php @@ -59,7 +59,7 @@ public function setRoles(?array $roles = null): void $this->roles = $roles; } - public function getDate(): \DateTimeInterface|null + public function getDate(): DateTimeInterface|null { return $this->dateTime; } diff --git a/tests/Fixtures/Entity/Sluggable/SluggableEntity.php b/tests/Fixtures/Entity/Sluggable/SluggableEntity.php index 832d0078..e51dae34 100644 --- a/tests/Fixtures/Entity/Sluggable/SluggableEntity.php +++ b/tests/Fixtures/Entity/Sluggable/SluggableEntity.php @@ -28,7 +28,7 @@ class SluggableEntity implements SluggableInterface private int $id; #[Column(type: 'datetime')] - private \DateTimeInterface|\DateTime $dateTime; + private DateTimeInterface|DateTime $dateTime; public function __construct() { diff --git a/tests/Fixtures/Entity/Sluggable/SluggableMultiEntity.php b/tests/Fixtures/Entity/Sluggable/SluggableMultiEntity.php index f8235ad0..667260ba 100644 --- a/tests/Fixtures/Entity/Sluggable/SluggableMultiEntity.php +++ b/tests/Fixtures/Entity/Sluggable/SluggableMultiEntity.php @@ -28,7 +28,7 @@ class SluggableMultiEntity implements SluggableInterface private ?string $name = null; #[Column(type: 'datetime')] - private \DateTimeInterface|\DateTime $dateTime; + private DateTimeInterface|DateTime $dateTime; public function __construct() { diff --git a/tests/ORM/TreeNodeTest.php b/tests/ORM/TreeNodeTest.php index f2a7cea8..10e1486c 100644 --- a/tests/ORM/TreeNodeTest.php +++ b/tests/ORM/TreeNodeTest.php @@ -353,8 +353,8 @@ public function testMoveChildren(): void public function testGetTree(): void { - /** @var TreeNodeRepository $treeNodeRepository */ - $treeNodeRepository = $this->entityManager->getRepository(TreeNodeEntity::class); + /** @var TreeNodeRepository $entityRepository */ + $entityRepository = $this->entityManager->getRepository(TreeNodeEntity::class); $entity = new TreeNodeEntity(); $entity->setId(1); @@ -372,7 +372,7 @@ public function testGetTree(): void $this->entityManager->persist($entity[0][0]); $this->entityManager->flush(); - $tree = $treeNodeRepository->getTree(); + $tree = $entityRepository->getTree(); $this->assertSame($tree[0][0], $entity[0][0]); } diff --git a/tests/config/config_test.php b/tests/config/config_test.php index 7a20bf2e..1f6072b7 100644 --- a/tests/config/config_test.php +++ b/tests/config/config_test.php @@ -9,8 +9,8 @@ use Knp\DoctrineBehaviors\Tests\Provider\TestLocaleProvider; use Knp\DoctrineBehaviors\Tests\Provider\TestUserProvider; use Psr\Log\Test\TestLogger; +use Symfony\Bundle\SecurityBundle\Security; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symfony\Component\Security\Core\Security; use function Symfony\Component\DependencyInjection\Loader\Configurator\service; return static function (ContainerConfigurator $containerConfigurator): void {