diff --git a/sources/AppBundle/Accounting/Entity/Event.php b/sources/AppBundle/Accounting/Entity/Event.php new file mode 100644 index 000000000..d6110ac23 --- /dev/null +++ b/sources/AppBundle/Accounting/Entity/Event.php @@ -0,0 +1,23 @@ + + */ +final class EventRepository extends EntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Event::class); + } + + /** + * @return array + */ + public function getAllSortedByName(): array + { + return $this->createQueryBuilder('e') + ->orderBy('e.name', 'asc') + ->getQuery() + ->execute(); + } +} diff --git a/sources/AppBundle/Accounting/Entity/Rule.php b/sources/AppBundle/Accounting/Entity/Rule.php index 1e412bcd0..df6377f27 100644 --- a/sources/AppBundle/Accounting/Entity/Rule.php +++ b/sources/AppBundle/Accounting/Entity/Rule.php @@ -31,8 +31,9 @@ class Rule #[ORM\JoinColumn(nullable: true)] public ?Category $category = null; - #[ORM\Column(nullable: true)] - public ?int $eventId = null; + #[ORM\OneToOne()] + #[ORM\JoinColumn(nullable: true)] + public ?Event $event = null; #[ORM\Column(name: 'mode_regl_id', nullable: true)] public ?int $paymentTypeId = null; diff --git a/sources/AppBundle/Accounting/Form/RuleType.php b/sources/AppBundle/Accounting/Form/RuleType.php index 6b23eb427..f14be2aaa 100644 --- a/sources/AppBundle/Accounting/Form/RuleType.php +++ b/sources/AppBundle/Accounting/Form/RuleType.php @@ -5,7 +5,9 @@ namespace AppBundle\Accounting\Form; use AppBundle\Accounting\Entity\Category; -use AppBundle\Accounting\Model\Repository\EventRepository; +use AppBundle\Accounting\Entity\Event; +use AppBundle\Accounting\Entity\Repository\CategoryRepository; +use AppBundle\Accounting\Entity\Repository\EventRepository; use AppBundle\Model\ComptaModeReglement; use Symfony\Bridge\Doctrine\Form\Type\EntityType; use Symfony\Component\Form\AbstractType; @@ -17,17 +19,12 @@ class RuleType extends AbstractType { public function __construct( + private readonly CategoryRepository $categoryRepository, private readonly EventRepository $eventRepository, ) {} public function buildForm(FormBuilderInterface $builder, array $options): void { - $events = []; - $events[''] = null; - foreach ($this->eventRepository->getAllSortedByName() as $event) { - $events[$event->getName()] = $event->getId(); - } - $builder->add('label', TextType::class, [ 'label' => 'Nom de la régle', 'required' => true, @@ -65,11 +62,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ])->add('category', EntityType::class, [ 'label' => 'Catégorie', 'class' => Category::class, + 'choices' => $this->categoryRepository->getAllSortedByName(), 'choice_label' => 'name', 'required' => false, - ])->add('eventId', ChoiceType::class, [ + ])->add('event', EntityType::class, [ 'label' => 'Évènement', - 'choices' => $events, + 'class' => Event::class, + 'choices' => $this->eventRepository->getAllSortedByName(), + 'choice_label' => 'name', 'required' => false, ])->add('attachmentRequired', ChoiceType::class, [ 'label' => ' Justificatif obligatoire ? ', diff --git a/sources/AppBundle/Accounting/Model/Event.php b/sources/AppBundle/Accounting/Model/Event.php deleted file mode 100644 index d287c96a0..000000000 --- a/sources/AppBundle/Accounting/Model/Event.php +++ /dev/null @@ -1,55 +0,0 @@ -id; - } - - public function setId(int $id): self - { - $this->propertyChanged('id', $this->id, $id); - $this->id = $id; - return $this; - } - - public function getName(): ?string - { - return $this->name; - } - - public function setName(?string $name): self - { - $this->propertyChanged('name', $this->name, $name); - $this->name = $name; - - return $this; - } - - public function getHideInAccountingJournalAt(): ?\DateTime - { - return $this->hideInAccountingJournalAt; - } - - public function setHideInAccountingJournalAt(?\DateTime $hideInAccountingJournalAt): void - { - $this->propertyChanged('hideInAccountingJournalAt', $this->hideInAccountingJournalAt, $hideInAccountingJournalAt); - $this->hideInAccountingJournalAt = $hideInAccountingJournalAt; - } -} diff --git a/sources/AppBundle/Accounting/Model/Repository/EventRepository.php b/sources/AppBundle/Accounting/Model/Repository/EventRepository.php deleted file mode 100644 index f18ff2050..000000000 --- a/sources/AppBundle/Accounting/Model/Repository/EventRepository.php +++ /dev/null @@ -1,60 +0,0 @@ - - */ -class EventRepository extends Repository implements MetadataInitializer -{ - /** - * @return CollectionInterface - */ - public function getAllSortedByName(): CollectionInterface - { - $query = $this->getQuery('SELECT * FROM compta_evenement ORDER BY evenement asc'); - return $query->query($this->getCollection(new HydratorSingleObject())); - } - - public static function initMetadata(SerializerFactoryInterface $serializerFactory, array $options = []) - { - $metadata = new Metadata($serializerFactory); - - $metadata->setEntity(Event::class); - $metadata->setConnectionName('main'); - $metadata->setDatabase($options['database']); - $metadata->setTable('compta_evenement'); - - $metadata - ->addField([ - 'columnName' => 'id', - 'fieldName' => 'id', - 'primary' => true, - 'autoincrement' => true, - 'type' => 'int', - ]) - ->addField([ - 'columnName' => 'evenement', - 'fieldName' => 'name', - 'type' => 'string', - ]) - ->addField([ - 'columnName' => 'hide_in_accounting_journal_at', - 'fieldName' => 'hideInAccountingJournalAt', - 'type' => 'datetime', - ]) - ; - - return $metadata; - } -} diff --git a/sources/AppBundle/Controller/Admin/Accounting/Configuration/AddEventAction.php b/sources/AppBundle/Controller/Admin/Accounting/Configuration/AddEventAction.php index 0fc15168a..105c30230 100644 --- a/sources/AppBundle/Controller/Admin/Accounting/Configuration/AddEventAction.php +++ b/sources/AppBundle/Controller/Admin/Accounting/Configuration/AddEventAction.php @@ -5,8 +5,8 @@ namespace AppBundle\Controller\Admin\Accounting\Configuration; use AppBundle\Accounting\Form\EventType; -use AppBundle\Accounting\Model\Event; -use AppBundle\Accounting\Model\Repository\EventRepository; +use AppBundle\Accounting\Entity\Event; +use AppBundle\Accounting\Entity\Repository\EventRepository; use AppBundle\AuditLog\Audit; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -26,7 +26,7 @@ public function __invoke(Request $request): Response $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $this->eventRepository->save($event); - $this->audit->log('Ajout de l\'évènement ' . $event->getName()); + $this->audit->log('Ajout de l\'évènement ' . $event->name); $this->addFlash('notice', 'L\'évènement a été ajouté'); return $this->redirectToRoute('admin_accounting_events_list'); } diff --git a/sources/AppBundle/Controller/Admin/Accounting/Configuration/EditEventAction.php b/sources/AppBundle/Controller/Admin/Accounting/Configuration/EditEventAction.php index 92476a030..85540e2ba 100644 --- a/sources/AppBundle/Controller/Admin/Accounting/Configuration/EditEventAction.php +++ b/sources/AppBundle/Controller/Admin/Accounting/Configuration/EditEventAction.php @@ -5,7 +5,7 @@ namespace AppBundle\Controller\Admin\Accounting\Configuration; use AppBundle\Accounting\Form\EventType; -use AppBundle\Accounting\Model\Repository\EventRepository; +use AppBundle\Accounting\Entity\Repository\EventRepository; use AppBundle\AuditLog\Audit; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; @@ -20,12 +20,12 @@ public function __construct( public function __invoke(int $id,Request $request): Response { - $event = $this->eventRepository->get($id); + $event = $this->eventRepository->find($id); $form = $this->createForm(EventType::class, $event); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $this->eventRepository->save($event); - $this->audit->log('Modification de l\'évènement ' . $event->getName()); + $this->audit->log('Modification de l\'évènement ' . $event->name); $this->addFlash('notice', 'L\'évènement a été modifié'); return $this->redirectToRoute('admin_accounting_events_list'); } diff --git a/sources/AppBundle/Controller/Admin/Accounting/Configuration/ListEventAction.php b/sources/AppBundle/Controller/Admin/Accounting/Configuration/ListEventAction.php index f9b6d2871..321d0385f 100644 --- a/sources/AppBundle/Controller/Admin/Accounting/Configuration/ListEventAction.php +++ b/sources/AppBundle/Controller/Admin/Accounting/Configuration/ListEventAction.php @@ -4,7 +4,7 @@ namespace AppBundle\Controller\Admin\Accounting\Configuration; -use AppBundle\Accounting\Model\Repository\EventRepository; +use AppBundle\Accounting\Entity\Repository\EventRepository; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Twig\Environment; diff --git a/sources/AppBundle/StaticAnalysis/Rule/DoctrineRepositoryRule.php b/sources/AppBundle/StaticAnalysis/Rule/DoctrineRepositoryRule.php index 646d1a2fe..264d6a944 100644 --- a/sources/AppBundle/StaticAnalysis/Rule/DoctrineRepositoryRule.php +++ b/sources/AppBundle/StaticAnalysis/Rule/DoctrineRepositoryRule.php @@ -13,6 +13,7 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\Type; +use Symfony\Component\Form\AbstractType; /** * @implements Rule @@ -28,11 +29,13 @@ ]; private ClassReflection $repositoryClassReflection; + private ClassReflection $formClassReflection; public function __construct( private ReflectionProvider $reflectionProvider, ) { $this->repositoryClassReflection = $this->reflectionProvider->getClass(EntityRepository::class); + $this->formClassReflection = $this->reflectionProvider->getClass(AbstractType::class); } public function getNodeType(): string @@ -57,6 +60,8 @@ public function processNode(Node $node, Scope $scope): array // Si l'appel est fait depuis l'intérieur d'un repository, c'est autorisé || $this->isRepositoryClass($scope->getClassReflection()) + // Si l'appel est fait depuis l'intérieur d'un formulaire, c'est autorisé + || $this->isFormClass($scope->getClassReflection()) ) { return []; } @@ -102,6 +107,28 @@ private function isRepositoryClass(ClassReflection|Type $target): bool || $target->isSubclassOfClass($this->repositoryClassReflection); } + private function isFormClass(ClassReflection|Type $target): bool + { + if ($target instanceof Type) { + $classes = $target->getObjectClassNames(); + + foreach ($classes as $class) { + $classReflection = $this->reflectionProvider->getClass($class); + + if ($classReflection->getName() === $this->formClassReflection->getName() + || $classReflection->isSubclassOfClass($this->formClassReflection) + ) { + return true; + } + } + + return false; + } + + return $target->getName() === $this->formClassReflection->getName() + || $target->isSubclassOfClass($this->formClassReflection); + } + private function isMethodOverridden(Type $type, string $methodName): bool { if ($type->isObject()->no()) { diff --git a/templates/admin/accounting/configuration/rule_form.html.twig b/templates/admin/accounting/configuration/rule_form.html.twig index 74fc40b8c..3ec43b6c8 100644 --- a/templates/admin/accounting/configuration/rule_form.html.twig +++ b/templates/admin/accounting/configuration/rule_form.html.twig @@ -13,7 +13,7 @@ {{ form_row(form.paymentTypeId) }} {{ form_row(form.vat) }} {{ form_row(form.category) }} - {{ form_row(form.eventId) }} + {{ form_row(form.event) }} {{ form_row(form.attachmentRequired) }} diff --git a/tests/behat/features/Admin/Tresorerie/Configuration.feature b/tests/behat/features/Admin/Tresorerie/Configuration.feature index 0cc5e723d..0820a6f48 100644 --- a/tests/behat/features/Admin/Tresorerie/Configuration.feature +++ b/tests/behat/features/Admin/Tresorerie/Configuration.feature @@ -106,7 +106,7 @@ Feature: Administration - Trésorerie - Configuration # À determiner When I fill in "rule[category]" with "26" # Association AFUP - When I fill in "rule[eventId]" with "27" + When I fill in "rule[event]" with "27" # Justification obligatoire When I fill in "rule[attachmentRequired]" with "1" And I press "Ajouter" @@ -120,5 +120,5 @@ Feature: Administration - Trésorerie - Configuration And The "rule[paymentTypeId]" field should have the following selected value "2" And The "rule[vat]" field should have the following selected value "5_5" And The "rule[category]" field should have the following selected value "26" - And The "rule[eventId]" field should have the following selected value "27" + And The "rule[event]" field should have the following selected value "27" And The "rule[attachmentRequired]" field should have the following selected value "1" diff --git a/tests/integration/AppBundle/StaticAnalysis/Rule/DoctrineRepositoryRuleTest.php b/tests/integration/AppBundle/StaticAnalysis/Rule/DoctrineRepositoryRuleTest.php index 056cd5da4..8f8741bfe 100644 --- a/tests/integration/AppBundle/StaticAnalysis/Rule/DoctrineRepositoryRuleTest.php +++ b/tests/integration/AppBundle/StaticAnalysis/Rule/DoctrineRepositoryRuleTest.php @@ -36,5 +36,6 @@ public function testRule(): void $this->analyse([__DIR__ . '/../../../../stubs/phpstan/Doctrine/SubClassRepository.php'], []); $this->analyse([__DIR__ . '/../../../../stubs/phpstan/Doctrine/NotRepository.php'], []); $this->analyse([__DIR__ . '/../../../../stubs/phpstan/Doctrine/SubNotRepository.php'], []); + $this->analyse([__DIR__ . '/../../../../stubs/phpstan/Doctrine/FormType.php'], []); } } diff --git a/tests/stubs/phpstan/Doctrine/FormType.php b/tests/stubs/phpstan/Doctrine/FormType.php new file mode 100644 index 000000000..c369b009e --- /dev/null +++ b/tests/stubs/phpstan/Doctrine/FormType.php @@ -0,0 +1,20 @@ +add('stdclass', EntityType::class, [ + 'query_builder' => fn(EntityRepository $er) => $er->createQueryBuilder('foo')->where('foo.id = 1'), + ]); + } +}