Symfony: syntaxe correcte d'un Form un peu complexe

a marqué ce sujet comme résolu.

Bonjour !

Désolé je spamme ces temps-ci. C’est que je suis enthousiaste sur un nouveau projet intéressant ^^'

Toujours pour mon salon du livre, je m’intéresse aux commandes de livres, maintenant. Et comme j’avais laissé tombé twig et les forms de Symfony pour des frontends en JS/Ajax ces derniers temps, je m’emmêle les pinceaux en essayant de respecter les formes (ah ah) de Symfony.

J’ai une Entité Stock, qui a notamment comme champ "quantités demandées" (ordered). Mais il a surtout un champ Book (Entité) et un champ Author (Entité aussi). L’idée est de pouvoir retrouver les commandes par auteurs, par livres, et par plein d’autres trucs utiles pour les administrateurs.

Un Auteur peut depuis son interface commander ses propres livres. Là ça va, c’est facile. Ce que j’aimerais maintenant c’est permettre aux Administrateurs de passer aussi des commandes. Le processus que j’aimerais qu’ils suivent, c’est que :

1- ils choisissent dans un menu déroulant l’Auteur

2- et c’est là mon souci : il y a ensuite un champ livre qui ne propose QUE les livres de l’auteur sélectionné en 1 pour éviter les erreurs et les requêtes inutiles. C’est une variante du formulaire de création depuis l’interface des auteurs dans laquelle je passais au formulaire les 'choices’ de mon ChoiceType depuis le Controller (ie tous les livres de l’auteur connecté).

3- suite du formulaire dans lequel ils notent les quantités demandées.

class AdminStockCreationFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('Author', EntityType::class, [
                'class' => Author::class,
                'choice_label' => 'name',
            ]);

        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event): void {
            
            $form = $event->getForm();
            $author = $event->getData();

            $formOptions = [
                'label' => 'Livre',
                'class' => Book::class,
                'placeholder' => 'Choisissez le livre',
                'choice_label' => 'title',
                'query_builder' => function (BookRepository $bookRep, AuthorRepository $authorRep): QueryBuilder {
                    return $authorBooks = $author->getBooks();
                },
            ];

            // create the field, this is similar the $builder->add()
            // field name, field type, field options
            $form->add('book', EntityType::class, $formOptions);
        });
        
        $builder             
            ->add('ordered')
            ->add('origin')
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Stock::class,
        ]);
    }
}

Si je fais ça, j’ai une erreur "Too few arguments to function App\Form\AdminStockCreationFormType::App\Form{closure}(), 1 passed in C:\wamp64\www\salondulivre\vendor\symfony\doctrine-bridge\Form\Type\EntityType.php on line 32 and exactly 2 expected".

Si je ne mets rien dans la fonction query_builder il me dit " undefined method named "getBooks" of class "App\Entity\Stock".

Si je mets AuthorRepository $authorRep, il me dit qu’il attend un BookRepository.

Je dois donc m’y prendre comme un manche, mais l’idée est là, pourtant. Que dois-je corriger s’il vous plait ?

Edit : en écrivant ceci je me rends compte que mon champ Author dans mon Stock est superflu. Il y a d’autres moyens d’afficher les données. C’est le livre, qui compte, bien plus que l’auteur. Mais ça ne change rien au processus que les administrateurs doivent suivre pour passer une commande…

+0 -0

Salut !

Le message d’erreur veut dire que tu ne peux pas avoir de closure pour 'query_builder' qui ait deux paramètres, c’est un seul, et forcément le repository de l’entité spécifiée dans 'class'.
Ceci étant, il faudrait donc faire une requête pour avoir les livres dont l’auteur est celui qui a été sélectionné, donc quelque chose comme $bookRepo->findByAuthor($author);. Pour plus de clarté, je préciserais use ($author): … à la déclaration de la fonction anonyme, même si le second message indique que ce n’est pas nécessaire.
Cependant, il dit aussi que $author n’est pas un objet Author, mais un objet Stock, et c’est normal : les données du formulaire sont au format déterminé par 'data_class'. Il faut donc soit attendre la soumission du champ author ($builder->get('author')->addEventListener(…), soit avec ce que tu as déjà récupérer l’auteur du stock ($stock->getAuthor()). Suivant le cas, la seconde méthode posera problème, surtout si tu décides de te passer de l’auteur dans l’objet Stock.

Il y aurait aussi la possibilité d’utiliser non pas 'query_builder', mais 'choices', où on pourrait lui passer directement $author->getBooks() probablement. Seulement, si l’auteur est prolifique, il pourrait y avoir des soucis de performances, je sais que l’utilisation de 'query_builder' permet une mise en cache, je ne sais pas pour l’utilisation du getter dans ce cas. Même constat qu’en fin de paragraphe précédent : si plus d’auteur dans l’objet Stock, cette manière de faire n’est pas la bonne.

Pour le fait que le champ author n’est pas nécessaire au niveau des données, mais plus facile pour la sélection dans le formulaire, tu peux le mettre en 'mapped' => false', ce qui fait que tu reçois toujours la valeur (un peu différemment, certes), mais Symfony ne tente pas de l’injecter dans l’entité.

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte