嵌入表单集合:检查实体是否存在

Embedding Collection of Forms: check if entity exists

我正在实施 this tutorial。但是在我的例子中 Tag 实体有一个独特的 name 属性:

/**
 * @ORM\Entity()
 */
class Tag
{
    /**
     * @var string
     * @ORM\Column(name="name", type="string", length=63, unique=true)
     */
    private $name;

    /**
     * @ORM\ManyToMany(targetEntity="Task", cascade={"persist"})
     * @ORM\JoinTable(name="tasks_tags")
     */
    protected $tasks;

  }

如何检查 Tag 实体是否已存在于 newedit 控制器中,以便我可以决定创建新实体。如果 Tag 存在,我可以将其添加到新任务中。

public function newAction(Request $request)
{
    $task = new Task();

    $form = $this->createForm(TaskType::class, $task);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $tags = $form->get('tags')->getData();

        $em = $this->getDoctrine()->getManager();
        foreach ($tags as $tagName) {
            $tag = $this->getDoctrine()->getRepository('AppBundle:Tag')->findOneBy(array('name' => $tagName));
            if (!$tag) {
                $newTag = new Tag();
                $newTag->setName($tagName);
                $em->persist($newTag);
            }
        }
        $em->persist($task);
        $em->flush();

        return $this->redirectToRoute('tasks_list');
    }
}

提交后出现这个错误:

Catchable Fatal Error: Object of class AppBundle\Entity\Tag could not be converted to string

我认为这是因为 Tag 实体在 Task 表单提交之前被持久化了。

我该如何处理这部分?

不,那是因为您正在重定向到 tasks_list,这将显示 Tag 您已坚持但您没有该对象的字符串表示形式。

为了避免这个错误,在Tag实体中引入如下方法

public function __toString()
{
  // This is only an example; you can insert 
  // here whatever you want to display
  // as tag's string representation

  return $this->name;
}

您在 Tag 实体的 $tasks 上有 cascade={"persist"};但是,您在 $tag 之后坚持 $task。删除 $task 上的 cascade={"persist"} 并尝试将 cascade={"persist"} 放在 Task 实体中的 $tags 上并检查是否有任何差异。

因为你还没有发布 TaskType::class,我猜你已经使用了这个代码片段来获取标签:

$builder->add('tags', CollectionType::class, array(
   'entry_type' => TagType::class
));

你看,Symfony 表单的工作方式是,它们将你从用户那里得到的数据序列化回实体。当你创建一个标签集合时,你实际上在做的是在内部将标签名称序列化回标签对象。所以这部分代码:

$tags = $form->get('tags')->getData();

实际上获取的是标签对象数组,而不仅仅是字符串。这就是为什么当您循环遍历数组以检查现有标签时:

foreach ($tags as $tagName) {}

$tagName 实际上不是标签名称。它是 class 标签的一个对象。当您尝试通过 tagName:

查找标签时
->findOneBy(array('name' => $tagName))

,您实际上正在做的是向学说存储库传递一个 Tag 对象以查询名称。现在内部它当然会尝试将其转换为字符串,因为您无法将对象与字符串进行比较。这就是您收到错误的原因。

为了实现您的目标,您首先必须:

->findOneBy(array('name' => $tagName->getName()))

(如果此时更改 tagName 的命名也很好,因为它实际上不是一个名称,它是一个对象),那么如果您在数据库中找到一个标签,您应该删除您的对象在这种情况下,已通过 $em->remove() 保存在 tagName 变量中,并将新找到的标签添加到任务中。

您应该使用数据转换器。参考:http://symfony.com/doc/current/form/data_transformers.html

// src/AppBundle/Form/DataTransformer/TagToNameTransformer.php
namespace AppBundle\Form\DataTransformer;

use AppBundle\Entity\Tag;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;

class TagToNameTransformer implements DataTransformerInterface
{
    private $manager;

    public function __construct(ObjectManager $manager)
    {
        $this->manager = $manager;
    }

    /**
     * Transforms an object (tag) to a string (name).
     *
     * @param  Tag|null $tag
     * @return string
     */
    public function transform($tag)
    {
        if (null === $tag) {
            return '';
        }

        return $tag->getName();
    }

    /**
     * Transforms a string (name) to an object (tag).
     *
     * @param  string $tagName
     * @return Tag|null
     * @throws TransformationFailedException if object (tag) is not found.
     */
    public function reverseTransform($tagName)
    {
        // empty tag name? It's optional, so that's ok
        if (empty($tagName)) {
            return;
        }

        $tag = $this->manager
            ->getRepository('AppBundle:Tag')
            ->find($tagName)
        ;

        if (null === $tag) {
            // causes a validation error
            // this message is not shown to the user
            // see the invalid_message option
            throw new TransformationFailedException(sprintf(
                'An tag with name "%s" does not exist!',
                $tagName
            ));
        }

        return $tag;
    }
}

TagTypeclass中添加这段代码:

class TagType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // ... your code

        $builder->get('name')
            ->addModelTransformer(new TagToNameTransformer($this->manager));
    }
}

瞧!如果您的标签已经存在,它将被找到并链接到您的任务。