phpdoctrine-ormmany-to-manyzend-framework2entity

Zend Framework 2 Doctrine 2 many to many entity relation issue


I have the following tables: films, categories, films_categories and the entities:

Film.php

    <?php

namespace Admin\Entity;

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

/**
 * @ORM\Table(name="films")
 */
class Film{

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     * @ORM\Column(length=11)
     */
    private $id;

    /**
     * @ORM\Column(type="string")
     */
    private $name;

    /* .... */

    /**
     * @ORM\ManyToMany(targetEntity="Category")
     * @ORM\JoinTable(name="films_categories", 
     *              joinColumns={@ORM\JoinColumn(name="film_id", referencedColumnName = "id")}, 
     *              inverseJoinColumns={@ORM\JoinColumn(name="category_id", referencedColumnName="id")})
     */
    private $categories;

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

    public function getCategoriesNames(){
        $names = array();
        foreach($this->categories as $category){
            $names[] = $category->getName();
        }   
        return $names;  
    }

    public function getId(){
        return $this->id;
    }

    public function setId($id){
        $this->id = $id;
    }

    /* ... */

    /**
     * @return Collection
     */
    public function getCategories(){
        return $this->categories;
    }

    public function addCategories(Collection $categories){
        foreach($categories as $category){
            $this->categories->add($category);
        }
    }

    public function removeCategories(Collection $categories){
        foreach($categories as $category){
            $this->categories->removeElement($category);
        }
    }
}

Category.php

<?php

namespace Admin\Entity;

use Doctrine\ORM\Mapping as ORM;


/**
 * @ORM\Table(name="categories")
 */
class Category {

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer")
     */
    private $id;

    /* ... */   

    public function getId(){
        return $this->id;
    }

    public function setId($id){
        $this->id = $id;
    }

    /* ... */
}

What I want to do is create a form and the action to add a new film and assign a category to it. Here is the form I used:

FilmFieldset.php

<?php
namespace Admin\Form;

use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use DoctrineORMModule\Stdlib\Hydrator\DoctrineEntity;
use Admin\Entity\Film;

class FilmFieldset extends Fieldset implements InputFilterProviderInterface{

    protected $entityManager;

    public function __construct($em){

        parent::__construct('film');


        $this->entityManager= $em;
        $this->setHydrator(new DoctrineEntity($em,'Admin\Entity\Film'))
            ->setObject(new Film());

        #$this->setAttribute('method','post');
        #$this->setAttribute('class','standardForm');

        $this->add(array(
            'name' => 'id',
            'type' => 'hidden'
        ));     

        /* ... */   

        $this->add(
            array(
                'type' => 'DoctrineModule\Form\Element\ObjectSelect',
                'name' => 'categories',
                'attributes' => array(
                    'multiple' => 'multiple',
                ),                
                'options' => array(
                    'object_manager' => $em,
                    'target_class'   => 'Admin\Entity\Category',
                    'property'       => 'name',
                    'label'          => 'Categories: ',
                    'disable_inarray_validator' => true               
                ),
            )
        );                      

    }

    public function getInputFilterSpecification(){
        return array(
                    /* .... */
            'categories' => array(
                'required' => true,                 
            ),                  
        );  
    }

}

The FilmForm.php

<?php
namespace Admin\Form;

use Zend\Form\Form;
use Zend\Stdlib\Hydrator\ClassMethods;
use Admin\Entity\Film;
use Zend\InputFilter\InputFilter;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;

class FilmForm extends Form{

    public function __construct($em){
        parent::__construct('filmForm');

        $this->setAttribute('method','post')
             ->setAttribute('class','standardForm')
             ->setHydrator(new DoctrineHydrator($em,'\Admin\Entity\Film'))
             ->setInputFilter(new InputFilter());

            /* I register the fieldset through a service and not directly here */
        // $this->add(array(
            // 'type' => new FilmFieldset($em),
            // 'options' => array(
                // 'user_as_base_fieldset' => true
            // )
        // ));

        $this->add(array(
            'name' => 'security',
            'type' => 'Zend\Form\Element\Csrf'
        ));

        $this->add(array(
            'name' => 'submit',
            'type' => 'submit',
        ));

        $this->setValidationGroup(array(
            'security',
            'film' => array(
                'categories',
            )
        ));
    }


}

The addAction:

public function addAction() {
    $em = $this->getEntityManager();
    $form = $this->getForm();
    $film = new Film();
        $form->bind($film);

    if($request->isPost()){
        $post = array_merge_recursive(
                    $request->getPost()->toArray(),
                    $request->getFiles()->toArray()
                );      
        $form->setData($post);
        if($form->isValid()){
            $categories = array();
            foreach($post['film']['categories'] as $categoryId){
                $categories[] = $em->getRepository('Admin\Entity\Category')->find($categoryId);
            }

            $film->addCategories($categories);

            $em->persist($film);
            $em->flush();
         }else{
             // the form is not valid
         }
    }

The result is various errors and and ORMExcption with the message "Found entity of type on association Admin\Entity\Film#categories, but expecting Admin\Entity\Category"

Please help me out, I'm literally freaking out over this! Thank you :)


Solution

  • From what i found out from this is some thing like this that your entity is receiving Admin\Entity\Film#categories and in this part categories is some what a value. Where as your entity is expecting an object of type Admin\Entity\Film#categories.

    To get over this you have to create a

     public function SetCategory(Admin\Entity\Category Category)
    {
     $this->categories(or category or w/e your variable name is)= Category;
    }
     public function getCategory()
    {
     return $this->categories(or category or w/e your variable name is);
    }
    

    And then in your Action you have to pass object of Category to Film Entity in some thing like this

      $Film->SetCategory($categoryObj);
    

    Of course you have to set your business logic according to your part, but this error Should be removed by this apporach.