Array Collection, symfony: add a relation

Gianni Alessandro picture Gianni Alessandro · Jan 29, 2014 · Viewed 12.5k times · Source

I'm new of Symfony and php, and I'm trying to understand, without outcome, the array collection.

Now I have two entity, Mission and User, in relation ManytoMany. I have a form to create new Missions and a form to create new User.

Now I have to create a "modifyMissionAction" that allows me to set the Users for that missions, but I didn't understand how to do it. I read the documentation here but it doesn't help. How could I do?

Thank you

This is my User Entity is:

 abstract class User extends BaseUser
 {

      /**
      * @var \Doctrine\Common\Collections\ArrayCollection
      * 
      * @ORM\ManyToMany(targetEntity="Acme\ManagementBundle\Entity\Mission", inversedBy="users", orphanRemoval=true)
      * @ORM\JoinTable(name="user_mission")
      */
     private $missions;    
     /**
      * Add missions
      *
      * @param \Acme\ManagementBundle\Entity\Mission $missions
      * @return User
      */
     public function addMission(\Acme\ManagementBundle\Entity\Mission $missions)
     {
         $this->missions[] = $missions;

         return $this;
     }
//...

And my Mission Entity:

<?php

namespace Acme\ManagementBundle\Entity;

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

/**
 * @ORM\Entity
 */
class Mission {
    /** 
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     * @var integer
     */
    protected $id;
        /** 
     * @ORM\Column(type="string", length=60)
     * @var String
     */
    protected $name;
    /** 
     * @ORM\Column(type="string", length=600)
     * @var String
     */
    protected $description;
    /**
     * @var \Doctrine\Common\Collections\ArrayCollection
     *
     * @ORM\ManyToMany(targetEntity="Acme\ManagementBundle\Entity\User", mappedBy="missions", cascade={"all"}, orphanRemoval=true)
     */
    private $users;

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

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Mission
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set description
     *
     * @param string $description
     * @return Mission
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return string 
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * Add users
     *
     * @param \Acme\ManagementBundle\Entity\User $users
     * @return Mission
     */
    public function addUser(\Acme\ManagementBundle\Entity\User $users)
    {
        $this->users[] = $users;

        return $this;
    }

    /**
     * Remove users
     *
     * @param \Acme\ManagementBundle\Entity\User $users
     */
    public function removeUser(\Acme\ManagementBundle\Entity\User $users)
    {
        $this->users->removeElement($users);
    }

    /**
     * Get users
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getUsers()
    {
        return $this->users;
    }
    public function __toString()
    {
        return $this->name;
    }
}

Answer

WebHQ picture WebHQ · Jan 29, 2014

first of all don't forget to add __constructor for both of classes and init ArrayCollection:

//src/WebHQ/NewBundle/Entity/Mission.php
//...
public function __construct()
{
    $this->users = new ArrayCollection();
}
//...

I assume that you want to add controller action witch allows you to relate user object or objects to mission object. Please read embbed forms part of Symfony book

Then create your Action. Most important is to add form element, with is embedded form of users entity:

// src/WebHQ/NewBundle/Controller/MissionController.php
//...
public function newAction(Request $request)
{
    $object = new \WebHQ\NewBundle\Entity\Mission();

    $form = $this->createFormBuilder($object)
            ->add('name', 'text')
            //...
            // Users objects embed form
            ->add('users', 'user')
            //...
            ->add('save', 'submit')
            ->getForm();

    if ($request->isMethod('POST')) {
        $form->bind($request);

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

            return $this->redirect($this->generateUrl('web_hq_new_mission_index'));
        }
    }

    return $this->render('WebHQNewBundle:Mission:new.html.twig', array(
        'form'      => $form->createView(),
        //...
    ));
}

public function editAction($id, Request $request)
{
    $object = $this->getDoctrine()
            ->getRepository('WebHQNewBundle:Mission')
            ->find($id);

    $form = $this->createFormBuilder($object)
            ->add('name', 'text')
            //...
            ->add('users', 'user')
            //...
            ->add('save', 'submit')
            ->add('delete', 'submit')
            ->getForm();

    if ($request->isMethod('POST')) {
        $form->bind($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $form->get('save')->isClicked() ? $em->persist($object) : $em->remove($object);
            $em->flush();

            return $this->redirect($this->generateUrl('web_hq_new_mission_index'));
        }
    }

    return $this->render('WebHQNewBundle:Mission:edit.html.twig', array(
        'form'      => $form->createView(),
        //...
    ));
}
//...

Check your routing. You should have route for edit action with {id} param. If name of param not fits you change it in route and in function definition:

// src/WebHQ/NewBundle/Resources/config/route.yml
//...
web_hq_new_mission_new:
    pattern: /mission/new
    defaults: { _controller: WebHQNewBundle:Mission:new }

web_hq_new_mission_edit:
    pattern: /mission/{id}/edit
    defaults: { _controller: WebHQNewBundle:Mission:edit }
//...

Then define Form Type for User objects:

// src/WebHQ/NewBundle/Form/Type/UserType.php
namespace WebHQ\NewBundle\Form\Type;

use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;


class UserType extends AbstractType
{
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'class' => 'WebHQNewBundle:User',
            'property' => 'name',
            'empty_value' => 'Choose',
            'required' => true,
            'multiple' => true,
            'query_builder' => function (Options $options) {
                return function(EntityRepository $er) use ($options) {
                    return $er->createQueryBuilder('c')
                        ->orderBy('c.name', 'ASC');
                };
            },
        ));
    }

    public function getParent()
    {
        return 'entity';
    }

    public function getName()
    {
        return 'user';
    }
}

And register a type in service.yml:

# src/WebHQ/NewBundle/Resources/config/services.yml
#...
services:
    web_hq_new.form.type.user:
        class: WebHQ\NewBundle\Form\Type\UserType
        tags:
            - { name: form.type, alias: user }
#...

Good Luck!