【问题标题】:Symfony form hidden relation to foreign entitySymfony 形成与外部实体的隐藏关系
【发布时间】:2017-03-31 08:36:29
【问题描述】:

我正在尝试在 symfony 3 中创建一个表单。它有一个隐藏字段用于与另一个实体的关系。它被定义为

$builder->add('course', HiddenType::class, array('property_path' => 'course.id'))

这样做时,当我提交表单时出现错误,因为 id 属性不可见并且显然没有设置器。

Could not determine access type for property "id".

我该怎么办?为 id 添加一个 setter 可能是个坏主意。默认情况下没有是有原因的。 id 以外的其他字段不是唯一的,所以我不能使用其中任何一个。

我猜一个选项可能是自定义类型,它查询数据库中的引用实体。但是对于这个非常标准的用例,有没有更简单的方法?

编辑

好吧,显然我的问题被误解了。所以我会解释得更好。这不是关于教义的。我的数据模型很好,该字段是正确的关系,而不是其他东西。问题仅在于 Symfony Form 组件,特别是 HiddenField 默认情况下似乎无法处理关系。首先,它会在显示表单Cannot serialize type Model to string 时导致错误,我可以使用property_path 指令来解决此问题以显示表单。但是一旦提交它就无法创建相关实体,因为它无法设置相关学说实体的 id 属性(参见property_path)。

一个可行的解决方案是使用DataTransformerInterface 类而不是属性路径之类的东西。但是必须为所需的每个实体实施一个。所以我想知道是否有更简单的解决方案,因为在我看来它是一个非常标准的用例。

【问题讨论】:

  • 为什么要使用 property_path?字段“课程”应映射到其他实体 - 然后隐藏字段中的值将是该外国实体的 ID。
  • @AndrzejPiszczek 可能这就是我在文档中遗漏的内容。有选择吗?目前我正在使用自定义变压器
  • 阅读docs.doctrine-project.org/projects/doctrine-orm/en/latest/…。有关于实体连接的信息。
  • 好吧,那你误会我了。教义联系很好,他们正在工作。我只是不知道表单组件(它正在工作,但我想知道是否有更简单的解决方案)

标签: forms symfony symfony-3.1


【解决方案1】:

这是我所做的:

我在现场使用Symfony\Component\Form\DataTransformerInterface 来序列化模型,然后在表单传输时通过原则从数据库再次加载它。可能不是每个人都想要的,这也意味着表单需要实体管理器作为选项。

在表格中

/**
 * Some form
 */
class SomeType extends AbstractType
{
    // Configure 
    public function configureOptions(OptionsResolver $resolver) {
        [...]
        $resolver->setRequired('entity_manager');
    }

    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder->add('course', HiddenType::class);

        [...]

        $builder->get('course')
           ->addModelTransformer(new CourseTransformer($options['entity_manager']));
    }
}

这里是 CourseTransformer

class CourseTransformer implements DataTransformerInterface
{
    // The object manager
    private $manager;

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

    /**
     * Transforms an object (course) to a string (id).
     */
    public function transform($course)
    {
        if (null === $course) return '';
        return $course->getId();
    }

    /**
     * Transforms a string (id) to an object (course).
     */
    public function reverseTransform($courseId)
    {
        // no course id? It's optional, so that's ok
        if (!$courseId) {
            return;
        }

        $course = $this->manager
            ->getRepository('MyBundle:Course')
            // query for the issue with this id
            ->find($courseId)
        ;

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

        return $course;
    }
}

【讨论】:

    【解决方案2】:

    Symfony 4 方法

    您需要为您的实体创建一个 DataTransformer 类,该类将告诉 Symfony 在将实体呈现为字符串时如何序列化它,例如作为隐藏输入。

    这就是我的做法:

    首先,我在“Form”文件夹中创建了一个文件夹“DataTransformer”,并在其中创建了我的实体 DataTransformer 类。

    您的类必须实现 DataTransformerInterface 和两个方法:transform 和 reverseTransform。您可以在接口类的 PHPDoc 中阅读更多关于这些方法的信息。

    这是一个 DataTransformer 类的工作示例。

    <?php
    
    
    namespace App\Form\DataTransformer;
    
    
    use App\Repository\CompetitionRepository;
    use Symfony\Component\Form\DataTransformerInterface;
    
    class CompetitionTransformer implements DataTransformerInterface
    {
    
        private $repository;
    
        public function __construct(CompetitionRepository $repository) {
            $this->repository = $repository;
        }
    
        /**
         * {@inheritdoc}
         */
        public function transform($competition): ?int
        {
            return ( $competition !== null ) ? $competition->getId() : null;
        }
    
        /**
         * {@inheritdoc}
         */
        public function reverseTransform($competitionId)
        {
    
            if ('' === $competitionId || null === $competitionId) {
                return '';
            }
    
            $competition = $this->repository->find( $competitionId );
            return $competition;
        }
    }
    

    您将在表单中使用 DataTransformer 类。

    class EventType extends AbstractType
    {
        [...]
        private $competitionRepository;
    
        public function __construct(CompetitionRepository $competitionRepository)
        {
            $this->competitionRepository = $competitionRepository;
        }
    
        [...]
    
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            [...]
            // This is how you use your DataTransformer class
            $builder->add('competition', HiddenType::class);
            $builder->get('competition')->addModelTransformer(new CompetitionTransformer( $this->competitionRepository ));
        }
    
        [...]
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-24
      • 2014-08-17
      • 1970-01-01
      相关资源
      最近更新 更多