faire persister les elements avec le collectionType

Default
,

Bonjour,
Je suis débutant en symfony pourtant j'aime bien ce framework.
j'aimerais rajouter des elements à partir d'un formulaire imbriqué.je veux ajouter des photos(entité Photo) dans mon formulaire ajouter une chaussure (de l'entité ModeleChaussure) à partir d'un formulaire imbriqué

Voila je rencontre un petit problème avec mon code.
le code m'a lair normal

Ce que je fais

Décrivez ici votre code ou ce que vous cherchez à faire

```Entyté ModeleChaussure:
<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use http\Env\Url;

/**

  • @ORM\Entity(repositoryClass="App\Repository\ModeleChaussureRepository")
    /
    class ModeleChaussure
    {
    /
    *

    • @ORM\Id()
    • @ORM\GeneratedValue()
    • @ORM\Column(type="integer") */ private $id;

    /**

    • @ORM\Column(type="string", length=100) */ private $nom;

    /**

    • @ORM\Column(type="decimal", precision=10, scale=2) */ private $prix;

    /**

    • @ORM\OneToMany(targetEntity="App\Entity\Commentaire", mappedBy="modele") */ private $commentaires;

    /**

    • @ORM\ManyToMany(targetEntity="App\Entity\Commande", inversedBy="modeleChaussures") */ private $commandes;

    /**

    • @ORM\ManyToMany(targetEntity="App\Entity\Taille", inversedBy="modeleChaussures") */ private $tailles;

    /**

    • @ORM\OneToMany(targetEntity="App\Entity\Promotion", mappedBy="modeleChaussure") */ private $promotions;

    /**

    • @ORM\ManyToOne(targetEntity="App\Entity\Marque", inversedBy="modeleChaussures")
    • @ORM\JoinColumn(nullable=false) */ private $marque;

    /**

    • @ORM\OneToMany(targetEntity="App\Entity\Photo",cascade={"persist"}, mappedBy="modeleChaussure") */ private $photo;

    /**

    • @var photo */ private $photos;

    /**

    • @ORM\Column(type="string", length=255, nullable=true) */ private $description;

    /**

    • @ORM\Column(type="string", length=255) */ private $coverImage;

    public function __construct()
    {
    $this->commentaires = new ArrayCollection();
    $this->commandes = new ArrayCollection();
    $this->tailles = new ArrayCollection();
    $this->promotions = new ArrayCollection();
    $this->photos = new ArrayCollection();
    }

    public function getId(): ?int
    {
    return $this->id;
    }

    public function getNom(): ?string
    {
    return $this->nom;
    }

    public function setNom(string $nom): self
    {
    $this->nom = $nom;

    return $this;
    

    }

    public function getPrix(): ?string
    {
    return $this->prix;
    }

    public function setPrix(string $prix): self
    {
    $this->prix = $prix;

    return $this;
    

    }

    /**

    • @return Collection|Commentaire[] */ public function getCommentaires(): Collection { return $this->commentaires; }

    public function addCommentaire(Commentaire $commentaire): self
    {
    if (!$this->commentaires->contains($commentaire)) {
    $this->commentaires[] = $commentaire;
    $commentaire->setModele($this);
    }

    return $this;
    

    }

    public function removeCommentaire(Commentaire $commentaire): self
    {
    if ($this->commentaires->contains($commentaire)) {
    $this->commentaires->removeElement($commentaire);
    // set the owning side to null (unless already changed)
    if ($commentaire->getModele() === $this) {
    $commentaire->setModele(null);
    }
    }

    return $this;
    

    }

    /**

    • @return Collection|Commande[] */ public function getCommandes(): Collection { return $this->commandes; }

    public function addCommande(Commande $commande): self
    {
    if (!$this->commandes->contains($commande)) {
    $this->commandes[] = $commande;
    }

    return $this;
    

    }

    public function removeCommande(Commande $commande): self
    {
    if ($this->commandes->contains($commande)) {
    $this->commandes->removeElement($commande);
    }

    return $this;
    

    }

    /**

    • @return Collection|Taille[] */ public function getTailles(): Collection { return $this->tailles; }

    public function addTaille(Taille $taille): self
    {
    if (!$this->tailles->contains($taille)) {
    $this->tailles[] = $taille;
    }

    return $this;
    

    }

    public function removeTaille(Taille $taille): self
    {
    if ($this->tailles->contains($taille)) {
    $this->tailles->removeElement($taille);
    }

    return $this;
    

    }

    /**

    • @return Collection|Promotion[] */ public function getPromotions(): Collection { return $this->promotions; }

    public function addPromotion(Promotion $promotion): self
    {
    if (!$this->promotions->contains($promotion)) {
    $this->promotions[] = $promotion;
    $promotion->setModeleChaussure($this);
    }

    return $this;
    

    }

    public function removePromotion(Promotion $promotion): self
    {
    if ($this->promotions->contains($promotion)) {
    $this->promotions->removeElement($promotion);
    // set the owning side to null (unless already changed)
    if ($promotion->getModeleChaussure() === $this) {
    $promotion->setModeleChaussure(null);
    }
    }

    return $this;
    

    }

    public function getMarque(): ?Marque
    {
    return $this->marque;
    }

    public function setMarque(?Marque $marque): self
    {
    $this->marque = $marque;

    return $this;
    

    }

    public function __toString()
    {
    // TODO: Implement __toString() method.
    return $this->nom;
    }

    /**

    • @return Collection|Photo[] */ public function getPhotos(): Collection { return $this->photos; }

    public function addPhoto(Photo $photo): self
    {
    if (!$this->photos->contains($photo)) {
    $this->photos[] = $photo;
    $this->photos->add($photo);
    $photo->setModeleChaussure($this);
    }

    return $this;
    

    }

    public function removePhoto(Photo $photo): self
    {
    if ($this->photos->contains($photo)) {
    $this->photos->removeElement($photo);
    // set the owning side to null (unless already changed)
    if ($photo->getModeleChaussure() === $this) {
    $photo->setModeleChaussure(null);
    }
    }

    return $this;
    

    }

    public function setPhoto(photo $photo): self
    {
    $this->$photo;
    return $this;
    }

    public function getPhoto(): ?photo
    {
    return $this->photo;
    }

    public function getDescription(): ?string
    {
    return $this->description;
    }

    public function setDescription(?string $description): self
    {
    $this->description = $description;

    return $this;
    

    }

    public function getCoverImage(): ?string
    {
    return $this->coverImage;
    }

    public function setCoverImage(string $coverImage): self
    {
    $this->coverImage = $coverImage;

    return $this;
    

    }

    Entité Photo:

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**

  • @ORM\Entity(repositoryClass="App\Repository\PhotoRepository")
    /
    class Photo
    {
    /
    *

    • @ORM\Id()
    • @ORM\GeneratedValue()
    • @ORM\Column(type="integer") */ private $id;

    /**

    • @ORM\Column(type="string", length=255) */ private $url;

    /**

    • @ORM\ManyToOne(targetEntity="App\Entity\ModeleChaussure", cascade={"persist"} ,inversedBy="photos")
    • @ORM\JoinColumn(nullable=false) */ private $modeleChaussure; private $photo;

    public function __construct()
    {

    $this->modeleChaussure = new ArrayCollection();
    

    }

    public function getId(): ?int
    {
    return $this->id;
    }

    public function getUrl(): ?string
    {
    return $this->url;
    }

    /**

    • @return Collection|ModeleChaussure[] */ public function getModeleChaussure(): Collection { return $this->modeleChaussure; }

    public function add(ModeleChaussure $modeleChaussure): self
    {
    if (!$this->modeleChaussure->contains($modeleChaussure)) {
    $this->modeleChaussure[] = $modeleChaussure;
    $modeleChaussure->setPhoto($this);
    }

    return $this;
    

    }

    public function remove(ModeleChaussure $modeleChaussure): self
    {
    if ($this->modeleChaussure->contains($modeleChaussure)) {
    $this->modeleChaussure->removeElement($modeleChaussure);
    // set the owning side to null (unless already changed)
    if ($modeleChaussure->getPhoto() === $this) {
    $modeleChaussure->setPhoto(null);
    }
    }

    return $this;
    

    }

    public function setModeleChaussure(?ModeleChaussure $modeleChaussure): self
    {
    $this->modeleChaussure = $modeleChaussure;

    return $this;
    

    }

    public function setUrl(string $url)
    {
    $this->url;
    return $this;
    }

}

ModeleChaussureType:
<?php

namespace App\Form;

use App\Entity\Marque;
use App\Entity\ModeleChaussure;
use App\Entity\Taille;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\MoneyType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ModeleChaussureType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nom', TextType::class)
->add('prix',MoneyType::class)
->add('description', TextareaType::class)
->add('coverImage',FileType::class,array('label'=>'importez une image de couverture'

        ))
        ->add('tailles', EntityType::class, [
            'class' => Taille::class,
            'choice_label' => 'taille',
            'multiple' => true
        ])
        ->add('marque',EntityType::class,[
            'class'=>Marque::class,
            'choice_label'=>'nom'
        ])

       ->add(
            'photos',
            CollectionType::class, [
                'entry_type' => PhotoType::class,
                'allow_add'=>true,
           'entry_options' => ['label' => false],
           'prototype' => true,
           'by_reference' => false,
       ])


        ->add('Ajouter une nouvelle chaussure',SubmitType::class,[
            'attr'=>[
                'class'=>'btn btn-success'
            ]
        ])

    ;
}

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

PhotoType:
<?php

namespace App\Form;

use App\Entity\Photo;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PhotoType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('url',FileType::class,array('label'=>'ajouter une autre image'))
;
}

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

}

controller contenant la fonction pour créer un modele chaussure:

<?php

namespace App\Controller;

use App\Entity\ModeleChaussure;
use App\Form\ModeleChaussureType;
use App\Repository\ClientRepository;
use App\Repository\MarqueRepository;
use App\Repository\ModeleChaussureRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\File\Exception\FileException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

class DetailChaussureController extends AbstractController
{
/**
* @var ClientRepository
/
private $marqueRepository;
private $clientRepository;
function __construct(MarqueRepository $marqueRepository,ClientRepository $clientRepository)
{
$this->marqueRepository = $marqueRepository;
$this->clientRepository=$clientRepository;
}
/
*
* @Route("/detail/chaussure/{id}", name="detail_chaussure")
*/
public function index(ModeleChaussureRepository $repository, int $id)
{
$list = $this->marqueRepository->findAll();
$chaussure = $repository->find($id);
return $this->render('detail_chaussure/index.html.twig', [
'chaussure' => $chaussure,'list'=>$list
]);
}

/**
 * @Route("/chaussure/new", name="chaussure_nouveau")
 * @param Request $request
 * @param EntityManagerInterface $manager
 * @return \Symfony\Component\HttpFoundation\Response
 */
public function create(Request $request)
{

    $chaussure = new ModeleChaussure();
    $manager=$this->getDoctrine()->getManager();
    $form = $this->createForm(ModeleChaussureType::class, $chaussure);
    $form->handleRequest($request);


    if($form->isSubmitted() && $form->isValid()) {
        $manager=$this->getDoctrine()->getManager();

   foreach($chaussure->getPhotos() as $photo)
    {
        $photo->setModeleChaussure($chaussure);
        $manager->persist($photo);

    }


   $file = $form->get('coverImage')->getData();
        $fileName = $this->generateUniqueFileName().'.'.$file->guessExtension();
        try{
            $file->move(
                $this->getParameter('coverImage_directory'),
                $fileName
            );
        }
        catch (fileException $e){

        }

      $manager=$this->getDoctrine()->getManager();

        $chaussure->setCoverImage($fileName);
        $manager->persist($chaussure);
        $manager->flush();

        $this->addFlash('success',
            "Bravo le modele<strong class='text-danger'>{$chaussure->getNom()}</strong> est bien ajouté");
        return $this->redirectToRoute('home');
    }
    $list = $this->marqueRepository->findAll();

    return $this->render('detail_chaussure/nouvelle_chaussure.html.twig', ['form'=>$form->createView(),
        'list'=>$list
    ]);
}

private function generateUniqueFileName()
{

    return md5(uniqid());
}

la vue Twig permettant d'afficher le formulaire d'ajout chaussure:

{% extends 'base.html.twig' %}

{% block title %}creation d'une nouvelle chaussure!{% endblock %}

{% form_theme form _self %}

{% block body%}


Créer une nouvelle chaussure


{{ form_start(form) }}
{{ form_row(form.nom, {'label': 'Nom','attr': {'placeholder': 'nom de la chaussure...'}}) }}
{{ form_row(form.prix, {'label': 'Prix','attr': {'placeholder': 'prix de la chaussure...'}}) }}
{{ form_row(form.description, {'label': 'description', 'attr': {'placeholder': 'ajouter une description...'}}) }}
    {{ form_row(form.tailles, {'label': 'la taille', 'attr': {'placeholder': 'selectionnez une taille...'}}) }}
    {{ form_row(form.marque, {'label': 'Sélectionnez la marque', 'attr': {'placeholder' : 'Votre marque...'}})}}

    <div class="form-group">
        <button type="button" id="add-photo" class="btn btn-primary">Ajouter une image</button>
    </div>

    {% block _modeleChaussure_photos_widget %}

Ici,vous pouvez ajouter vos propres photos!

{{ form_widget(form) }}

{{ form_end(form) }}
{% endblock %}

{% endblock %}

 {% block _chaussure_entry_widget %}
 <div class="form-group" id="block_{{id}}">
     <div class="row">
         <div class="col-10">
             <div class="row">
                 <div class="col">
                     {{ form_widget(form.url) }}
                 </div>

             </div>
         </div>
         <div class="col-2">
             <button type="button" class="btn-btn-danger">supprimer</button>
         </div>
     </div>

 </div>
 {% endblock %}

    {% block javascripts %}

        <script>

            $('#add-photo').click(function()
            {
                const index = $('#modele_chaussure_photos div.form-group');
                const tmpl= $('#modele_chaussure_photos').data('prototype').replace(/_name_/g, index);

              $('#modele_chaussure_photos').append(tmpl);
            });
        </script>
    {% endblock %}

Entourez votre code pour bien le mettre en forme


### Ce que je veux

Décrivez ici ce que vous cherchez à obtenir
la nouvelle chaussure s'ajoute mais pas  les photos donc ils n'apparaissent pas dans la base de donnée
### Ce que j'obtiens

Décrivez ici vos erreurs ou ce que vous obtenez à la place de ce que vous attendez :(
on ne m'affiche aucun messaged'erreur.
s'il vous plait j'ai besoin de votre aide .ça fait une semaine que je galère dessus.
Merci d'avance