IvanFantoM
@IvanFantoM
PHP-разработчик

Как в Zend Framework 2 реализовать динамическое добавление связанных моделей?

Я использовал следующий пример для реализации:

У меня есть 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 ?
  • Вопрос задан
  • 593 просмотра
Решения вопроса 1
IvanFantoM
@IvanFantoM Автор вопроса
PHP-разработчик
Отличная иллюстрация динамического добавления сущностей:
https://github.com/arvind2110/ZF2-Collection-Form
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Fesor
@Fesor
Full-stack developer (Symfony, Angular)
давайте думать. Нужно ли странице знать о своих тегах? Да, было бы прикольно но только что делать если теги нужно будет добавлять еще чему-нибудь? Или например кейсы когда какие-то теги надо создать а какие-то просто подключить.

Так вот, убираем связь тегов из Page и оставляем однонаправленную ассоциацию на страницы у тегов. Теги нам делает какой-нибудь сервис (Tagger) с методами аля assignTags($page, $tags) и getTagsOf($page)

Да, это суть сложнее зато можно спокойно реюзать в будущем, это проще поддерживать и вообще кошерно.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы