Doctrine 2: Form field shows Doctrine\Common\Collections\ArrayCollection@00 as value

490 views Asked by At

I am pretty new to Zend Framework 2 and Doctrine 2, so I am not even sure how to search or debug my problem.

I have 3 database tables

1. advert
id
adverttitle ...

2. category
id
categoryname ...

3. advert_category
advert_id
category_id

I have created 2 Entities, Advert and Category. I have now got a Form where I show the Categories to choose from. I use jQuery to display the categories as a list instead of a dropdown, together with a selectable function. So when you click on a category, the value of this listelement gets entered into a hidden input field called categories.

Everything works fine, besides that when I display the form, the hidden categories input field got a value of Doctrine\Common\Collections\ArrayCollection@000000000..... instead of being empty. What am I doing wrong here? I have tried to find a solution, but unsuccessfully.

I have chosen a ManyToMany Relationship because I want to be able to save more then 1 category in the end. Currently it is only working with 1, but this way I should be able to change this at a later time.

Here my Advert entity:

namespace Advert\Entity;

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

/** Advert
 * 
 * @ORM\Table(name="advert")
 * @ORM\Entity(repositoryClass="Advert\Repository\AdvertRepository")
 */

class Advert
{
 /**
  * @var integer
  *
  * @ORM\Column(name="id", type="integer", nullable=false)
  * @ORM\Id
  * @ORM\GeneratedValue(strategy="IDENTITY")
  */
  private $id;


 /**
  * @var string
  *
  * @ORM\Column(name="advert_title", type="string", length=255, nullable=true)
  */
  private $advertTitle; 

 /** 
  * @ORM\ManyToMany(targetEntity="Category", inversedBy="adverts", cascade={"persist"}) 
  * @ORM\JoinTable(name="advert2category") 
  */
  private $categories;

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

 /**
  * Set categories
  *
  * @param ArrayCollection $category
  * @return Advert
  */
  public function setCategories($categories)
  {
    $this->categories = $categories;
    return $this;
  }


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

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

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

  }

Is there an Error in the Advert Entity which causes this? I hope someone can help. I have this problems since weeks and can not get it to work correctly.

UPDATE -- added my Form and part in controller to call form

The below Form displays 2 Dropdown Elements and 2 Hidden Input Fields. The 2 Dropdown Fields get turned into a selectable List via jQuery. When you click on a List Element from the Maincategory, the Subcategories show up for that chosen Maincategory again as a selectable list. The MaincategoryID gets then entered into the hidden categoryID Field. As soon you choose the Subcategory from the List, the id of that category gets written in the hidden categories field. A click on the "next" button saves the value of $_POST['categories'] together with the advertID in my linking table.

use Zend\Form\Form;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;

class CategoryForm extends Form implements ObjectManagerAwareInterface
{

protected $objectManager;


public function __construct()
{       
    $this->setInputFilter(new AdvertFilter());
    parent::__construct('category');


}

public function init()
{
    $this->setAttribute('method', 'post');




    $this->add(array(
            'name' => 'categories',
            'attributes' => array(
                    'type' => 'hidden',
                    'id'    => 'categories',

            ),
            'options'=> array(
                    'label'=> 'categories',
                    ),


    ));


    $this->add(
            array(

                    'type' => 'DoctrineModule\Form\Element\ObjectSelect',
                    'name' => 'categoriesList',

                    'options' => array(

                            'object_manager' => $this->getObjectManager(),
                            'label' => 'Main Category',
                            'target_class'   => 'Advert\Entity\Category',
                            'property'       => 'name',
                            'is_method' => true,

                            'find_method'        => array(
                                    'name'   => 'getMainCategories',
                            ),
                    ),
                    'allow_empty'  => true,
                    'required'     => false,
                    'attributes' => array(
                            'id' => 'categoryList',
                            'multiple' => true,


                    )
            )
    );

    $this->add(
            array(
                    'type' => 'DoctrineModule\Form\Element\ObjectSelect',
                    'name' => 'subcategoryList',
                    'options' => array(
                            'object_manager' => $this->getObjectManager(),
                            'label' => 'Sub Category',


                            'target_class'   => 'Advert\Entity\Category',
                            'property'       => 'name',

                            'is_method' => true,
                            'find_method'        => array(
                                    'name'   => 'getSubCategories',
                            ),
                    ),
                    'allow_empty'  => true,
                    'required'     => false,
                    'attributes' => array(

                            'id' => 'subcategoryList',
                            'multiple' => true,
                            )
            )
    );


    $this->add(array(
            'type' => 'hidden',
            'name' => 'categoryID',
            'options'=> array(
                    'label'=> 'categoryID'),
            'attributes' => array(
                    'id' => 'categoryID',
                    'value' => '1',
            )
    ));

   $this->add(array(
            'name' => 'submit',
            'attributes' => array(
                    'type'  => 'submit',
                    'value' => 'Next',
                    'id' => 'submitbutton',
            ),
    ));



}

public function setObjectManager(ObjectManager $objectManager)
{
    $this->objectManager = $objectManager;
}

public function getObjectManager()
{
    return $this->objectManager;
}

}

In my Controller I call my form the following way:

    $sl = $this->getServiceLocator();
    $form = $sl->get('FormElementManager')->get('\Advert\Form\CreateForm');

    # create a new, empty entity
    $advert = new Advert();

    # set the hydrator to connect form and entity
    $form->setHydrator(new DoctrineHydrator($this->getEntityManager(),'Advert\Entity\Advert'));

    # connect form and entity
    $form->bind($advert);
1

There are 1 answers

2
edigu On

The first thing is, bidirectional relationships doesn't uses join tables. Your mapping seems like bi-directional however you're trying to use the third table: advert_category.

I recommend to change mapping of the $categories property of Advert entity to a uni-directional relationship:

class Advert
{
     // ...

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

    // ...
}

Also you should implement the addCategories(Collection $categories) and removeCategories(Collection $categories) methods inside Advert entity if you want to take advantage of DoctrineObject hydrator. (I'm assuming that you're using DoctrineORMModule).

At this point, your Category entity shouldn't know anything about Advert and you CAN'T DIRECTLY ACCESS all adverts from a category instance via an entity method like $category->getAdverts(). However when needed, you can easily write a getAdvertsByCategoryId($categoryId) method in your AdvertRepository.

The last detail is, you should have a CategoryFieldset (which also needs to use Category entity as object) and you have to point this fieldset in your form's categories element using target_elementconfiguration key or directly providing instance's itself.

For example:

$formManager = $serviceLocator->get('FormElementManager');
$form = $formManager->get('your\form\name');
$form->add(
    array(
        'name' => 'categories',
        'type' => 'Zend\Form\Element\Collection',
        'options' => array(
            'target_element' => $formManager->get('your\fieldset\name');
            // or you can do this but probably you will also need $entityManager
            // inside the CategoryFieldset
            // 'target_element' => new CategoryFieldset();
            ),
        )
    );

I strongly recommend using of FormElementManager to get form and fieldset instances instaed of directly instantiate them by new AdvertForm() and new CategoryFieldset(). Also writing an AbstractFormElementFactory would be good practice to inject $entityManager like dependencies to your fieldsets and forms, the right way.

Hope it helps.