user776440
user776440

Reputation:

Symfony2.1 entity field type multiple expanded not saving

I'm having some trouble with bi-directional relations (one-to-many) when using the entity field type in Symfony 2.1. When creating the child (Product) and selecting the parent (Category), everything is hunky dory, it's created and persisted as expected. The issue comes when creating a parent (Category), and selecting multiple children (Product) checkboxes as generated by Symfony2's form system. This does not persist.

I'll put my example classes below, however it might be easier cloning my example project repo - https://github.com/domudall/multiple-expanded-entity-choice-issue

Category

class Category
{
    protected $id;
    protected $name;

    /**
     * @ORM\OneToMany(targetEntity="Product", mappedBy="category")
     */
    protected $products;

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

    public function addProduct($product)
    {
        if (!$this->products->contains($product)) {
            $this->products->add($product);
            $product->setCategory($this);
        }

        return $this;
    }

    public function setProducts($products)
    {
        foreach ($products as $product) {
            $this->addProduct($product);
        }

        return $this;
    }

    public function removeProducts()
    {
        if ($this->products->contains($product)) {
            $this->products->remove($product);
        }

        return $this;
    }
}

Product

class Product
{
    protected $id;
    protected $name;

    /**
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
     */
    protected $category;

    public function setCategory(Category $category)
    {
        $this->category = $category;
        $category->addProduct($this);

        return $this;
    }

    public function getCategory()
    {
        return $this->category;
    }
}

DefaultController

class DefaultController extends Controller
{
    public function categoryCreateAction(Request $request)
    {
        $category = new Category();

        $form = $this
            ->createFormBuilder($category)
            ->add('name', 'text', array())
            ->add('products', 'entity', array(
                'class' => 'AcmeShopBundle:Product',
                'property' => 'name',
                'required' => false,
                'expanded' => true,
                'multiple' => true,
            ))
            ->getForm();

        if ($request->getMethod() == 'POST') {
            $this->save($category, $form, $request);
        }
    }

    public function productCreateAction(Request $request)
    {
        $product = new Product();

        $form = $this
            ->createFormBuilder($product)
            ->add('name', 'text')
            ->add('category', 'entity', array(
                'class' => 'AcmeShopBundle:Category',
                'property' => 'name',
                'required' => false,
            ))
            ->getForm();

        if ($request->getMethod() == 'POST') {
            $this->save($product, $form, $request);
        }
    }

    protected function save($entity, $form, $request)
    {
        $form->bind($request);

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

Any pointers to where I'm going wrong would be greatly appreciated.

NOTE

I don't want to use Doctrine cascade options, would rather have the logic in the entities for unit testing separate from the db.

Upvotes: 1

Views: 2266

Answers (1)

Lighthart
Lighthart

Reputation: 3656

You have to manually persist these entities if you don't want to cascade.

I use collection's map() method (below) but you can do it with foreach or array_map via the collection's toArray() method.

Try the following controller code:

protected function saveCategory($category, $form, $request)
{
    $form->bind($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();           
        $entity->getProducts()->map(
            function($product) use ($em) {
                $em->persist($product);
            }
        );

        $em->persist($category);
        $em->flush();
    }
}

Upvotes: 2

Related Questions