Я использовал следующий пример для реализации:
У меня есть Entity для страницы и для тэгов к страницам:
MyModule\Entity\Page
MyModule\Entity\Tag
Для одной страницы можно указать N тэгов.
На странице добавления страницы не удается динамически добавлять тэги для страницы. Подскажите что я делаю не так.
Привожу листинг основных файлов:
Модель Страницы:
namespace MyModule\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
 * Page
 *
 * @ORM\Table(name="page", indexes={@ORM\Index(name="fk_page_category", columns={"category_id"})})
 * @ORM\Entity
 */
class Page
{
    /**
     * @var integer
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    private $name;
    /**
     * @var \MyModule\Entity\Category
     * @ORM\ManyToOne(targetEntity="MyModule\Entity\Category")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="category_id", referencedColumnName="id")
     * })
     */
    private $category;
    /**
     * @ORM\OneToMany(targetEntity="MyModule\Entity\Tag", mappedBy="page", cascade={"persist"})
     */
    protected $tags;
    /**
     * Never forget to initialize all your collections !
     */
    public function __construct()
    {
        $this->tags = new ArrayCollection();
    }
    /**
     * @param Collection $tags
     */
    public function addTags(Collection $tags)
    {
        foreach ($tags as $tag) {
            $tag->setPage($this);
            $this->tags->add($tag);
        }
    }
    /**
     * @param Collection $tags
     */
    public function removeTags(Collection $tags)
    {
        foreach ($tags as $tag) {
            $tag->setPage(null);
            $this->tags->removeElement($tag);
        }
    }
    /**
     * @return Collection
     */
    public function getTags()
    {
        return $this->tasg;
    }
    /**
     * Get category_id.
     * @return string
     */
    public function getCategory()
    {
        return $this->category;
    }
    /**
     * Set category_id.
     * @param Category $category
     * @return Category
     */
    public function setCategory($category)
    {
        $this->category = $category;
        return $this;
    }
}
Модель Тэга:
namespace MyModule\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
 * Tag
 * @ORM\Table(name="tag", indexes={@ORM\Index(name="fk_tag_page", columns={"page_id"})})
 * @ORM\Entity
 */
class Tag
{
    private $id;
    private $name;
    /**
     * @var \MyModule\Entity\Page
     * @ORM\ManyToOne(targetEntity="MyModule\Entity\Page")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="page_id", referencedColumnName="id")
     * })
     */
    private $page;
    /**
     * Allow null to remove association
     * @param Page $page
     */
    public function setPage(Page $page = null)
    {
        $this->page = $page;
    }
    /**
     * @return Page
     */
    public function getPage()
    {
        return $this->page;
    }
}
MyModule\Form\PageFieldset:
namespace MyModule\Form;
use MyModule\Entity\Page;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class PageFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('page');
        $this->setHydrator(new DoctrineHydrator($objectManager))
            ->setObject(new Page());
        $this->add(array(
            'type' => 'Zend\Form\Element\Text',
            'name' => 'name'
        ));
        $tagsFieldset = new TagFieldset($objectManager);
        $this->add(array(
            'type'    => 'Zend\Form\Element\Collection',
            'name'    => 'tags',
            'options' => array(
                'count'           => 2,
                'target_element' => $tagFieldset
            )
        ));
    }
    public function getInputFilterSpecification()
    {
        return array(
            'name' => array(
                'required' => true
            ),
        );
    }
}
MyModule\Form\TagFieldset:
namespace MyModule\Form;
use MyModule\Entity\Tag;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
class TagFieldset extends Fieldset implements InputFilterProviderInterface
{
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('tag');
        $this->setHydrator(new DoctrineHydrator($objectManager))
            ->setObject(new Tag());
        $this->add(array(
            'type' => 'Zend\Form\Element\Hidden',
            'name' => 'id'
        ));
        $this->add(array(
            'type'    => 'Zend\Form\Element\Text',
            'name'    => 'name',
            'options' => array(
                'label' => 'Tag'
            )
        ));
    }
    public function getInputFilterSpecification()
    {
        return array(
            'id' => array(
                'required' => false
            ),
            'name' => array(
                'required' => true
            )
        );
    }
}
MyModule\Form\PageForm:
namespace MyModule\Form;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Form\Form;
class PageForm extends Form
{
    protected $objectManager;
    /**
     * Set the object manager
     *
     * @param ObjectManager $objectManager
     */
    public function setObjectManager(ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;
    }
    /**
     * Get the object manager
     *
     * @return ObjectManager
     */
    public function getObjectManager()
    {
        return $this->objectManager;
    }
    public function __construct(ObjectManager $objectManager)
    {
        parent::__construct('page-form');
        $this->setObjectManager($objectManager);
        // The form will hydrate an object of type "Page"
        $this->setHydrator(new DoctrineHydrator($objectManager));
        // Add the user fieldset, and set it as the base fieldset
        $pageFieldset = new PageFieldset($objectManager);
        $pageFieldset->setUseAsBaseFieldset(true);
        $this->add($pageFieldset);
        $this->createElements();
    }
    public function createElements()
    {
        $this->setAttribute('method', 'post');
        $this->add(array(
            'name' => 'security',
            'type' => 'Zend\Form\Element\Csrf',
        ));
        $this->add(array(
            'type' => 'DoctrineModule\Form\Element\ObjectSelect',
            'name' => 'category',
            'options' => array(
                'label' => 'Category',
                'empty_option' => 'Select platform ...',
                'object_manager' => $this->getObjectManager(),
                'target_class' => 'MyModule\Entity\Category',
                'property' => 'name'
            ),
            'attributes' => array(
                'required' => 'required'
            )
        ));
        $this->add(array(
            'name' => 'name',
            'type' => 'Text',
            'options' => array(
                'label' => 'Name'
            ),
            'attributes' => array(
                'required' => 'required'
            )
        ));
        $this->add(array(
            'name' => 'submit',
            'type' => 'Submit',
            'attributes' => array(
                'value' => 'Save'
            )
        ));
    }
}
Ну и наконец представление add.twig:
{% extends 'layout/layout.twig' %}
{% block content %}
<h1>Add Page</h1>
<div>
    {{ form().prepare() }}
    {{ form().openTag(form)|raw }}
    {% for element in form %}
        {% set attributes = element.getAttributes() %}
        {% if type != 'hidden' and type != 'submit' %}
            <div class="field">
        {% endif %}
        {% if attributes.type is defined %}
            {% set type = attributes.type %}
        {% else %}
            {% set type = '' %}
        {% endif %}
        {% if type != 'hidden' and type != 'submit' %}
            <label for="{{ element.getName() }}">{{ element.getLabel() }}: </label>
            {% if attributes.required is defined %}
                <abbr title="Обязательное поле">*</abbr>
            {% endif %}
        {% endif %}
        {% if type == 'text' %}
            {{ formInput(form.get( element.name ))|raw }}
        {% elseif type == 'textarea' %}
            {{ formTextarea(form.get( element.name ))|raw }}
        {% elseif type == 'select' %}
            {{ formSelect(form.get( element.name ))|raw }}
        {% elseif type != 'hidden' and type != 'submit' %}
            {{ formRow().setInputErrorClass('error').render(form.get( element.name ))|raw }}
        {% endif %}
        {% if type != 'hidden' and type != 'submit' %}
            </div>
        {% endif %}
    {% endfor %}
    {{ formCollection(form.get( 'page' ).get( 'tags' ))|raw }}
    {{ formRow(form.get('submit'))|raw }}
    {{ form().closeTag(form)|raw }}
</div>
{% endblock content %}
Подскажите что нужно поправить, чтобы при создании Page можно было динамически добавлять сколько угодно Tag ?