diff --git a/phpstan-baseline.php b/phpstan-baseline.php index f85ead6d8..f4a4f8018 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -4448,38 +4448,38 @@ 'path' => __DIR__ . '/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php', ]; $ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$comment of method AppBundle\\\\Event\\\\Model\\\\SpeakerSuggestion\\:\\:setComment\\(\\) expects string, mixed given\\.$#', + 'message' => '#^Parameter \\#2 \\$data of method AppBundle\\\\Controller\\\\Event\\\\Speaker\\\\SuggestionAction\\:\\:createSpeakerSuggestion\\(\\) expects array, mixed given\\.$#', 'identifier' => 'argument.type', 'count' => 1, 'path' => __DIR__ . '/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php', ]; $ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$eventId of method AppBundle\\\\Event\\\\Model\\\\SpeakerSuggestion\\:\\:setEventId\\(\\) expects int, int\\|null given\\.$#', - 'identifier' => 'argument.type', + 'message' => '#^Property AppBundle\\\\Event\\\\Entity\\\\SpeakerSuggestion\\:\\:\\$comment \\(string\\|null\\) does not accept mixed\\.$#', + 'identifier' => 'assign.propertyType', 'count' => 1, 'path' => __DIR__ . '/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php', ]; $ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$speakerName of method AppBundle\\\\Event\\\\Model\\\\SpeakerSuggestion\\:\\:setSpeakerName\\(\\) expects string, mixed given\\.$#', - 'identifier' => 'argument.type', + 'message' => '#^Property AppBundle\\\\Event\\\\Entity\\\\SpeakerSuggestion\\:\\:\\$eventId \\(int\\) does not accept int\\|null\\.$#', + 'identifier' => 'assign.propertyType', 'count' => 1, 'path' => __DIR__ . '/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php', ]; $ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$suggesterEmail of method AppBundle\\\\Event\\\\Model\\\\SpeakerSuggestion\\:\\:setSuggesterEmail\\(\\) expects string, mixed given\\.$#', - 'identifier' => 'argument.type', + 'message' => '#^Property AppBundle\\\\Event\\\\Entity\\\\SpeakerSuggestion\\:\\:\\$speakerName \\(string\\) does not accept mixed\\.$#', + 'identifier' => 'assign.propertyType', 'count' => 1, 'path' => __DIR__ . '/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php', ]; $ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$suggesterName of method AppBundle\\\\Event\\\\Model\\\\SpeakerSuggestion\\:\\:setSuggesterName\\(\\) expects string, mixed given\\.$#', - 'identifier' => 'argument.type', + 'message' => '#^Property AppBundle\\\\Event\\\\Entity\\\\SpeakerSuggestion\\:\\:\\$suggesterEmail \\(string\\) does not accept mixed\\.$#', + 'identifier' => 'assign.propertyType', 'count' => 1, 'path' => __DIR__ . '/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php', ]; $ignoreErrors[] = [ - 'message' => '#^Parameter \\#2 \\$data of method AppBundle\\\\Controller\\\\Event\\\\Speaker\\\\SuggestionAction\\:\\:createSpeakerSuggestion\\(\\) expects array, mixed given\\.$#', - 'identifier' => 'argument.type', + 'message' => '#^Property AppBundle\\\\Event\\\\Entity\\\\SpeakerSuggestion\\:\\:\\$suggesterName \\(string\\) does not accept mixed\\.$#', + 'identifier' => 'assign.propertyType', 'count' => 1, 'path' => __DIR__ . '/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php', ]; @@ -6631,24 +6631,6 @@ 'count' => 1, 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/SpeakerRepository.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method AppBundle\\\\Event\\\\Model\\\\Repository\\\\SpeakerSuggestionRepository\\:\\: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/SpeakerSuggestionRepository.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method AppBundle\\\\Event\\\\Model\\\\Repository\\\\SpeakerSuggestionRepository\\:\\:initMetadata\\(\\) return type with generic class CCMBenchmark\\\\Ting\\\\Repository\\\\Metadata does not specify its types\\: T$#', - 'identifier' => 'missingType.generics', - 'count' => 1, - 'path' => __DIR__ . '/sources/AppBundle/Event/Model/Repository/SpeakerSuggestionRepository.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/SpeakerSuggestionRepository.php', -]; $ignoreErrors[] = [ 'message' => '#^Method AppBundle\\\\Event\\\\Model\\\\Repository\\\\SponsorScanRepository\\:\\:getBySponsorTicket\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', diff --git a/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php b/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php index 65520902b..6656198bd 100644 --- a/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php +++ b/sources/AppBundle/Controller/Event/Speaker/SuggestionAction.php @@ -9,8 +9,8 @@ use AppBundle\Email\Mailer\MailUserFactory; use AppBundle\Event\Form\SpeakerSuggestionType; use AppBundle\Event\Model\Event; -use AppBundle\Event\Model\Repository\SpeakerSuggestionRepository; -use AppBundle\Event\Model\SpeakerSuggestion; +use AppBundle\Event\Entity\Repository\SpeakerSuggestionRepository; +use AppBundle\Event\Entity\SpeakerSuggestion; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -62,14 +62,15 @@ public function __invoke(Request $request, string $eventSlug): Response private function createSpeakerSuggestion(Event $event, array $data): SpeakerSuggestion { - return new SpeakerSuggestion() - ->setEventId($event->getId()) - ->setSuggesterEmail($data['suggester_email']) - ->setSuggesterName($data['suggester_name']) - ->setSpeakerName($data['speaker_name']) - ->setComment($data['comment']) - ->setCreatedAt(new \DateTime('now')) - ; + $speakerSuggestion = new SpeakerSuggestion(); + $speakerSuggestion->eventId = $event->getId(); + $speakerSuggestion->suggesterEmail = $data['suggester_email']; + $speakerSuggestion->suggesterName = $data['suggester_name']; + $speakerSuggestion->speakerName = $data['speaker_name']; + $speakerSuggestion->comment = $data['comment']; + $speakerSuggestion->createdAt = new \DateTimeImmutable('now'); + + return $speakerSuggestion; } private function sendMail(Event $event, SpeakerSuggestion $speakerSuggestion): void diff --git a/sources/AppBundle/Event/Entity/Repository/SpeakerSuggestionRepository.php b/sources/AppBundle/Event/Entity/Repository/SpeakerSuggestionRepository.php new file mode 100644 index 000000000..36255c8e9 --- /dev/null +++ b/sources/AppBundle/Event/Entity/Repository/SpeakerSuggestionRepository.php @@ -0,0 +1,20 @@ + + */ +final class SpeakerSuggestionRepository extends EntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, SpeakerSuggestion::class); + } +} diff --git a/sources/AppBundle/Event/Entity/SpeakerSuggestion.php b/sources/AppBundle/Event/Entity/SpeakerSuggestion.php new file mode 100644 index 000000000..2983734d2 --- /dev/null +++ b/sources/AppBundle/Event/Entity/SpeakerSuggestion.php @@ -0,0 +1,37 @@ + - */ -class SpeakerSuggestionRepository extends Repository implements MetadataInitializer -{ - /** - * - * @return Metadata - */ - public static function initMetadata(SerializerFactoryInterface $serializerFactory, array $options = []) - { - $metadata = new Metadata($serializerFactory); - $metadata->setEntity(SpeakerSuggestion::class); - $metadata->setConnectionName('main'); - $metadata->setDatabase($options['database']); - $metadata->setTable('afup_speaker_suggestion'); - - $metadata - ->addField([ - 'columnName' => 'id', - 'fieldName' => 'id', - 'primary' => true, - 'autoincrement' => true, - 'type' => 'int', - ]) - ->addField([ - 'columnName' => 'event_id', - 'fieldName' => 'eventId', - 'type' => 'int', - ]) - ->addField([ - 'columnName' => 'suggester_email', - 'fieldName' => 'suggesterEmail', - 'type' => 'string', - ]) - ->addField([ - 'columnName' => 'suggester_name', - 'fieldName' => 'suggesterName', - 'type' => 'string', - ]) - ->addField([ - 'columnName' => 'speaker_name', - 'fieldName' => 'speakerName', - 'type' => 'string', - ]) - ->addField([ - 'columnName' => 'comment', - 'fieldName' => 'comment', - 'type' => 'string', - ]) - ->addField([ - 'columnName' => 'created_at', - 'fieldName' => 'createdAt', - 'type' => 'datetime', - 'serializer_options' => [ - 'unserialize' => ['unSerializeUseFormat' => false], - ], - ]) - ; - - return $metadata; - } -} diff --git a/sources/AppBundle/Event/Model/SpeakerSuggestion.php b/sources/AppBundle/Event/Model/SpeakerSuggestion.php deleted file mode 100644 index 41c49a9c2..000000000 --- a/sources/AppBundle/Event/Model/SpeakerSuggestion.php +++ /dev/null @@ -1,193 +0,0 @@ -id; - } - - /** - * @param int $id - * - * @return $this - */ - public function setId($id): self - { - $this->propertyChanged('id', $this->id, $id); - $this->id = $id; - - return $this; - } - - /** - * @return mixed - */ - public function getEventId() - { - return $this->eventId; - } - - /** - * @param int $eventId - * - * @return $this - */ - public function setEventId($eventId): self - { - $this->propertyChanged('eventId', $this->eventId, $eventId); - $this->eventId = $eventId; - - return $this; - } - - /** - * @return string - */ - public function getSuggesterEmail() - { - return $this->suggesterEmail; - } - - /** - * @param string $suggesterEmail - * - * @return $this - */ - public function setSuggesterEmail($suggesterEmail): self - { - $this->propertyChanged('suggesterEmail', $this->suggesterEmail, $suggesterEmail); - $this->suggesterEmail = $suggesterEmail; - - return $this; - } - - /** - * @return string - */ - public function getSuggesterName() - { - return $this->suggesterName; - } - - /** - * @param string $suggesterName - * - * @return $this - */ - public function setSuggesterName($suggesterName): self - { - $this->propertyChanged('suggesterName', $this->suggesterName, $suggesterName); - $this->suggesterName = $suggesterName; - - return $this; - } - - /** - * @return string - */ - public function getSpeakerName() - { - return $this->speakerName; - } - - /** - * @param string $speakerName - * - * @return $this - */ - public function setSpeakerName($speakerName): self - { - $this->propertyChanged('speakerName', $this->speakerName, $speakerName); - $this->speakerName = $speakerName; - - return $this; - } - - /** - * @return string - */ - public function getComment() - { - return $this->comment; - } - - /** - * @param string $comment - * - * @return $this - */ - public function setComment($comment): self - { - $this->propertyChanged('comment', $this->comment, $comment); - $this->comment = $comment; - - return $this; - } - - /** - * @return \DateTime - */ - public function getCreatedAt() - { - return $this->createdAt; - } - - /** - * @return $this - */ - public function setCreatedAt(\DateTime $createdAt): self - { - $this->propertyChanged('createdAt', $this->createdAt, $createdAt); - $this->createdAt = $createdAt; - - return $this; - } -} diff --git a/tests/integration/AppBundle/Event/Entity/Repository/SpeakerSuggestionRepositoryTest.php b/tests/integration/AppBundle/Event/Entity/Repository/SpeakerSuggestionRepositoryTest.php new file mode 100644 index 000000000..9f5cd4641 --- /dev/null +++ b/tests/integration/AppBundle/Event/Entity/Repository/SpeakerSuggestionRepositoryTest.php @@ -0,0 +1,89 @@ +get(SpeakerSuggestionRepository::class); + + $suggestion = $this->buildSuggestion(); + $repository->save($suggestion); + + self::assertNotNull($suggestion->id); + } + + public function testSavePersistsEveryFieldOfTheSuggestion(): void + { + $repository = self::getContainer()->get(SpeakerSuggestionRepository::class); + + $suggestion = $this->buildSuggestion(); + $repository->save($suggestion); + + $fromDatabase = $repository->find($suggestion->id); + + self::assertInstanceOf(SpeakerSuggestion::class, $fromDatabase); + self::assertSame(42, $fromDatabase->eventId); + self::assertSame('suggere@example.com', $fromDatabase->suggesterEmail); + self::assertSame('Camille Soumetteur', $fromDatabase->suggesterName); + self::assertSame('Doe Speaker', $fromDatabase->speakerName); + self::assertSame('Parle très bien des sabres laser', $fromDatabase->comment); + self::assertEquals(new \DateTimeImmutable('2026-01-10 09:00:00'), $fromDatabase->createdAt); + self::assertNull($repository->find($suggestion->id + 1)); + } + + public function testSaveSupportsANullComment(): void + { + $repository = self::getContainer()->get(SpeakerSuggestionRepository::class); + + $suggestion = $this->buildSuggestion(); + $suggestion->comment = null; + $repository->save($suggestion); + + $fromDatabase = $repository->find($suggestion->id); + + self::assertInstanceOf(SpeakerSuggestion::class, $fromDatabase); + self::assertNull($fromDatabase->comment); + } + + public function testDeleteRemovesTheSuggestion(): void + { + $repository = self::getContainer()->get(SpeakerSuggestionRepository::class); + $connection = self::getContainer()->get(Connection::class); + + $suggestion = $this->buildSuggestion(); + $repository->save($suggestion); + $id = $suggestion->id; + + $repository->delete($suggestion); + + // Doctrine remet l'identifiant à null après la suppression + self::assertNull($suggestion->id); + self::assertNull($repository->find($id)); + self::assertSame( + 0, + $connection->fetchOne('SELECT COUNT(*) FROM afup_speaker_suggestion'), + ); + } + + private function buildSuggestion(): SpeakerSuggestion + { + $suggestion = new SpeakerSuggestion(); + $suggestion->eventId = 42; + $suggestion->suggesterEmail = 'suggere@example.com'; + $suggestion->suggesterName = 'Camille Soumetteur'; + $suggestion->speakerName = 'Doe Speaker'; + $suggestion->comment = 'Parle très bien des sabres laser'; + $suggestion->createdAt = new \DateTimeImmutable('2026-01-10 09:00:00'); + + return $suggestion; + } +}