如何在Symfony中设置数据变换器以重用现有实体? [英] How to setup a data transformer in Symfony to reuse existing entities?

查看:198
本文介绍了如何在Symfony中设置数据变换器以重用现有实体?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在Symfony的文章编辑器中使用内置的标记功能:





控制器



$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ {$ $ $ $ $ $ $ $ $ $ $ $ $ - > getDoctrine() - > getManager();

// $ article = ...

$ form = $ this-> createForm(new ArticleType(),$ article);
$ form-> handleRequest($ request);

if($ form-> isValid()){
$ em-> persist($ article);
$ em-> flush();
return $ this-> redirect($ this-> generateUrl('acme_edit_success'));
}

return $ this-> render('AcmeBundle:Main:index.html.twig',array(
'form'=> $ form-> createView()
));

}
}



表单



标签表单注册为具有 @Doctrine 参数的服务,因此我可以在类中使用实体管理器。标签表单嵌入文章格式。



ArticleType.php



  class ArticleType extends AbstractType 
{
public function buildForm(FormBuilderInterface $ builder,array $ options)
{
$ builder
- > add('content' )
- > add('tags','collection',array(
'type'=>'acme_bundle_tagtype',
'allow_add'=> true,
'$'$'$'$'$'$'$'$'$'$'$'$'$'
}

public function setDefaultOptions(OptionsResolverInterface $ resolver)
{
$ resolver-> setDefaults(array(
'data_class'=> Acme\Bundle\Entity\Article',
'cascade_validation'=> true
));
}

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



TagType.php



  class TagType extends AbstractType 
{
private $ entityManager;

public function buildForm(FormBuilderInterface $ builder,array $ options)
{
$ transformer = new TagTransformer($ this-> entityManager);

$ builder-> add(
$ builder-> create('name')
- > addModelTransformer($ transformer)
);
}

函数__construct(\Doctrine\Bundle\DoctrineBundle\Registry $ doctrine){
$ this-> entityManager = $ doctrine-> getManager( );
}

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

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



数据变压器



我创建了这个数据变换器来检查给定的标签是否已经存在,然后将标签对象转换为数据库中已存在的标签对象:

  class TagTransformer实现DataTransformerInterface 
{
/ **
* @var ObjectManager
* /
private $ om;

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

public function transform($ tag)
{
if(null === $ tag){
return'';
}

return $ tag;
}

public function reverseTransform($ name)
{
if(!$ name)
return null;

$ tag = $ this-> om
- > getRepository('AcmeBundle:Tag')
- > findOneByName($ name)
;

if(!$ tag){
$ tag = new Tag();
$ tag-> setName($ name);
}

return $ tag;
}
}

当我尝试保存一个已经存在的文章标签, reverseTransform()函数成功返回原始的标签对象,但是, DBAL将对象转回到一个字符串,其 __ toString( )方法,而Doctrine仍然启动一个 INSERT 查询而不是 UPDATE ,所以我得到下一个错误:


执行'INSERT INTO Tag(name)
VALUES(?)时发生异常'with params [{}]:



SQLSTATE [23000]:完整性约束违规:1062重复条目
'现有标签'用于密钥'UNIQ_0123456789ABCDE' / p>

我该如何解决?当我输入已经使用的标签名称时,我希望Symfony在文章标签关系中使用相同的标签。实体类出现在我之前关于如何避免与Doctrine的多对多关系中重复输入的问题的问题

解决方案

您的变压器有一个错误。而不是检查名称是否为null,您应该验证是否返回标签:

  if(! $ tag){
$ tag = new Tag();
$ tag-> setName($ name);
}

您也不需要保留标签,因为默认情况下,doctrine将级联持有所有相关实体。



完整方法:

  public function reverseTransform($ name)
{
if(!$ name){
return null;
}

$ tag = $ this-> om
- > getRepository('AcmeBundle:Tag')
- > findOneByName($ name)
;

if(!$ tag){
$ tag = new Tag();
$ tag-> setName($ name);
}

return $ tag;
}


I'm working on an article editor in Symfony with built-in tagging capability:

The controller

class MainController extends Controller
{
    public function indexAction(Request $request, $id)
    {
        $em = $this->getDoctrine()->getManager();

        // $article = ...

        $form = $this->createForm(new ArticleType(), $article);
        $form->handleRequest($request);

        if ($form->isValid()) {
            $em->persist($article);
            $em->flush();
            return $this->redirect($this->generateUrl('acme_edit_success'));
        }

        return $this->render('AcmeBundle:Main:index.html.twig', array(
            'form' => $form->createView()
        ));

    }
}

The forms

The tag form is registered as a service with the @Doctrine argument, so I can use the entity manager inside the class. The tag form is embed inside the article form.

ArticleType.php

class ArticleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('content')
            ->add('tags', 'collection', array(
                'type' => 'acme_bundle_tagtype',
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false
            ))
            ->add('save', 'submit')
        ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Acme\Bundle\Entity\Article',
            'cascade_validation' => true
        ));
    }

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

TagType.php

class TagType extends AbstractType
{
    private $entityManager;

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new TagTransformer($this->entityManager);

        $builder->add(
            $builder->create('name')
                ->addModelTransformer($transformer)
        );
    }

    function __construct(\Doctrine\Bundle\DoctrineBundle\Registry $doctrine) {
        $this->entityManager = $doctrine->getManager();
    }

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

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

The data transformer

I created this data transformer to check if the given tag already exists and then transform the tag object to the one that already exists in the database:

class TagTransformer implements DataTransformerInterface
{
    /**
     * @var ObjectManager
     */
    private $om;

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

    public function transform($tag)
    {
        if (null === $tag) {
            return '';
        }

        return $tag;
    }

    public function reverseTransform($name)
    {
        if (!$name)
            return null;

        $tag = $this->om
            ->getRepository('AcmeBundle:Tag')
            ->findOneByName($name)
        ;

        if (!$tag) {
            $tag = new Tag();
            $tag->setName($name);
        }

        return $tag;
    }
}

When I try to save an article with an already existing tag, the reverseTransform() function successfully returns the original tag objects, but the DBAL converts the object back to a string by its __toString() method, and Doctrine still initiates an INSERT query instead of UPDATE, so I get the next error:

An exception occurred while executing 'INSERT INTO Tag (name) VALUES (?)' with params [{}]:

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'An Existing Tag' for key 'UNIQ_0123456789ABCDE'

How can I fix this? When I enter a tag name that's already in use, I want Symfony to use the same tag in the article-tag relationship. The entity classes appear in my previous question about how to avoid duplicate entries in a many-to-many relationship with Doctrine.

解决方案

There's one mistake in your transformer. Instead of checking if name is null you should verify if a tag was returned:

    if (!$tag) {
        $tag = new Tag();
        $tag->setName($name);
    }

You also don't need to persist the tag since by default doctrine will cascade persist all the related entities.

Full method:

public function reverseTransform($name)
{
    if (!$name) {
        return null;
    }

    $tag = $this->om
        ->getRepository('AcmeBundle:Tag')
        ->findOneByName($name)
    ;

    if (!$tag) {
        $tag = new Tag();
        $tag->setName($name);
    }

    return $tag;
}

这篇关于如何在Symfony中设置数据变换器以重用现有实体?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