Я использую теги в форме, используя tagsinput :
Этот плагин заканчивается одним текстовым полем, содержащим теги, разделенные запятой (например: tag1, tag2, …)
Эти теги в настоящее время управляются в неотображенном поле формы:
$builder
// ...
->add('tags', 'text', array(
'mapped' => false,
'required' => false,
))
;
И, наконец, они хранятся в коллекции ArrayCollection, как это плохая практика для хранения нескольких значений в поле базы данных:
/**
* @var ArrayCollection[FiddleTag]
*
* @ORM\OneToMany(targetEntity="FiddleTag", mappedBy="fiddle", cascade={"all"}, orphanRemoval=true)
*/
protected $tags;
Чтобы отобразить мою форму на мою сущность, я могу сделать некоторый код в моем контроллере, например:
$data->clearTags();
foreach (explode(',', $form->get('tags')->getData()) as $tag)
{
$fiddleTag = new FiddleTag();
$fiddleTag->setTag($tag);
$data->addTag($fiddleTag);
}
Но на первый взгляд это выглядит не так.
Мне интересно, как лучше всего сопоставить мою сущность с моей формой, а мою форму — с моей сущностью.
Это сложно, так как вы не просто встраиваете коллекцию форм Tag, которые, скажем, все отдельные текстовые поля. Я полагаю, вы могли бы сделать это с некоторыми хитростями, но как насчет использования преобразователя данных вместо этого? Вы можете преобразовать список тегов через запятую в ArrayCollection и передать его обратно в форму, а с другой стороны взять коллекцию и вернуть теги в виде строки через запятую.
FiddleTagsTransformer.php
<?php
namespace Fuz\AppBundle\Transformer;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Form\DataTransformerInterface;
use Fuz\AppBundle\Entity\FiddleTag;
class FiddleTagTransformer implements DataTransformerInterface
{
public function transform($tagCollection)
{
$tags = array();
foreach ($tagCollection as $fiddleTag)
{
$tags[] = $fiddleTag->getTag();
}
return implode(',', $tags);
}
public function reverseTransform($tags)
{
$tagCollection = new ArrayCollection();
foreach (explode(',', $tags) as $tag)
{
$fiddleTag = new FiddleTag();
$fiddleTag->setTag($tag);
$tagCollection->add($fiddleTag);
}
return $tagCollection;
}
}
Заметка: нельзя указать тип ArrayCollection для public function transform($tagCollection)
потому что ваша реализация должна соответствовать интерфейсу.
Второй шаг — заменить объявление поля формы, чтобы оно прозрачно использовало преобразователь данных, вам даже не нужно ничего делать в вашем контроллере:
FiddleType.php
$builder
// ...
->add(
$builder
->create('tags', 'text', array(
'required' => false,
))
->addModelTransformer(new FiddleTagTransformer())
)
;
Ты можешь использовать @Assert \ Count ограничить количество разрешенных тегов, и @Assert \ Valid если ваша сущность FiddleTag имеет некоторые ограничения валидации.
Fiddle.php
/**
* @var ArrayCollection[FiddleTag]
*
* @ORM\OneToMany(targetEntity="FiddleTag", mappedBy="fiddle", cascade={"all"}, orphanRemoval=true)
* @Assert\Count(max = 5, maxMessage = "You can't set more than 5 tags.")
* @Assert\Valid()
*/
protected $tags;
См. Документ Symfony2 о преобразователях данных: http://symfony.com/doc/current/cookbook/form/data_transformers.html
Смотрите эти сообщения для некоторых других идей:
Анализ строки, разделенной запятыми, в несколько записей базы данных (например, теги)
Других решений пока нет …