IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

Gestion des formulaires : faut-il créer des FormHandler pour alléger nos contrôleurs ?


Sujet :

Symfony PHP

  1. #1
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    octobre 2019
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : octobre 2019
    Messages : 66
    Points : 72
    Points
    72
    Par défaut Gestion des formulaires : faut-il créer des FormHandler pour alléger nos contrôleurs ?
    Bonjour à tous,

    J'ai pu lire sur d'anciens tutoriels / documentation, qu'il était possible de créer des FormHandler afin de déléguer le traitement des formulaires sur Symfony.

    J'ai moi-même mis en place cette technique sur certains projets, mon but principal étant d'alléger le ou les controllers au maximum...
    Cependant j'aimerai l'avis de personnes avisées concernant cette méthode, est-ce une bonne pratique ou non sur la dernière version de Symfony ?

    Je me pose la question car je ne vois rien à ce sujet sur la documentation récente.
    En fait je me retrouve parfois avec des actions de controleurs, qui doivent gérer 3 ou 4 formulaires sur la même vue... On se retrouve donc rapidement avec des dizaines de lignes c'est pourquoi l'idée d'un FormHandler me séduit

    Je me permets de vous poster un petit exemple simplifié pour illustrer mes propos.

    App\Controller\ContactController
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    /**
         * @Route("/contact/{contact_id}", name="edit_contact")
         */
        public function editContact(Request $request)
        {
            $form = $this->createForm(ContactType::class, new Contact(), array(
                'action' => $this->generateUrl('admin_client_contact', array('contact_id' => $contact->getId()))
            ));
     
            $entityManager = $this->getDoctrine()->getManager();
            $contactHandler = new ContactHandler($form, $request, $entityManager);
     
            if ($contactHandler->process()) {
                return $this->redirectToRoute('liste_contacts');
            }
     
            return $this->render('contact.html.twig', [
                'form' => $contactHandler->getForm()->createView()
            ]);
        }
    App\FormHandler\ContactHandler
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
     
    class ContactHandler
    {
        protected $request;
        protected $form;
        protected $mailer;
     
        /**
         * @param Form $form
         * @param Request $request
         * @param EntityManagerInterface $entityManager
         *
         */
        public function __construct(Form $form, Request $request, EntityManagerInterface $entityManager)
        {
            $this->form = $form;
            $this->request = $request;
            $this->entityManager = $entityManager;
        }
     
        /**
         * Process form
         *
         * @return boolean
         */
        public function process()
        {
            $this->form->handleRequest($this->request);
     
            if ($this->form->isSubmitted() && $this->form->isValid()) {
                $this->onSuccess();
                return true;
            }
     
            return false;
        }
     
        /**
         * Get form
         *
         * @return
         */
        public function getForm()
        {
            return $this->form;
        }
     
        /**
         * Persist on success
         */
        protected function onSuccess()
        {
            $this->entityManager->persist($this->form->getData()).
            $this->entityManager->flush();
        }
    }
    Aujourd'hui je me retrouve avec ce problème, car je souhaite gérer 4 ou 5 formulaires pour certains quasi-identiques sur la même vue...

    Tous les avis seront les bienvenus !

    Merci d'avance,
    PhiMau

  2. #2
    Membre émérite

    Profil pro
    Inscrit en
    mai 2008
    Messages
    1 575
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : mai 2008
    Messages : 1 575
    Points : 2 441
    Points
    2 441
    Par défaut
    Je ne me souviens pas avoir vu précisément cette technique des formhandlers dans la doc de symfony; mais tout ce qui permet de retirer du contrôleur tout ce qui n'a pas directement lié à une Request ou une Response est une bonne idée.

  3. #3
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    octobre 2019
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : octobre 2019
    Messages : 66
    Points : 72
    Points
    72
    Par défaut
    Merci pour cette réponse, donc le code exemple que j'ai montré ne vous choque pas ? (le fait que le "form->handlerequest" se fasse en dehors du controleur, etc..)
    Effectivement j'ai peut être vu ça sur un tuto et non sur une DOC officielle...

    Si d'autres personnes veulent également témoigner de leurs habitudes concernant la gestion de formulaires multiples, je suis preneur

  4. #4
    Membre habitué
    Homme Profil pro
    Developpeur
    Inscrit en
    juillet 2014
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Developpeur

    Informations forums :
    Inscription : juillet 2014
    Messages : 102
    Points : 153
    Points
    153
    Par défaut
    Un grand oui personnel pour cette pratique.
    Moins il y a de code dans le controller mieux c'est pour moi.

  5. #5
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    octobre 2019
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : octobre 2019
    Messages : 66
    Points : 72
    Points
    72
    Par défaut
    Je me sens moins seul grâce à vos avis

    Je posterai plus tard le code de mon Controller et de mon FormHandler pour montrer à quoi ça ressemble.
    On sait jamais, si quelqu'un veut me faire une petite revue de code gratos

    En fait j'ai une vue qui permet à l'utilisateur d'ouvrir plusieurs fenêtres modals :
    - Un modal pour uploader une vidéo locale (fichier depuis son PC)
    - Un modal pour poster une vidéo distante (URL d'une vidéo distante)
    - Un modal pour poster une vidéo youtube

    Ce qui nous fait 3 formulaires différents mais très similaires, et qui au final vont persister dans la même table Video...
    Sans parler des autres modals / formulaires qui permettent d'upload de l'image ou du PDF.

    D'où la crainte d'encombrer mon contrôleur avec tout ça !

    Bref merci à vous deux !
    Bonne soirée

  6. #6
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    octobre 2019
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : octobre 2019
    Messages : 66
    Points : 72
    Points
    72
    Par défaut
    Bonjour,

    Je me permets d'up ce topic avec une autre question concernant ce principe de traitement de formulaire dans un service au lieu d'un contrôleur...

    J'ai modifié un peu mon code dans le but que mon service "FormHandler" gère tout :
    - création du formulaire
    - traitement
    - sauvegarde de l'entité

    Du coup j'injecte certains composants dans mon service comme le FormFactory, mais aussi Router (obligé car j'appelle la méthode "generateUrl" pour assigner l'action du formulaire).
    J'appelle aussi TokenStorage car j'ai besoin d'effectuer un "getUser" au moment de persister l'entité...

    Ma question est simple, est-ce que ça reste une bonne pratique ? Ou bien ces actions devraient être réservées au contrôleur ?
    Sachant que j'ai quand même laissé la redirection dans le contrôleur.

    Voici le code pour illustrer mes propos :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
     
    class InterventionPhotoHandler
    {
        private $requestStack;
        private $form;
        private $photosDirectory;
        private $currentUser;
        private $fileUploader;
        private $imageProcessing;
        private $formFactory;
        private $router;
     
        /**
         * Initialize the handler with the form and the request
         */
        public function __construct
        (
            RequestStack $requestStack,
            EntityManagerInterface $entityManager,
            ContainerInterface $container,
            TokenStorageInterface $tokenStorageInterface,
            FileUploader $fileUploader,
            ImageProcessing $imageProcessing,
            FormFactoryInterface $formFactory,
            RouterInterface $router
        )
        {
            $this->requestStack = $requestStack;
            $this->entityManager = $entityManager;
            $this->photosDirectory = $container->getParameter('photos_directory');
            $this->currentUser = $tokenStorageInterface->getToken()->getUser();
            $this->fileUploader = $fileUploader;
            $this->imageProcessing = $imageProcessing;
            $this->formFactory = $formFactory;
            $this->router = $router;
        }
     
        public function init(Intervention $intervention)
        {
            $interventionPhoto = new InterventionPhoto();
            $interventionPhoto->setIntervention($intervention);
     
            $form = $this->formFactory->create(InterventionPhotoType::class, $interventionPhoto, [
                'action' => $this->router->generate('admin_' . $intervention->getType() . '_edit', array("intervention_id" => $intervention->getId())),
                'method' => 'POST',
                'photo_types' => $photo_types
            ]);
     
            $this->form = $form;
     
            return $this->form;
        }
     
        /**
         * Process form
         *
         * @return boolean
         */
        public function process()
        {
            $this->form->handleRequest($this->requestStack->getCurrentRequest());
     
            if ($this->form->isSubmitted() && $this->form->isValid()) {
                $this->onSuccess();
                return true;
            }
     
            return false;
        }
     
        public function setForm(Form $form)
        {
            $this->form = $form;
        }
     
        /**
         * Get form
         *
         * @return
         */
        public function getForm()
        {
            return $this->form;
        }
     
        /**
         * Persist on success
         */
        protected function onSuccess()
        {
            $interventionPhoto = $this->form->getData();
            /** @var Symfony\Component\HttpFoundation\File\UploadedFile $file */
            $file = $this->form->getData()->getFichier();
     
            $targetDirectory = $this->photosDirectory;
            $thumbTargetDirectory = $targetDirectory . '/thumbs';
     
            $fileName = $this->fileUploader->upload($file, $targetDirectory);
     
            if (!$fileName)
                return false;
     
            copy($targetDirectory . '/' . $fileName, $thumbTargetDirectory . '/' . $fileName);
            $this->imageProcessing->resizeImage($targetDirectory . '/' . $fileName, 1920);
            $this->imageProcessing->resizeImage($thumbTargetDirectory . '/' . $fileName, 250);
     
            $interventionPhoto->setNom($fileName);
            $interventionPhoto->setFichier($fileName);
            $interventionPhoto->setUser($this->currentUser);
     
            // Sauvegarde dans la base de données
            $this->entityManager->persist($interventionPhoto);
            $this->entityManager->flush();
        }
    }
    Et voici le seul code restant dans l'action de mon contrôleur qui s'en retrouve allégé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    $form = $contactHandler->init($contact);
     
            if ($contactHandler->process()) {
                // Action facultative lorsque le formulaire est envoyé...
                return $this->redirectToRoute('admin_client_contacts', array('client_id' => $contact->getClientId()));
            }
     
            return $this->render('admin/client_contact/contact.html.twig', [
                'contact' => $contact,
                'client' => $contact->getClient(),
                'form' => $form->createView()
            ]);
    Merci d'avance pour vos conseils !

  7. #7
    Membre habitué
    Homme Profil pro
    Developpeur
    Inscrit en
    juillet 2014
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Developpeur

    Informations forums :
    Inscription : juillet 2014
    Messages : 102
    Points : 153
    Points
    153
    Par défaut
    Hello,
    Pour moi c'est dans la bonne voie.
    Apres c'est peut être un peu trop spécifique, tu pourrai par exemple créer une / des classe(s) mère avec :
    - formFactory
    - form
    - requestStack

    Puis pourquoi pas un FileFormHandler qui hérite également de cette classe mère etc... (je suppose que tu vois le principe).

    Le service InterventionPhotoHandler hériterait de cette classe et tu ne complète que les dépendances manquantes. Ainsi créer d'autres formHandler serait plus générique et rapide par la suite.

    Sinon c'est comme ça que je procède également, je trouve cela plus clair mais ce n'est que mon avis.

    Sinon dans ton controlleur :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    return $this->render('admin/client_contact/contact.html.twig', [
                'contact' => $contact,
                'client' => $contact->getClient(),
                'form' => $form->createView()
            ]);
    Pourquoi envoyer client et contact alors que contact suffirait ?

  8. #8
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    octobre 2019
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : octobre 2019
    Messages : 66
    Points : 72
    Points
    72
    Par défaut
    Hey,

    Super du coup je vais créer classe parente avec de l'héritage, car effectivement je me retrouve déjà avec plusieurs FormHandler et du code un peu répété (des constructeurs ressemblants, avec les mêmes composants injectés).

    Je vais étudier la question d'une éventuelle classe FileFormHandler, là tout de suite j'ai du mal à visualiser si je vais y gagner (pour moi la classe parente avec quelques dépendances suffirait : requestStack, formFactory, etc...)
    Merci pour ces conseils, ça aide beaucoup d'avoir un avis extérieur

    Sinon pour le "client" & "contact" renvoyé par le contrôleur c'est exact, bien vu.
    Après je ne pense pas que ça change quelque chose niveau performances, à priori il n'y a pas de requête supplémentaire.

  9. #9
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    octobre 2019
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : octobre 2019
    Messages : 66
    Points : 72
    Points
    72
    Par défaut
    Par contre je reviens sur ce que j'ai dit concernant le "generateUrl" afin d'assigner l'action du formulaire dans le FormHandler lui même.

    C'est surement à éviter, si je veux réutiliser mon FormHandler dans différents contrôleurs.

  10. #10
    Membre habitué
    Homme Profil pro
    Developpeur
    Inscrit en
    juillet 2014
    Messages
    102
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Calvados (Basse Normandie)

    Informations professionnelles :
    Activité : Developpeur

    Informations forums :
    Inscription : juillet 2014
    Messages : 102
    Points : 153
    Points
    153
    Par défaut
    Oui c'est pas forcément terrible.
    Serait il possible de générer l'url d'action depuis le controlleur et de passer celle-ci au formHandler plutot que de la construire dans ce dernier ?

  11. #11
    Membre régulier
    Homme Profil pro
    Développeur Web
    Inscrit en
    octobre 2019
    Messages
    66
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : octobre 2019
    Messages : 66
    Points : 72
    Points
    72
    Par défaut
    Oui j'ai finalement opté pour cette méthode.

    Dans le controleur j'appelle le FormHandler en lui passant l'URL :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    $form = $clientHandler->init($client, $this->generateUrl('admin_client_show', ['id' => $client->getId()]));
     
    if ($clientHandler->process()) {
       $this->alerteService->editClientAlert($client, $old_client);
    }

    Et je récupère l'URL dans le FormHandler :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
        public function init(Client $client, $action)
        {
            $form = $this->formFactory->create(ClientType::class, $client, [
                'action' => $action
            ]);
     
            $this->form = $form;
     
            return $this->form;
        }
    Merci

Discussions similaires

  1. Gestion des erreurs pour runtime
    Par cbleas dans le forum Runtime
    Réponses: 1
    Dernier message: 09/12/2006, 14h18
  2. Réponses: 6
    Dernier message: 09/06/2006, 12h17
  3. Question des gestions des genres pour livre, BD...
    Par Oberown dans le forum Schéma
    Réponses: 3
    Dernier message: 16/09/2004, 16h58
  4. Gestion des modifications pour un enregistrement
    Par Pascal Jankowski dans le forum Bases de données
    Réponses: 3
    Dernier message: 10/03/2004, 14h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo