ArrayCollection imbriqué dans un autre ArrayCollection

by Stéphane Hortet   Last Updated March 19, 2019 17:26 PM

Voila je rencontre un petit problème avec mon code.

J'ai un formulaire de prestataires dans lequel je souhaite intégrer une collection de prestations et pour chaque prestation y intégrer une collection de expériences. J'ai donc un formulaire avec un ArrayColllection qui lui contient un autre ArrayCollection.

Ce que j'ai fait

J'ai donc créé les entités Prestataire, Prestation et Expérience :

<?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\PrestataireRepository")
 */
class Prestataire
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Prestation", mappedBy="prestataire", cascade={"persist"}, orphanRemoval=true)
     */
    private $prestations;

    public function __construct()
    {
        $this->caterings = new ArrayCollection();
    }

    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;
    }

    /**
     * @return Collection|Prestation[]
     */
    public function getPrestations(): Collection
    {
        return $this->prestations;
    }

    public function addPrestation(Prestation $prestation): self
    {
        if (!$this->prestations->contains($prestation)) {
            $this->prestations[] = $prestation;
            $prestation->setPrestataire($this);
        }
        return $this;
    }

    public function removePrestation(Prestation $prestation): self
    {
        if ($this->prestations->contains($prestation)) {
            $this->prestations->removeElement($prestation);
            // set the owning side to null (unless already changed)
            if ($prestation->getPrestataire() === $this) {
                $prestation->setPrestataire(null);
            }
        }
        return $this;
    }
}
<?php
namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass="App\Repository\PrestationRepository")
 */
class Prestation
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="text", nullable=true)
     */
    private $descriptif;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Prestataire", inversedBy="prestations")
     */
    private $prestataire;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\Experience", mappedBy="prestation", cascade={"persist"}, orphanRemoval=true)
     */
    private $experiences;

    public function __construct()
    {
        $this->experiences = new ArrayCollection();
    }

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

    public function getDescriptif(): ?string
    {
        return $this->descriptif;
    }

    public function setDescriptif(?string $descriptif): self
    {
        $this->descriptif = $descriptif;
        return $this;
    }

    public function getPrestataire(): ?Prestataire
    {
        return $this->prestataire;
    }

    public function setPrestataireLogistique(?Prestataire $prestataire): self
    {
        $this->prestataire = $prestataire;

        return $this;
    }

    /**
     * @return Collection|Experience[]
     */
    public function getExperience(): Collection
    {
        return $this->experiences;
    }

    public function addExperience(Experience $experience): self
    {
        if (!$this->experiencesCinema->contains($experience)) {
            $this->experiences[] = $experience;
            $experience->setPrestation($this);
        }
        return $this;
    }

    public function removeExperience(Experience $experience): self
    {
        if ($this->experiences->contains($experience)) {
            $this->experiences->removeElement($experience);
            // set the owning side to null (unless already changed)
            if ($experience->getPrestation() === $this) {
                $experience->setPrestation(null);
            }
        }
        return $this;
    } 
}
<?php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ExperienceRepository")
 */
class Experience
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

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

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Prestation", inversedBy="experiences")
     * @ORM\JoinColumn(nullable=false)
     */
    private $prestation;

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

    public function getTitre(): ?string
    {
        return $this->titre;
    }

    public function setTitre(?string $titre): self
    {
        $this->titre = $titre;
        return $this;
    }

    public function getPrestation(): ?Prestation
    {
        return $this->prestation;
    }

    public function setPrestation(?Prestation $prestation): self
    {
        $this->prestation = $prestation;
        return $this;
    }
}

Voici les Forms correspondants à ces entités

<?php
namespace App\Form;

use App\Entity\Prestataire;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PrestataireType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('prestations', CollectionType::class, [
                'entry_type' => PrestationType::class,
                'entry_options' => [
                    'label' => false,
                ],
                'allow_add' => true,
                'allow_delete' => true,
                'required' => false,
                'by_reference' => false,
                'prototype' => true,
                'prototype_name' => 'prestation'
            ])
            ->add('save', SubmitType::class, [
                'attr' => [
                    'class' => 'btn btn-primary',
                ]
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Prestataire::class,
        ]);
    }
}
<?php
namespace App\Form;

use App\Entity\Catering;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PresdtationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('descriptif', TextareaType::class, [
                'label' => " Descriptif de l’activité et du service proposé",
            ])
            ->add('experiences', CollectionType::class, [
                'entry_type' => ExperienceType::class,
                'entry_options' => [
                    'label' => false,
                ],
                'allow_add' => true,
                'allow_delete' => true,
                'required' => false,
                'by_reference' => true,
                'prototype' => true,
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Prestation::class,
        ]);
    }
}
<?php
namespace App\Form;

use App\Entity\Travaux;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TravauxType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('titre', TextType::class, [
                'label' => false,
            ])
        ;
    }

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

Et voici la fonction de mon controller

/**
* @Route("/prestataire/new", name="prestataire_create")
* @Route("/prestataire/edit", name="prestataire_edit")
*/
public function editionform(PrestataireRepository $repository, Request $request, ObjectManager $manager)
    {
        $user = $this->getUser();

        $prestataire = $repository->findOneByUser($user);

        if(!$prestataire){
            $prestataire = new Prestataire();
        }

        $form = $this->createForm(PrestataireType::class, $prestataire);
        $form->handleRequest($request);

        if($form->isSubmitted() && $form->isValid()){
            //Je boucle l'ArrayCollection de Prestation
            foreach($prestataire->getPrestations() as $prestation){
                $prestation->setPrestataire($prestation);
                $manager->persist($prestation);
                //Je flush le $manager afin d'obtenir l'id  de la Prestation
                $manager->flush();

                foreach ($catering->getExperience() as $experience){
                    $experience->setPrestation($prestation);
                    $manager->persist($experience);
                }

                 //Enfin je flush de nouveau le $manager d'enregistrer les expériences
                $manager->flush();   
           }

            return $this->redirectToRoute('dashboard_user');
        }

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

Ce que je veux

Je voudrais que pour chaque prestation, l'ensemble des expériences soit enregsitré.

Ce que j'obtiens

Could not determine access type for property "experiences" in class
"App\Entity\Prestation": Neither the property "experiences" nor one 
of the methods "addExperienceson()"/"removeExperienceson()", 
"addExperiencesum()"/"removeExperiencesum()", "setExperiences()", 
"experiences()", "__set()" or "__call()" exist and have public access in 
class "App\Entity\Prestation".

J'ai tente depuis plusieurs de trouver une solution mais toute mes recherches sont infructueuses donc si quelqu'un peut me donner une solution ça sera avec un immense plaisir.

Merci



Answers 1


Le méthode getter (et setter) doit s'appeler exactement comme le property.

Donc:

private $experiences;
public function getExperiences();

et non:

private $experiences;
public function getExperience();
matwr
matwr
March 19, 2019 17:22 PM

Related Questions


Updated March 18, 2019 09:26 AM

Updated March 24, 2019 21:26 PM

Updated February 28, 2019 08:26 AM

Updated June 12, 2017 16:26 PM