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 :

Merge de CollectionType avec une collection existante


Sujet :

Symfony PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Responsable sécurité
    Inscrit en
    Novembre 2013
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Responsable sécurité

    Informations forums :
    Inscription : Novembre 2013
    Messages : 5
    Par défaut Merge de CollectionType avec une collection existante
    Bonjour,

    Je sollicite votre aide pour la gestion des CollectionType. Pour rendre ma question plus accessible, je vais la translater sur le modèle de la documentation officielle, avec les Tâches (Tasks), et les Tags.

    Ce que je souhaite :

    une tâche disposerait déjà de tags assignés
    je veux soumettre une nouvelle collection de tags avec une certaine valeur
    si les tags soumis sont présents, je veux mettre leur valeur à jour
    si les tags soumis ne sont pas présents, je veux les ajouter tels que soumis
    les tags existants non soumis doivent rester en place
    Mon problème :

    tous les tags sont systématiquement écrasés par ceux soumis, y-compris avant le handleRequest du form.
    je ne peux donc même pas faire l'analyse moi-même entre le repository et ce qui vient du formulaire, j'arrive trop tard dans le contrôleur
    Au niveau des entités, j'ai une relation ManyToMany avec un attribut supplémentaire nommé value (donc en réalité deux relations OneToMany).

    Entity "Task"

    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
    class Task
    {
        #[ORM\Id]
        #[ORM\GeneratedValue]
        #[ORM\Column]
        private ?int $id = null;
     
        #[ORM\OneToMany(mappedBy: 'task', targetEntity: TaskTags::class, orphanRemoval: false, cascade: ['persist'])]
        private Collection $TaskTags;
     
         /**
         * @return Collection<int, TaskTags>
         */
        public function getTaskTags(): Collection
        {
            return $this->TaskTags;
        }
     
        public function addTaskTag(TaskTags $TaskTag): self
        {
            // J'ai volontairement supprimé la condition d'équivalence à des fins de test
            $this->TaskTags->add($TaskTag);
            $TaskTag->setTask($this);
            return $this;
        }
     
        public function removeTaskTag(TaskTags $TaskTag): self
        {
            if ($this->TaskTags->removeElement($TaskTag)) {
                // set the owning side to null (unless already changed)
                if ($TaskTag->getTask() === $this) {
                    $TaskTag->setTask(null);
                }
            }
            return $this;
        }
    }
    Entity "Tag"

    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
    class Tag
    {
        #[ORM\Id]
        #[ORM\GeneratedValue]
        #[ORM\Column]
        private ?int $id = null;
     
        #[ORM\OneToMany(mappedBy: 'tag', targetEntity: TaskTags::class, orphanRemoval: false)]
        private Collection $TaskTags;
     
        /**
         * @return Collection<int, TaskTags>
         */
        public function getTaskTags(): Collection
        {
            return $this->TaskTags;
        }
     
        public function addTaskTag(TaskTags $TaskTag): self
        {
            // J'ai volontairement supprimé la condition d'équivalence à des fins de test
            $this->TaskTags->add($TaskTag);
            $TaskTag->setTag($this);
            return $this;
        }
     
        public function removeTaskTag(TaskTags $TaskTag): self
        {
            if ($this->TaskTags->removeElement($TaskTag)) {
                // set the owning side to null (unless already changed)
                if ($TaskTag->getTag() === $this) {
                    $TaskTag->setTag(null);
                }
            }
            return $this;
        }
     
    }
    Entity "TaskTags"

    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
    class TaskTags
    {
        #[ORM\Id]
        #[ORM\ManyToOne(inversedBy: 'TaskTags')]
        #[ORM\JoinColumn(nullable: false)]
        private Task $task;
     
        #[ORM\Id]
        #[ORM\ManyToOne(inversedBy: 'TaskTags')]
        #[ORM\JoinColumn(nullable: false)]
        private Tag $tag;
     
        // Mon fameux champ additionnel
        #[ORM\Column(nullable: true)]
        private ?int $value = null;
     
            public function getTask(): ?Task
        {
            return $this->task;
        }
     
        public function setTask(?Task $task): self
        {
            if(null !== $task) {
                $this->task = $task;
            }
            return $this;
        }
     
        public function getTag(): ?Tag
        {
            return $this->tag;
        }
     
        public function setTag(?Tag $tag): self
        {
            if(null !== $tag) {
                $this->tag = $tag;
            }
            return $this;
        }
     
        public function getValue(): ?string
        {
            return $this->value;
        }
     
        public function setValue(?string $value): self
        {
            $this->value = $value;
     
            return $this;
        }
     
    }
    FormType "TaskFormType
    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
     
    class TaskFormType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options): void
        {
            $builder
                ...
                ->add('TaskTags', CollectionType::class, [
                    'by_reference' => false,
                    'entry_type' => TaskTagsFormType::class,
                    'entry_options' => ['label' => false],
                    'allow_add' => true,
                    'allow_delete' => true,
                    'prototype' => true,
                ]);
        }
     
        public function configureOptions(OptionsResolver $resolver): void
        {
            $resolver->setDefaults([
                'data_class' => Task::class,
                'csrf_protection' => false
            ]);
        }
    }
    FormType "TaskTagsFormType

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class TaskTagsFormType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options): void
        {
            $builder
                ->add('task')
                ->add('tag')
                ->add('value')
            ;
        }
    Contrôleur

    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
     #[Route('/tasks/edit/{id}/tags', name: 'app_edit_task')]
        public function editasktags(Request $request, EntityManagerInterface $em, TaskTagsRepository $TaskTagsRepo): Response
        {
        ...
            // Create an ArrayCollection of the current tags assigned to the task
     
            $task = $this->getTask();
     
            // quand on affiche le formulaire (GET), cette collection reflète bien tous les tags assignés à la tâche
            // quand on soumet le formulaire, ça devient immédiatement les tags soumis depuis le formulaire
            $ExistingTaskTags = $TaskTagsRepo->findByTask($task);
     
            $form = $this->createForm(TaskFormType::class, $task);
     
            $form->handleRequest($request);
     
            if ($form->isSubmitted() && $form->isValid()) {
                // c'est là que j'ai tout essayé ... sauf que comme je ne parviens plus à avoir la collection d'origine, je suis perdu
                $task = $form->getData();
     
                $SubmittedTaskTags = $userForm->getTaskTags();
                $CalculatedTaskTags = new ArrayCollection();
                foreach ($ExistingTaskTags as $ExistingTaskTag) {
                    foreach ($SubmittedTaskTags as $SubmittedTaskTag) {
                        if ($ExistingTaskTag->getTag()->getId() !== $SubmittedTaskTag->getTag()->getId()) {
                            // The existing tag is not the same as submitted, keeping it as it in a new collection
                            $CalculatedTaskTags->add($ExistingTaskTag);
                        } else {
                            // The submitted tag is equal to the one in DB, so adding the submitted one
                            $SubmittedTaskTag->setTask($task);
                            $CalculatedTaskTags->add($SubmittedTaskTag);
                        }
                    }
                }
                $em->persist($task);
                $em->flush();
            }
            return $this->render('task/edittasktags.twig.html', [
                'form' => $form,
                'task' => $this->getTask()
            ]);
        }
    Mon problème principal réside dans l'incapacité, une fois le formulaire soumis, d'accéder à la collection existante, pour faire un "merge".
    Si je fais un dump du repo, les éléments affichés sont ceux soumis dans le formulaire, et c'est tout.

    J'ai essayé beaucoup de choses...
    Il y en a une que je n'ai pas faîte, et c'est volontaire : passer les éléments existants en "hidden" et les re soumettre.
    Je n'aime pas du tout cette pratique, car elle peut être piégeuse avec du multi onglet (on soumettrait des valeurs chargées précédemment, et possiblement modifiées entre temps).

    D'avance merci pour votre aide pour ce sujet qui n'est pas simple, j'en conviens !

    NB : j'ai volontairement réécrit tout le code en mode "exemple", ça ne correspond pas à la réalité de mes entités.

  2. #2
    Nouveau membre du Club
    Homme Profil pro
    Responsable sécurité
    Inscrit en
    Novembre 2013
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Responsable sécurité

    Informations forums :
    Inscription : Novembre 2013
    Messages : 5
    Par défaut
    Solution trouvée.
    J'ai ajouté 'mapped' => false dans le FormType

    J'ai été en mesure de récupérer les tags soumis avec $SubmittedTags = $form->get('TaskTags')->getData();

    Le repository n'est plus écrasé.
    En espérant que ça puisse en aider d'autres.

Discussions similaires

  1. Réponses: 1
    Dernier message: 28/03/2009, 17h28
  2. Lier un DataTable avec une collection typee
    Par osteinme dans le forum C#
    Réponses: 2
    Dernier message: 29/01/2009, 22h33
  3. un For Each qui ne fonctionne pas avec une collection maison !
    Par grenouillesiverte dans le forum Windows Forms
    Réponses: 7
    Dernier message: 01/09/2007, 21h00
  4. Dataset interfacé avec une Collection d'objets
    Par boulo dans le forum Général Dotnet
    Réponses: 1
    Dernier message: 16/03/2007, 10h55
  5. template et utilisation avec une classe existante
    Par vartav dans le forum Langage
    Réponses: 6
    Dernier message: 14/03/2007, 10h39

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