Search code examples
many-to-manysymfony-2.8arraycollection

Symfony expected must be an instance of Doctrine\Common\Collections\ArrayCollection, instance of Doctrine\ORM\PersistentCollection given


I am using Symfony 2.8

I have a many to many relationship between User and Sectors entities, the insertion works well but the creation of the edit form (the editAction function) does not work. All the code is there:

<?php
namespace UserBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
class User extends BaseUser implements ParticipantInterface
{
/**
 * @ORM\Id
 * @ORM\Column(name="id", type="integer", nullable=false)
 * @ORM\GeneratedValue(strategy="CUSTOM")
 * @ORM\CustomIdGenerator(class="CoreBundle\Model\CustomIdGenerator")
 *
 * @var int
 */
protected $id;

/**
 * @ORM\ManyToMany(targetEntity="UserBundle\Entity\Sectors", fetch="EAGER")
 * @ORM\JoinTable(
 *      joinColumns={@ORM\JoinColumn(onDelete="CASCADE")},
 *      inverseJoinColumns={@ORM\JoinColumn(onDelete="CASCADE")}
 * )
 */
public $sectors;

/**
 * @ORM\ManyToMany(targetEntity="UserBundle\Entity\UserAccreditation")
 * @ORM\JoinTable(
 *      joinColumns={@ORM\JoinColumn(onDelete="CASCADE")},
 *      inverseJoinColumns={@ORM\JoinColumn(onDelete="CASCADE")}
 * )
 */
public $accreditationdata;

/**
 * Constructor.
 */
public function __construct()
{
    $this->accreditationdata = new ArrayCollection();
    $this->sectors = new ArrayCollection();

    parent::__construct();
}

/**
 * Add accreditationdatum
 *
 * @param UserBundle\Entity\UserAccreditation $accreditationdatum
 *
 * @return User
 */
public function addAccreditationdatum(UserBundle\Entity\UserAccreditation $accreditationdatum)
{
    $this->accreditationdata[] = $accreditationdatum;

    return $this;
}

/**
 * Remove accreditationdatum
 *
 * @param UserBundle\Entity\UserAccreditation $accreditationdatum
 */
public function removeAccreditationdatum(UserBundle\Entity\UserAccreditation $accreditationdatum)
{
    $this->accreditationdata->removeElement($accreditationdatum);
}


/**
 * Add sector
 *
 * @param UserBundle\Entity\Sectors $sector
 *
 * @return User
 */
public function addSector(UserBundle\Entity\Sectors $sector)
{
    $this->sectors[] = $sector;

    return $this;
}

/**
 * Remove sector
 *
 * @param UserBundle\Entity\Sectors $sector
 */
public function removeSector(UserBundle\Entity\Sectors $sector)
{
    $this->sectors->removeElement($sector);
}

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

ProfileAboutMeFormType.php

<?php
namespace UserBundle\Form\Type;

use UserBundle\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\HttpFoundation\RequestStack;  
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;  
use Doctrine\ORM\EntityRepository;    
use Symfony\Component\Form\FormInterface;     
use UserBundle\Entity\Sectors;
use UserBundle\Entity\UserAccreditation;

class ProfileAboutMeFormType extends AbstractType
{ 

/**
 * @param string       $class The User class name
 * @param RequestStack $requestStack
 * @param array        $locales
 */
public function __construct($class, RequestStack $requestStack, $locales)
{
    $this->class = $class;
    $this->request = $requestStack->getCurrentRequest();
    $this->locale = $this->request->getLocale();
    $this->locales = $locales;
}


public function buildForm(FormBuilderInterface $builder, array $options)
{
    /** @var User $user */
    $user = $builder->getData();

        $builder
        ->add('sectors', EntityType::class, array(
            'class'       => 'UserBundle\Entity\Sectors',
            'placeholder' => 'Select Sector ...',
            'label' => 'Sector',
            'required' => false,
            'multiple' => false,
            'expanded' => false,
            'query_builder' => function(EntityRepository $er) {
                return $er->createQueryBuilder('u')
                    ->orderBy('u.name', 'ASC');
            },
        ))
        ->add('accreditationdata', EntityType::class, array(
            'class' => 'UserBundle\Entity\UserAccreditation',
            'label' => 'Accreditation',
            'required' => false,
            'multiple' => true,
            'expanded' => true,
        ))
        ;
    }    

            $formModifier = function (FormInterface $form, UserMake $make = null) {

                $maker = null === $make ? array() : $make->getId();


                $form->add('model', EntityType::class, array(
                        'class' => 'Cocorico\UserBundle\Entity\UserModel',
                        'query_builder' => function(EntityRepository $ert) use ($maker) {
                                $query = $ert->createQueryBuilder('i')
                                    ->select(array('i'))
                                    ->where('i.make = :make_id')
                                    ->setParameter('make_id', $maker)
                                    ->orderBy('i.name', 'ASC');

                                return $query;
                            },
                        //'choices' => array($maker),
                        'required' => false,
                        'placeholder' => 'Select Make First ...',
                        'multiple' => false,
                        'expanded' => false,
                    )
                );
            };

            $builder->addEventListener(FormEvents::PRE_SET_DATA,function (FormEvent $event) use ($formModifier) {
                    $data = $event->getData();
                    $form = $event->getForm();
                    if ($data != null) 
                    $formModifier($event->getForm(), $data->getMakes());
                }
            );

