diff --git a/phpstan-baseline.php b/phpstan-baseline.php index f1f8774b8..c101e3df8 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -6235,54 +6235,6 @@ 'count' => 1, 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/BadgeRepository.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Cannot cast mixed to string\\.$#', - 'identifier' => 'cast.string', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method AppBundle\\\\Event\\\\Model\\\\Repository\\\\EventCouponRepository\\:\\:changeCouponForEvent\\(\\) has parameter \\$coupons with no value type specified in iterable type array\\.$#', - 'identifier' => 'missingType.iterableValue', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method AppBundle\\\\Event\\\\Model\\\\Repository\\\\EventCouponRepository\\:\\:couponsListForEvent\\(\\) has no return type specified\\.$#', - 'identifier' => 'missingType.return', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method AppBundle\\\\Event\\\\Model\\\\Repository\\\\EventCouponRepository\\:\\:initMetadata\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#', - 'identifier' => 'missingType.iterableValue', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method AppBundle\\\\Event\\\\Model\\\\Repository\\\\EventCouponRepository\\:\\:initMetadata\\(\\) should return M of CCMBenchmark\\\\Ting\\\\Repository\\\\Metadata but returns CCMBenchmark\\\\Ting\\\\Repository\\\\Metadata\\\\.$#', - 'identifier' => 'return.type', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(mixed\\)\\: mixed\\)\\|null, Closure\\(AppBundle\\\\Event\\\\Model\\\\EventCoupon\\)\\: string given\\.$#', - 'identifier' => 'argument.type', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$databaseName of method CCMBenchmark\\\\Ting\\\\Repository\\\\Metadata\\\\:\\:setDatabase\\(\\) expects string, mixed given\\.$#', - 'identifier' => 'argument.type', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$iterator of function iterator_to_array expects iterable, mixed given\\.$#', - 'identifier' => 'argument.type', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php', -]; $ignoreErrors[] = [ 'message' => '#^Argument of an invalid type CCMBenchmark\\\\Ting\\\\Repository\\\\CollectionInterface\\\\|null supplied for foreach, only iterables are supported\\.$#', 'identifier' => 'foreach.nonIterable', diff --git a/sources/AppBundle/Controller/Admin/Event/EventAction.php b/sources/AppBundle/Controller/Admin/Event/EventAction.php index 5cdc42f98..3e1610b05 100644 --- a/sources/AppBundle/Controller/Admin/Event/EventAction.php +++ b/sources/AppBundle/Controller/Admin/Event/EventAction.php @@ -4,9 +4,9 @@ namespace AppBundle\Controller\Admin\Event; +use AppBundle\Event\Entity\Repository\EventCouponRepository; use AppBundle\Event\Form\EventType; use AppBundle\Event\Model\Event; -use AppBundle\Event\Model\Repository\EventCouponRepository; use AppBundle\Event\Model\Repository\EventRepository; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Form\FormInterface; @@ -35,7 +35,7 @@ public function __invoke(Request $request, $id): Response $form = $this->createForm(EventType::class, $event); - if ($event->getId() && $couponsImploded = $this->couponRepository->couponsListForEventImploded($event)) { + if ($event->getId() && $couponsImploded = $this->couponRepository->couponsListForEventImploded((int) $event->getId())) { $form->get('coupons')->setData($couponsImploded); } @@ -49,7 +49,7 @@ public function __invoke(Request $request, $id): Response if ($form->get('coupons')->getData()) { $eventCoupons = explode(',', (string) $form->get('coupons')->getData()); - $this->couponRepository->changeCouponForEvent($event, $eventCoupons); + $this->couponRepository->changeCouponForEvent((int) $event->getId(), $eventCoupons); } $this->addFlash('notice', 'Évènement ' . ($id ? 'modifié' : 'ajouté')); diff --git a/sources/AppBundle/Event/Entity/EventCoupon.php b/sources/AppBundle/Event/Entity/EventCoupon.php new file mode 100644 index 000000000..54b31d31a --- /dev/null +++ b/sources/AppBundle/Event/Entity/EventCoupon.php @@ -0,0 +1,24 @@ + + */ +final class EventCouponRepository extends EntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, EventCoupon::class); + } + + /** + * @param list $coupons + */ + public function changeCouponForEvent(int $eventId, array $coupons): void + { + $this->getEntityManager()->wrapInTransaction(function () use ($eventId, $coupons): void { + ($qb = $this->createQueryBuilder('c')) + ->delete() + ->where($qb->expr()->eq('c.eventId', ':eventId')) + ->setParameter('eventId', $eventId) + ->getQuery() + ->execute(); + + foreach ($coupons as $coupon) { + $coupon = trim($coupon); + if ($coupon === '') { + continue; + } + + $eventCoupon = new EventCoupon(); + $eventCoupon->eventId = $eventId; + $eventCoupon->texte = $coupon; + $this->getEntityManager()->persist($eventCoupon); + } + + $this->getEntityManager()->flush(); + }); + } + + /** + * @return list + */ + public function couponsListForEvent(int $eventId): array + { + return ($qb = $this->createQueryBuilder('c')) + ->where($qb->expr()->eq('c.eventId', ':eventId')) + ->setParameter('eventId', $eventId) + ->orderBy('c.id', 'ASC') + ->getQuery() + ->getResult(); + } + + public function couponsListForEventImploded(int $eventId, string $separator = ', '): string + { + $texts = array_map( + static fn(EventCoupon $coupon): string => $coupon->texte, + $this->couponsListForEvent($eventId), + ); + + return implode($separator, $texts); + } +} diff --git a/sources/AppBundle/Event/Model/EventCoupon.php b/sources/AppBundle/Event/Model/EventCoupon.php deleted file mode 100644 index 108bacaf2..000000000 --- a/sources/AppBundle/Event/Model/EventCoupon.php +++ /dev/null @@ -1,68 +0,0 @@ -getId() === null) { - throw new \Exception('Event has no ID'); - } - - if (strlen($coupon) === 0) { - throw new \Exception('Coupon argument is not a not empty string'); - } - - $eventCoupon = new self(); - $eventCoupon->setIdEvent($event->getId()); - $eventCoupon->setText($coupon); - - return $eventCoupon; - } - - public function getId(): ?int - { - return $this->id; - } - - public function setId(int $id): void - { - $this->id = $id; - } - - public function getIdEvent(): ?int - { - return $this->idEvent; - } - - public function setIdEvent(int $idEvent): void - { - $this->propertyChanged('idEvent', $this->idEvent ?? '', $idEvent); - $this->idEvent = $idEvent; - } - - public function getText(): string - { - return $this->text; - } - - public function setText(string $text): void - { - $this->propertyChanged('text', $this->text ?? '', $text); - $this->text = $text; - } -} diff --git a/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php b/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php deleted file mode 100644 index 87224885d..000000000 --- a/sources/AppBundle/Event/Model/Repository/EventCouponRepository.php +++ /dev/null @@ -1,83 +0,0 @@ - - */ -class EventCouponRepository extends Repository implements MetadataInitializer -{ - public static function initMetadata(SerializerFactoryInterface $serializerFactory, array $options = []) - { - $metadata = new Metadata($serializerFactory); - $metadata->setEntity(EventCoupon::class); - $metadata->setConnectionName('main'); - $metadata->setDatabase($options['database']); - $metadata->setTable('afup_forum_coupon'); - - $metadata - ->addField([ - 'columnName' => 'id', - 'fieldName' => 'id', - 'primary' => true, - 'autoincrement' => true, - 'type' => 'int', - ]) - ->addField([ - 'columnName' => 'id_forum', - 'fieldName' => 'idEvent', - 'type' => 'int', - ]) - ->addField([ - 'columnName' => 'texte', - 'fieldName' => 'text', - 'type' => 'string', - ]); - - return $metadata; - } - - public function changeCouponForEvent(Event $event, array $coupons): void - { - $query = $this->getQuery('DELETE FROM afup_forum_coupon WHERE id_forum = :id'); - $query->setParams([ - 'id' => $event->getId(), - ]); - $query->execute(); - - foreach ($coupons as $coupon) { - $coupon = trim((string) $coupon); - if (empty($coupon) === true) { - continue; - } - $this->save(EventCoupon::initForEventAndCoupon($event, $coupon)); - } - } - - public function couponsListForEvent(Event $event) - { - $query = $this->getQuery('SELECT * FROM afup_forum_coupon WHERE id_forum = :id'); - $query->setParams([ - 'id' => $event->getId(), - ]); - return $query->query($this->getCollection(new HydratorSingleObject())); - } - - public function couponsListForEventImploded(Event $event, string $separator = ', '): string - { - $eventCoupons = $this->couponsListForEvent($event); - $array = array_map(static fn(EventCoupon $coupon): string => $coupon->getText(), iterator_to_array($eventCoupons)); - - return implode($separator, $array); - } -} diff --git a/tests/integration/AppBundle/Event/Entity/Repository/EventCouponRepositoryTest.php b/tests/integration/AppBundle/Event/Entity/Repository/EventCouponRepositoryTest.php new file mode 100644 index 000000000..b66689f7d --- /dev/null +++ b/tests/integration/AppBundle/Event/Entity/Repository/EventCouponRepositoryTest.php @@ -0,0 +1,74 @@ +get(EventCouponRepository::class); + $connection = self::getContainer()->get(Connection::class); + + $repository->changeCouponForEvent(42, ['FREE_FORUM', ' SUPER_FORUM ', '', ' ']); + + self::assertSame(['FREE_FORUM', 'SUPER_FORUM'], $this->fetchCouponTexts($connection, 42)); + + $repository->changeCouponForEvent(42, ['OTHER_COUPON']); + + self::assertSame(['OTHER_COUPON'], $this->fetchCouponTexts($connection, 42)); + } + + public function testChangeCouponForEventWithOnlyEmptyCouponsEmptiesTheTable(): void + { + $repository = self::getContainer()->get(EventCouponRepository::class); + $connection = self::getContainer()->get(Connection::class); + + $repository->changeCouponForEvent(42, ['FREE_FORUM']); + $repository->changeCouponForEvent(42, ['', ' ']); + + self::assertSame([], $this->fetchCouponTexts($connection, 42)); + } + + public function testCouponsListForEventReturnsCouponsOfTheEventOnly(): void + { + $repository = self::getContainer()->get(EventCouponRepository::class); + $connection = self::getContainer()->get(Connection::class); + + $repository->changeCouponForEvent(42, ['COUPON_42']); + $repository->changeCouponForEvent(43, ['COUPON_43']); + + self::assertSame(['COUPON_42'], $this->fetchCouponTexts($connection, 42)); + self::assertSame(['COUPON_43'], $this->fetchCouponTexts($connection, 43)); + } + + public function testCouponsListForEventImplodedJoinsCouponTexts(): void + { + $repository = self::getContainer()->get(EventCouponRepository::class); + + $repository->changeCouponForEvent(42, ['COUPON_A', 'COUPON_B']); + + self::assertSame('COUPON_A, COUPON_B', $repository->couponsListForEventImploded(42)); + self::assertSame('COUPON_A|COUPON_B', $repository->couponsListForEventImploded(42, '|')); + self::assertSame('', $repository->couponsListForEventImploded(999)); + } + + /** + * @return list + */ + private function fetchCouponTexts(Connection $connection, int $eventId): array + { + return array_column( + $connection->fetchAllAssociative( + 'SELECT texte FROM afup_forum_coupon WHERE id_forum = :id ORDER BY id', + ['id' => $eventId], + ), + 'texte', + ); + } +}