L.S
L.S

Reputation: 399

Symfony CollectionType self referencing

I have many to many association in my entity and i would like to use the collection type field from symfony to add multiple times. I've never done this way before and i'm kinda lost. The field that i would like to have multiple times is headquarter with a non mapped field for each headquarter. The error that i'm getting;

The property "headquarter" in class "AppBundle\Entity\SurveyManager" can be defined with the methods "addHeadquarter()", "removeHeadquarter()" but the new value must be an array or an instance of \Traversable, "AppBundle\Entity\HeadQuarterManager" given.

The Form Type implementing the Collection.

class SurveyOptionType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('headquarter', CollectionType::class, [
                'entry_type' => HeadQuarterType::class,
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,
                'label' => 'Sediu',
            ])
            ->add('isEnabled', CheckboxType::class, [
                'label' => 'Chestionar Activ',
                'required' => false,
            ])
            ->add('submit', SubmitType::class, [
                'label' => 'Salveaza',
            ]);
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => SurveyManager::class
        ]);
    }
}

This is the Collection Form

class HeadQuarterType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('headquarter', EntityType::class, [
                'class' => HeadQuarterManager::class,
                'label' => 'Sediu',
            ])
            ->add('userNumber', TextType::class, [
                'label' => 'Numar Utilizatori',
                'mapped' => false,
            ])
        ;
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => SurveyManager::class
        ]);
    }
}

UPDATE:

Here is the Entity class stripped away from all the uncesessary data

class SurveyManager
{

    /**
     * @var int
     *
     * @ORM\ManyToMany(targetEntity="AppBundle\Entity\HeadQuarterManager")
     * @ORM\JoinTable(name="survey_headquarters",
     *      joinColumns={@ORM\JoinColumn(name="survey_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="headquarter_id", referencedColumnName="id")}
     *      )
     */
    private $headquarter;

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


    /**
     * Constructor
     */
    public function __construct()
    {
        $this->question = new \Doctrine\Common\Collections\ArrayCollection();
        $this->headquarter = new \Doctrine\Common\Collections\ArrayCollection();
    }



    /**
     * Add headquarter
     *
     * @param \AppBundle\Entity\HeadQuarterManager $headquarter
     *
     * @return SurveyManager
     */
    public function addHeadquarter(\AppBundle\Entity\HeadQuarterManager $headquarter)
    {
        $this->headquarter[] = $headquarter;

        return $this;
    }

    /**
     * Remove headquarter
     *
     * @param \AppBundle\Entity\HeadQuarterManager $headquarter
     */
    public function removeHeadquarter(\AppBundle\Entity\HeadQuarterManager $headquarter)
    {
        $this->headquarter->removeElement($headquarter);
    }

    /**
     * Get headquarter
     *
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getHeadquarter()
    {
        return $this->headquarter;
    }
}

Controller method

$em = $this->getDoctrine()->getManager();
$surveyRepository = $em->getRepository(SurveyManager::class);
//$surveyManager = $surveyRepository->findOneById($surveyId);
$surveyManager = new SurveyManager();
$form = $this->createForm(SurveyOptionType::class, $surveyManager);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
    $em = $this->getDoctrine()->getManager();
    $em->persist($surveyManager);
    $em->flush();

    return $this->redirectToRoute('admin_survey-manager_survey_options',
        [
            'id' => $surveyManager->getId()
        ]);
}

return [
    'surveyManager' => $surveyManager,
    'form' => $form->createView(),
];

While doing research about this i did a separate demo to test on it to see better the problem.

Here is an updated code simplified that should work really smooth

https://gist.github.com/bogdaniel/a0bcc848e2bd282382f45a2bd15cc0e2

You will find more info about the error in the gist.

Upvotes: 1

Views: 1087

Answers (2)

redecs
redecs

Reputation: 111

How are you rendering your form? I suspect something similar to the issue found here: Error : the new value must be an array or an instance of \Traversable, Entity class given

Other than that, the only issue I can see in your code is that the 'data_class' option from HeadQuarterType should be HeadQuarterManager::class.

Upvotes: 0

Dmitry Malyshenko
Dmitry Malyshenko

Reputation: 3051

I don't really understand what do you want to archive, and what exactly does it mean:

The field that i would like to have multiple times is headquarter with a non mapped field for each headquarter.

But the error is quite self-explaining. Your SurveyManager class has to have some methods to populate data from Form. The flow is following - Form gets data (from User, for example) with an array of headquarters. And then Form looks for the way to pass this array to the object - and doesn't find them because you don't have appropriate methods.

Id doesn't matter if the property is mapped (I believe, you mean mapped to Doctrine), it still has be provided a way to pass data from Form to Object.

Upvotes: 0

Related Questions