【问题标题】:Symfony Data Transformer with Collection Field带有集合字段的 Symfony 数据转换器
【发布时间】:2015-05-22 01:22:51
【问题描述】:

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

我已经尝试了变压器的各种组合和放置方式,许多似乎都会产生各种错误。我最常看到的:

“对象、数组或空”类型的预期参数,给定“字符串”

我已经通过下面的代码使其进入非错误状态,它确实输出了一个文本字段,但它没有为该字段提供 Campaign 表单的任何属性(ID 或名称)以将其链接起来,因此对我没有好处。

活动实体

/**
 * @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';
    }
}

【问题讨论】:

  • 你能告诉我们你的控制器吗?
  • 控制器不处理任何一个,除了创建表单和处理请求对象的两行。编辑:渲染块中的​​行将表单分配给 twig 变量。
  • 你能精确一点你的错误Expected argument of type "object, array or empty", "string" given 吗?你在哪个对象/方法上得到这个?
  • @RaphaëlMalié,特别是在我看到该错误的情况下,它会显示为 Tag 实体。但是上面发布的代码并没有发生这种情况,使用该代码我得到了几乎预期的输出,但是输入字段缺少自动生成的 id/name 字段以将它们链接到 Campaign 表单。
  • 好吧,我想我明白了,看看if (empty($value)) 你没有$value var,用$tag 替换它;)

标签: php forms symfony


【解决方案1】:

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

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

【讨论】:

  • 我迟到了 4 年,但你能告诉我们你的事件监听器代码吗?谢谢
  • 抱歉@Pete_Gore 我无法再访问该代码库了。
猜你喜欢
  • 2011-12-07
  • 2014-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多