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);
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 ofAdvert
entity to a uni-directional relationship:Also you should implement the
addCategories(Collection $categories)
andremoveCategories(Collection $categories)
methods insideAdvert
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 aboutAdvert
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 agetAdvertsByCategoryId($categoryId)
method in your AdvertRepository.The last detail is, you should have a
CategoryFieldset
(which also needs to useCategory
entity as object) and you have to point this fieldset in your form'scategories
element usingtarget_element
configuration key or directly providing instance's itself.For example:
I strongly recommend using of
FormElementManager
to get form and fieldset instances instaed of directly instantiate them bynew AdvertForm()
andnew CategoryFieldset()
. Also writing anAbstractFormElementFactory
would be good practice to inject$entityManager
like dependencies to your fieldsets and forms, the right way.Hope it helps.