            $builder->get('makes')->addEventListener(FormEvents::POST_SUBMIT,function (FormEvent $event) use ($formModifier) {
                    $make = $event->getForm()->getData();
                    $formModifier($event->getForm()->getParent(), $make);
                }
            );
        /******************************** This event listener is for sector and accreditation dependent fields ***************************************/
            $factory = $builder->getFormFactory();

                $formModify = function (FormInterface $form, \Doctrine\Common\Collections\ArrayCollection $sector, $factory) {

                    $output = [];
                    foreach($sector as $sec) {
                        $output[] = $sec->id;
                    }

                    $formOption = array(
                        'class' => 'UserBundle\Entity\UserAccreditation',
                        'multiple' => true,
                        'auto_initialize' => false,
                        'required' => false,
                        'expanded' => true,
                        'choice_attr' => function ($output) {
                                return ['class' => 'attr_checkbox'];
                        },
                        'query_builder' => function(EntityRepository $ertt) use ($output) {
                            // build a custom query, or call a method on your repository (even better!)
                            $qb = $ertt->createQueryBuilder('g');
                            $qb->select(array('g'));
                            $qb->where('g.sector IN (:sector_id)');
                            $qb->setParameters( array('sector_id' => $output) );
                            $qb->orderBy('g.name', 'ASC');
                            return $qb;
                        },
                    );
                    $form->add($factory->createNamed('accreditationdata', EntityType::class, null, $formOption));
                };

                $builder->addEventListener(FormEvents::PRE_SET_DATA,function (FormEvent $event) use ($formModify,$factory) {
                        $data = $event->getData();
                        $form = $event->getForm();
                        if ($data != null) 
                        //print_r(get_object_vars($data->getSectors()));
                        $formModify($event->getForm(), $data->getSectors(),$factory);
                    }
                );

                $factory = $builder->getFormFactory();
                $builder->get('sectors')->addEventListener(FormEvents::POST_SUBMIT,function (FormEvent $event) use ($formModify,$factory) {
                        $sector = $event->getForm()->getData();
                        $formModify($event->getForm()->getParent(), $sector,$factory);
                    }
                );

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(
        array(
            'data_class' => $this->class,
            'csrf_token_id' => 'profile',
            'translation_domain' => 'user',
        )
    );
}

/**
 * BC
 * {@inheritdoc}
 */
public function getName()
{
    return $this->getBlockPrefix();
}

/**
 * {@inheritdoc}
 */
public function getBlockPrefix()
{
    return 'user_profile_about_me';
}
}

and I get error which says :

Type error: Argument 2 passed to UserBundle\Form\Type\ProfileAboutMeFormType::UserBundle\Form\Type{closure}() must be an instance of Doctrine\Common\Collections\ArrayCollection, instance of Doctrine\ORM\PersistentCollection given, called in /src/UserBundle/Form/Type/ProfileAboutMeFormType.php on line 383

should I change getSectors method to PersistentCollection ?? or I need to totally change my approach?


Solution

  • So yeah, basically answered my own question. I got my own solution for this kind of approach. I have to change my eventlistener for "Edit Form".

    As I understood when you query database by repository you get PersistentCollection and when your working with your entities you get ArrayCollection

    I have changed the \Doctrine\Common\Collections\ArrayCollection $sector as array $sector in $formModify function. also I have changed in PRE_SET_DATA and POST_SUBMIT Events too.

    Below is the full code of changed event listener.

                    $factory = $builder->getFormFactory();
    
                    $formModify = function (FormInterface $form, array $sector, $factory) {
                        $output = [];
                        foreach($sector as $sec) {
                            $output[] = $sec;
                        }
                        $formOption = array( 
                            'class' => 'Cocorico\UserBundle\Entity\UserAccreditation',
                            'multiple' => true,
                            'auto_initialize' => false,
                            'required' => false,
                            'expanded' => true,
                            'label' => 'Accreditations',
                            'choice_attr' => function ($output) {
                                    return ['class' => 'attr_checkbox'];
                            },
                            'query_builder' => function(EntityRepository $ertt) use ($output) {
                                // build a custom query, or call a method on your repository (even better!)
                                $qb = $ertt->createQueryBuilder('g');
                                $qb->select(array('g'));
                                $qb->where('g.sector IN (:sector_id)');
                                $qb->setParameters( array('sector_id' => $output) );
                                $qb->orderBy('g.name', 'ASC');
                                return $qb;
                            },
    
                        );
    
                        $form->add($factory->createNamed('accreditationdata', EntityType::class, null, $formOption));
                    };
    
                    $builder->addEventListener(FormEvents::PRE_SET_DATA,function (FormEvent $event) use ($formModify,$factory) {
                            $data = $event->getData();
                            $form = $event->getForm();
                            if ($data != null) 
                            $newarray = [];
                            foreach ($data->getSectors() as $post) {
                                // $post is not an array
                                 $newarray[] = $post->id;
                            }
                            $formModify($event->getForm(), $newarray,$factory);
                        }
                    );
    
                    $factory = $builder->getFormFactory();
                    $builder->get('sectors')->addEventListener(FormEvents::POST_SUBMIT,function (FormEvent $event) use ($formModify,$factory) {
                            $sector = $event->getForm()->getData();
                            $created_array = array($sector->getId());
                            $array = [];
                            foreach($created_array as $secval) {
                                $array[] = $secval;
                            }
                            $formModify($event->getForm()->getParent(), $array,$factory);
                        }
                    );    
    

    This works for me. Hope it help someone else for such type of approach.