Bonjour,

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 quelq'un peut me donner une solution ça sera avec un immense plaisir.

Merci

4 réponses


Bonjour.
Tu as à mon avis un problème dans le nommage de ton getter pour la propriété $experiences, ton getter devrait se nommer getExperiences et non getExperience.

Bonjour,

Merci pour ta réponse mais hélas le problème persiste.
En effet il manquait un "s" dans le getter indiqué sur mon exemple mais c'était juste un problème de recopie...
Mon getter est bien getExperiences

C'est comme si le controller recherchait les fonctions getExperiences , addExperience et removeExperience dans l'entité Prestataire alors que celles-ci se trouvent dans mon entité Prestation puisque les expériences sont une collection de la collection Prestation.

Il y a quelque chose qui me dérange dans la fonction de ton controller, dans ta boucle qui liste les prestations, tu utilises la variable $catering, sauf que je ne la voi définie nulle-part, du coup à quoi est-ce qu'elle correspond ?

c'était juste un problème de recopie...
Mon getter est bien getExperiences

Dans ce cas là tu as fait une autre erreur de recopie, étant donné que lorsque tu veux boucler sur les expériences, tu fais appel à une méthode getExperience.

C'est comme si le controller recherchait les fonctions getExperiences , addExperience et removeExperience dans l'entité Prestataire alors que celles-ci se trouvent dans mon entité Prestation puisque les expériences sont une collection de la collection Prestation.

Ce n'est pas le cas étant donné que dans l'erreur il y est bien dit :

in class "App\Entity\Prestation"

Je m'aperçois que j'ai commis trop d'erreurs dans la recopie de mes scripts et que ma demande n'est pas hyper claire...
Je vais donc corriger tout cela avant de poursuivre ma demande.
En attendant, je vais clore le sujet afin de ne pas polluer inutilement le forum.
Je te remercie pour le temps que tu as consacré à tenter de répondre à mon problème.