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 :

Entités Many-to-Many avec attributs lors de l'INSERT [4.x]


Sujet :

Symfony PHP

  1. #1
    Candidat au Club
    Homme Profil pro
    Webmaster
    Inscrit en
    Janvier 2020
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Webmaster

    Informations forums :
    Inscription : Janvier 2020
    Messages : 2
    Points : 2
    Points
    2
    Par défaut Entités Many-to-Many avec attributs lors de l'INSERT
    Bonjour à tous !

    Pour un petit projet personnel, je suis en train de créer un site web pour des dégustations de whisky. J'ai décidé d'utiliser Symfony pour son coté "boite à outil complète, il reste plus qu'à construire toi la maison !".
    J'ai suivi quelques tuto, des vidéos, etc. mais j'arrive à un problème que mes connaissances n'arrivent pas à résoudre. C'est certainement plus le concept général qu'une erreur de code, donc je m'excuse d'avance pour ça !

    J'ai une relation Many to Many bidirectionnelle avec attributs. Dégustation -> DegustationWhiskys -> Whisky. Logiquement, une dégustation a plusieurs whisky et les whiskys peuvent être présent dans plusieurs dégustation. L'entité du milieu me permet de définir l'ordre de dégustation, et éventuellement un petit commentaire.
    Mon entité Degustation :
    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
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    <?php
     
    namespace App\Entity;
     
    use App\Entity\DW;
    use App\Entity\DWU;
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\Common\Collections\Collection;
    use Doctrine\ORM\Mapping as ORM;
    use Symfony\Component\HttpFoundation\File\File;
    use Vich\UploaderBundle\Mapping\Annotation as Vich;
     
    /**
     * @ORM\Entity(repositoryClass="App\Repository\DegustationRepository")
     * @ORM\HasLifecycleCallbacks()
     * @Vich\Uploadable
     */
    class Degustation
    {
        /**
         * @ORM\Id()
         * @ORM\GeneratedValue()
         * @ORM\Column(type="integer")
         */
        private $id;
     
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $Name;
     
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $Location;
     
        /**
         * @ORM\Column(type="date", nullable=true)
         */
        private $Date;
     
        /**
         * @ORM\Column(type="text", nullable=true)
         */
        private $ShortDescription;
     
        /**
         * @Vich\UploadableField(mapping="degustations", fileNameProperty="imageName")
         * @var File
         */
        private $imageFile;
     
        /**
         * @ORM\Column(type="string", length=255, nullable=true)
         */
        private $imageName;
     
        /**
         * @ORM\OneToMany(targetEntity="App\Entity\DW", mappedBy="degustation", cascade={"persist"})
         */
        private $DWs;
     
        /**
         * @ORM\Column(type="datetime", nullable=true)
         */
        private $UpdatedAt;
     
        /**
         * @ORM\Column(type="datetime")
         */
        private $CreatedAt;
     
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $CreatedBy;
     
        public function getId(): ?int
        {
            return $this->id;
        }
     
        public function getName(): ?string
        {
            return $this->Name;
        }
     
        public function setName(?string $Name): self
        {
            $this->Name = $Name;
     
            return $this;
        }
     
        public function getLocation(): ?string
        {
            return $this->Location;
        }
     
        public function setLocation(?string $Location): self
        {
            $this->Location = $Location;
     
            return $this;
        }
     
        public function getDate(): ?\DateTimeInterface
        {
            return $this->Date;
        }
     
        public function setDate(?\DateTimeInterface $Date): self
        {
            $this->Date = $Date;
     
            return $this;
        }
     
        public function getShortDescription(): ?string
        {
            return $this->ShortDescription;
        }
     
        public function setShortDescription(?string $ShortDescription): self
        {
            $this->ShortDescription = $ShortDescription;
     
            return $this;
        }
     
        public function getImageName(): ?string
        {
            return $this->imageName;
        }
     
        public function setImageName(?string $imageName): self
        {
            $this->imageName = $imageName;
     
            return $this;
        }
     
     
        /**
         *
         * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $imageFile
         */
        public function setImageFile(?File $imageFile = null): void
        {
            $this->imageFile = $imageFile;
     
            if (null !== $imageFile) {
                $this->updatedAt = new \DateTimeImmutable();
            }
        }
     
        public function getImageFile(): ?File
        {
            return $this->imageFile;
        }
     
        public function __construct()
        {
            $this->DWs = new ArrayCollection();
        }
     
        /**
         * @return Collection|DW[]
         */
        public function getDWs(): Collection
        {
            return $this->DWs;
        }
     
        public function addDW(DW $dW): self
        {
            if (!$this->DWs->contains($dW)) {
                $this->DWs[] = $dW;
                $dW->setDegustation($this);
            }
     
            return $this;
        }
     
        public function removeDW(DW $dW): self
        {
            if ($this->DWs->contains($dW)) {
                $this->DWs->removeElement($dW);
                // set the owning side to null (unless already changed)
                if ($dW->getDegustation() === $this) {
                    $dW->setDegustation(null);
                }
            }
     
            return $this;
        }
     
        public function getUpdatedAt(): ?\DateTimeInterface
        {
            return $this->UpdatedAt;
        }
     
        public function setUpdatedAt(?\DateTimeInterface $UpdatedAt): self
        {
            $this->UpdatedAt = $UpdatedAt;
     
            return $this;
        }
     
        public function getCreatedAt(): ?\DateTimeInterface
        {
            return $this->CreatedAt;
        }
     
        public function setCreatedAt(\DateTimeInterface $CreatedAt): self
        {
            $this->CreatedAt = $CreatedAt;
     
            return $this;
        }
     
        public function getCreatedBy(): ?string
        {
            return $this->CreatedBy;
        }
     
        public function setCreatedBy(string $CreatedBy): self
        {
            $this->CreatedBy = $CreatedBy;
     
            return $this;
        }
        /**
         * @ORM\PreUpdate
         */
        public function updateDate()
        {
            $this->setUpdatedAt(new \Datetime());
        }
     
     
    }
    Mon entité DegustationWhisky :
    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
     
    <?php
     
    namespace App\Entity;
     
    use Doctrine\ORM\Mapping as ORM;
     
    /**
     * @ORM\Entity(repositoryClass="App\Repository\DWRepository")
     */
    class DW
    {
        /**
         * @ORM\Id()
         * @ORM\GeneratedValue()
         * @ORM\Column(type="integer")
         */
        private $id;
     
        /**
         * @ORM\Column(type="integer")
         */
        private $placeOrder;
     
        /**
         * @ORM\Column(type="text", nullable=true)
         */
        private $description;
     
        /**
         * @ORM\ManyToOne(targetEntity="App\Entity\Whisky")
         * @ORM\JoinColumn(nullable=false)
         */
        private $Whisky;
     
        /**
         * @ORM\ManyToOne(targetEntity="App\Entity\Degustation", inversedBy="DWs", cascade={"persist"})
         * @ORM\JoinColumn(nullable=false)
         */
        private $degustation;
     
        public function getId(): ?int
        {
            return $this->id;
        }
     
        public function getPlaceOrder(): ?int
        {
            return $this->placeOrder;
        }
     
        public function setPlaceOrder(int $placeOrder): self
        {
            $this->placeOrder = $placeOrder;
     
            return $this;
        }
     
        public function getDescription(): ?string
        {
            return $this->description;
        }
     
        public function setDescription(?string $description): self
        {
            $this->description = $description;
     
            return $this;
        }
     
        public function getDegustation(): ?Degustation
        {
            return $this->Degustation;
        }
     
        public function setDegustation(?Degustation $Degustation): self
        {
            $this->Degustation = $Degustation;
     
            return $this;
        }
     
        public function getWhisky(): ?Whisky
        {
            return $this->Whisky;
        }
     
        public function setWhisky(?Whisky $Whisky): self
        {
            $this->Whisky = $Whisky;
     
            return $this;
        }
     
        public function __toString()
        {
            return $this->Whisky->getName();
        }
    }
    Toujours logiquement, l'entité DegustationWhisky est la propriétaire des deux relations, vu qu'elle est en sandwich dans une One-to-many-to-One. Jusqu'à la pas de problème.

    Comme le nombre de whisky par dégustation est fixe, j'instancie directement mes entités du milieu lors de sa création.

    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
    public function new(Request $request): Response
        {
            $degustation = new Degustation();
            $degustation->setCreatedAt(new \Datetime());
            $degustation->setCreatedBy($this->getUser()->getFullName());
            for ($x=0;$x<=4;$x++){
                $dw = new DW;
                $dw->setPlaceOrder($x+1);
                $degustation->addDW($dw);
            }
            $form = $this->createForm(DegustationType::class, $degustation);
            $form->handleRequest($request);
     
            if ($form->isSubmitted() && $form->isValid()) {
                $entityManager = $this->getDoctrine()->getManager();
                $entityManager->persist($degustation);
                $entityManager->flush();
     
                return $this->redirectToRoute('degustation_index');
            }
    J'instancie mon entité $degustation, renseigne les champs "créer par" et "créer à", puis une petite boucle for pour créer 5 entités DegustationWhisky, habilement raccourci par le nom DW.
    Mon formulaire, générer avec le bundle maker, est comme suit :
    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
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            /* This form is the base form to hydrate Degustation.
            The 5 DWs is fixed and is created directly in the class when its instancied. */
            $builder
                ->add('Name')
                ->add('Location')
                ->add('Date')
                /* The image is persisted through the use of Vich Bundle */
                ->add('imageFile',VichImageType::class,[
                    'required'=>false,
                    'allow_delete'=>true,
                    'download_uri'=>true,
                    'image_uri'=>true,
                ])
                /* the decription of each degustation is a HTML text in the DB, using CKEditor */
                ->add('ShortDescription',CKEditorType::class,array(
                    'config'=>array(
                        'uiColor'=>'#ffffff'
                    ),
                ))
                /* Here we add the user that will attend to the degustation. This field is not mapped, it will be used in the base degustation class to create DWUs */
                ->add('user',EntityType::class,[
                    'class'=>User::class,
                    'label'=>false,
                    'choice_label'=>'fullName',
                    'multiple'=>true,
                    'mapped'=>false
                ])
                /* Each degustation has a fixed number of DW (5) and it's set in the class */
                ->add('DWs',CollectionType::class,[
                    'entry_type'=>DWType::class,
                    'by_reference'=>false,
                    'prototype'=>true,
                ])
            ;
        }
    La aussi, rien de magique me semble-t-il, J'utilise le collectionType pour mes 5 entités DegustationWhisky (DWs). Selon les tuto, je renseigne le champ 'by_reference' à false afin que cela soit mis à jours comme il se doit.

    Je créé ma vue sans problème, les 5 DW sont la, avec les bons champs. Je renseigne tout selon ce que j'ai besoin, et au moment de cliquer sur "Save", j'ai l'erreur suivante qui apparaît :

    An exception occurred while executing 'INSERT INTO dw (place_order, description, whisky_id, degustation_id) VALUES (?, ?, ?, ?)' with params [1, null, 1, null]:

    SQLSTATE[23000]: Integrity constraint violation: 1048 Le champ 'degustation_id' ne peut être vide (null)
    L'erreur est cristalline : je tente de persister un champ qui est marqué comme obligatoire, refus net et rollback. Le champ obligatoire qui est vide est l'ID de ma dégustation. L'outil de débug me montre que le "INSERT INTO" de ma dégustation est bien avant celui de DegustationWhisky, ce qui fait du sens: il faut la persister en premier pour générer un ID. Mais comment l'ajouter à mes DegustationWhisky ?
    Or mon problème est là. Je supposais que le cascade:persist dans mon entité prendrait soin de ce genre de détail. Je pourrais sans problème faire un gros détour pour contourner ce problème (par exemple récupérer l'ID de la dégustation après le ->flush()), mais cela me semble tellement peu élégant que je suis persuadé qu'il doit y avoir un moyen de faire ça directement, surtout que tout le monde dit de mettre le moins de code dans son contrôleur. Help !

    Merci d'avance et désolé du long message !

  2. #2
    Candidat au Club
    Homme Profil pro
    Webmaster
    Inscrit en
    Janvier 2020
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Webmaster

    Informations forums :
    Inscription : Janvier 2020
    Messages : 2
    Points : 2
    Points
    2
    Par défaut
    Bonjour,

    Mon problème a été résolu; j'ai recréer de toute part ma relation many-to-one et la seule chose qui change par rapport à mon ancienne entité c'est l'ordre dans lequel apparait le cascade={"persist"}, qui est maintenant avant orphanRemove.

    Je pense que ce n'est pas la cause exact du problème: à mon avis il s'agit une bête erreur de majuscule ou de nom, je vois pas quoi d'autre. Je marque donc le sujet comme résolu !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Persistance: Many To Many avec attributs
    Par EtudiantJ2EE dans le forum Doctrine2
    Réponses: 3
    Dernier message: 16/04/2013, 14h36
  2. Relation many-to-many avec attribut
    Par Legenyes dans le forum Doctrine2
    Réponses: 2
    Dernier message: 06/08/2012, 13h05
  3. Réponses: 0
    Dernier message: 30/05/2012, 21h05
  4. [sqlalchemy] Création d'une relation Many to Many avec attributs
    Par joubu dans le forum Bibliothèques tierces
    Réponses: 6
    Dernier message: 05/08/2010, 20h13
  5. Réponses: 3
    Dernier message: 01/03/2007, 14h51

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