【问题标题】:Entity primary key inside symfony collectionsymfony 集合中的实体主键
【发布时间】:2016-07-20 09:24:09
【问题描述】:

我正在学习 symfony 3 和学说,并创建了一个带有实体集合的表单。实体是具有 manyTomany 关系的 Post 和 Tags。主要形式是带有标签集合的帖子。 我只想传递集合中标签的 ID(主键)。结果我只有一个标签形式的字段:

$builder->add('tagId');

我为它创建了自动完成功能,这就是我只需要主键的原因。 保存我的表单后,学说使用传递的 id 创建新的标签实体,但我想找到这些实体而不是创建新的实体。一点头绪都没有……

我试图让它在我的控制器中工作:

$formTags = $form->get('tag');
foreach ($formTags->getData() as $key => $formTag)
{
    // here i have new entities with id ;/
    if($formTag->getTagId())
    {
    // so i tryied to find them, and replace it
        $formTags->offsetSet($key, 
            array($this->getDoctrine()->getRepository('BlogBundle:Tag')
                          ->find($formTag->getTagId())));
    }
}

但是 symfony 会抛出异常,同样使用 setData 方法。提交表单后无法更改。希望大家帮帮我!

我正在尝试制作数据转换器。忘了提 :) 问题是我的转换器将 tagId 字段更改为标记对象。结果我有带有新实体的标签对象,而不是 tagId 值 - 里面有另一个对象,已转换。所以不要像我预期的那样工作。我认为我应该为收集字段而不是标签 ID 制作转换器,但不知道如何让它工作。我尝试在集合中创建“标记”字段并对其进行转换,但原则总是尝试根据字段从实体中获取值,因此没有找到 getTag() 方法:)

【问题讨论】:

    标签: php forms doctrine-orm symfony


    【解决方案1】:

    我做到了。使用数据转换器,但我们需要为集合制作转换器,而不是为集合中的字段。

    所以它看起来像那样(有效!)。

    我的 PostType.php 表单需要有实体管理器(如内部文档,关于数据转换器)和用于收集的数据转换器,所以我添加了:

    # PostType.php form
    namespace BlogBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    use Doctrine\ORM\EntityRepository;
    use Symfony\Bridge\Doctrine\Form\Type\EntityType;
    use Symfony\Component\Form\Extension\Core\Type\CollectionType;
    use BlogBundle\Form\DataTransformer\TagToIdTransformer;
    use Doctrine\Common\Persistence\ObjectManager;
    class PostType extends AbstractType
    {
    private $manager;
    
    public function __construct(ObjectManager $manager)
    {
        // needed for transformer :( 
        // and we need to register service inside app config for this. Details below
        $this->manager = $manager;
    }
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('tag', CollectionType::class, array(
            'entry_type' => TagType::class,
            'by_reference' => false,
            'allow_add'    => true,
        ))
            ->add('save', SubmitType::class, array('label' => 'Save'));
        $builder->get('tag')
            ->addModelTransformer(new TagToIdTransformer($this->manager));
    }
    }
    

    构造函数会抛出异常,我们需要将 ObjectManager 传递给它。要做到这一点,请修改捆绑包中的配置文件:

    # src/BlogBundle/Resources/config/services.yml
    services:
        blog.form.type.tag:
            class: BlogBundle\Form\PostType
            arguments: ["@doctrine.orm.entity_manager"]
            tags:
                - { name: form.type }
    

    现在让我们为一个集合制作转换器!我之前做错了,因为我试图为一个领域制作像内部文档一样的东西。对于集合,我们需要转换整个标签数组(它的 manyToMany 集合):

    <?php
    namespace BlogBundle\Form\DataTransformer;
    
    use BlogBundle\Entity\Tag;
    use Doctrine\Common\Persistence\ObjectManager;
    use Symfony\Component\Form\DataTransformerInterface;
    use Symfony\Component\Form\Exception\TransformationFailedException;
    
    class TagToIdTransformer implements DataTransformerInterface
    {
        private $manager;
    
        public function __construct(ObjectManager $manager)
        {
            $this->manager = $manager;
        }
    
        /**
         * Transforms array of objects (Tag) to an array of string (number).
         *
         * @param  array|null $tags
         * @return string
         */
        public function transform($tags)
        {
            $result = array();
    
            if (null === $tags) {
                return null;
            }
            foreach ($tags as $tag)
            {
                $result[] = $tag->getTagId();
            }
    
            return $result;
        }
    
        /**
         * Transforms an array of strings (numbers) to an array of objects (Tag).
         *
         * @param  string $tagsId
         * @return Tag|null
         * @throws TransformationFailedException if object (Tag) is not found.
         */
        public function reverseTransform($tagsId)
        {
            // no issue number? It's optional, so that's ok
            if (!$tagsId) {
                return;
            }
            $result = array();
            $repository = $this->manager
                ->getRepository('BlogBundle:Tag');
            foreach ($tagsId as $tagId) {
                $tag = $repository->find($tagId);
                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 id "%s" does not exist!',
                        $tagId
                    ));
                }
                $result[] = $tag;
            }
            return $result;
        }
    }
    

    现在一切正常。我可以使用仅填充标签 ID 的自动完成功能轻松保存我的实体

    【讨论】:

    • 你成就了我的一天!这是我的解决方案
    【解决方案2】:

    您可以在 TagType 中使用 Symfony DataTransfomer 将 tagId 转换为标签实体。

    来自 Symfony DataTransformer docs

    数据转换器用于将字段的数据转换为可以在表单中显示(并在提交时返回)的格式。

    ...

    假设您有一个从任务实体到问题实体的多对一关系(即每个任务都有一个与其相关问题的可选外键)。添加一个包含所有可能问题的列表框最终可能会变得非常长并且需要很长时间才能加载。相反,您决定要添加一个文本框,用户可以在其中输入问题编号。

    【讨论】:

    • 我以前做过。忘了提 :) 问题是我的转换器将 tagId 字段更改为标记对象。结果我使用了新实体的对象,没有 tagId 值 - 里面有另一个对象,已转换。我认为我应该为收集字段而不是标签 ID 制作转换器,但不知道如何使它工作。我尝试在集合中创建“标记”字段并对其进行转换,但原则总是尝试根据字段从实体中获取值,因此没有找到 getTag() 方法:)
    • 成功了...最后,还是感谢您的回答。这很有帮助,因为我几乎可以在没有数据转换器的情况下使其工作(以一种非常糟糕的方式),但在您发布后我越来越多地尝试使用它,现在可以使用
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多