具有收集字段的Symfony数据变换器

时间:2015-05-20 21:24:52

标签: php forms symfony

我正在试图找出如何让数据转换器在集合字段上工作。目前,用户可以创建一个Campaign并最多附加三个标签。实际上,每次用户输入标签的字符串(它是开放文本输入)时,都会创建该标签的新条目,无论它是否已存在。我想要做的是创建一个新标签,如果匹配的标签不存在,但如果有,则使用现有的标签。

我已尝试过变压器的各种组合和放置方式,而且很多都会产生各种错误。我最常见的是:

  

“object,array or empty”类型的预期参数,“string”给出

我已将其置于非错误状态,代码如下,它确实输出了一个文本字段,但它没有为该字段提供Campaign表单的任何属性(id或name)来链接它,因此我不好。

广告系列实体

/**
 * @ORM\Entity
 * @ORM\Table(name="campaigns")
 */
class Campaign
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string")
     */
    protected $name;

    /**
     * @var integer
     *
     * @ORM\Column(name="length", type="integer")
     */
    protected $length;

    /**
     * @var string
     *
     * @ORM\Column(name="start_date", type="datetime")
     */
    protected $startDate;

    /* ... snip ... */

    /**
     * @ORM\ManyToMany(targetEntity="Tag", inversedBy="campaigns", cascade={"persist"})
     * @ORM\JoinTable(name="campaigns_tags")
     */
    protected $tags;

    /* ... snip ... */

标记实体

/**
 * @ORM\Entity
 * @ORM\Table(name="tags")
 */
class Tag
{
    use TimestampTrait;

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     *
     * @ORM\Column(name="tag", type="string")
     */
    protected $tag;

    /* ... snip ... */

广告系列表单

class CampaignFormType extends AbstractType
{
    public function __construct(TagFormType $tag_form, SecurityContext $security_context)
    {
        $this->tag_form = $tag_form;
        $this->user = $security_context->getToken()->getUser();
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', 'text', array(
            'label' => 'Campaign Name:'
        ))
                ->add('length', 'integer', array(
                    'label' => 'Length (weeks):'
                ))
                ->add('startDate', 'date', array(
                    'label'  => 'Start Date:',
                    'widget' => 'single_text'
                ))
                ->add('tags', 'collection', array(
                    'type'         => $this->tag_form,
                    'allow_add'    => TRUE,
                    'by_reference' => FALSE
                ));
    /* ... snip ... */

标签表格

class TagFormType extends AbstractType
{
    /**
     * @var EntityManager
     */
    private $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new TagToStringTransformer($this->em);
        $builder->addModelTransformer($transformer);;
    }

    public function getParent()
    {
        return 'text';
    }
    /* ... snip ... */

标记变换器

class TagToStringTransformer implements DataTransformerInterface
{
    /**
     * @var EntityManager
     */
    private $em;

    /**
     * @param EntityManager $em
     */
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    /**
     * @param Tag|null $tag
     *
     * @return string
     */
    public function transform($tag)
    {
        if (empty($value)) {
            return "";
        }

        return $tag->getTag();
    }

    /**
     * @param string $string
     *
     * @return Tag|null
     */
    public function reverseTransform($string)
    {
        if (empty($string)) {
            return NULL;
        }

        $tag = $this->em->getRepository('AppBundle:Tag')->findOneBy(array('tag' => $string));

        // If the tag doesn't exist, create it
        if (empty($tag)) {
            $tag = new Tag();

            $tag->setTag($string);
        }

        return $tag;
    }
}

更新 我挖掘了一些更多的东西,并将TagFormType稍微改为看起来像下面的代码,似乎正确输出具有预期名称/ id属性的字段:

class TagFormType extends AbstractType
{
    /**
     * @var EntityManager
     */
    private $em;

    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new TagToStringTransformer($this->em);
        $builder->add($builder->create('tag', 'text', array('label' => FALSE))->addModelTransformer($transformer));
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(
            array(
                'data_class' => 'AppBundle\Entity\Tag',
            )
        );
    }

    public function getName()
    {
        return 'tag';
    }
}

1 个答案:

答案 0 :(得分:1)

我遇到的问题似乎源于我如何创造这个领域。简单地添加字段不能正常工作,但如果我在TagFormType类中切换到$builder->add($builder->create('tag', 'text', array('label' => FALSE))->addModelTransformer($transformer));,那么它几乎按预期工作。唯一的问题是变换器正在处理标签实体的标签字段,将其转换为Tag对象,而不是将Campaign表单的每个标签转换为已经存在的conuterpart(如果已经存在的conuterpart,则返回一个新标签)它不存在)。

让我按照我需要的方式工作的答案:我在POST_SUBMIT上实现了一个表单事件监听器。我拿每个提交的标签对象并检查它们是否已经存在。如果它们存在,我换掉对象。