【问题标题】:Symfony CollectionType & class inheritanceSymfony CollectionType 和类继承
【发布时间】:2017-04-04 10:54:59
【问题描述】:

我正在为一个 Symfony 项目苦苦挣扎,由于我对框架没有那么丰富的经验,所以我不知道我是否有设计缺陷,是否 Symfony 无法处理我的用例,或者我是否只需要找到正确的方法。

这里是:

我有一个实体 Row,它应该包含 1 到 n 个具有不同内容的项目,例如“标题”、“文本”、“图像”等。

由于每个内容都有不同的特征,我通过单表继承从一个名为 RowContent 的抽象类中扩展了每个内容类型。 这是实体的编辑版本: 类行

class Row
{
    //.....

    /**
     * @var ArrayCollection $rowContents
     *
     * @ORM\OneToMany(targetEntity="RowContent", mappedBy="row", cascade={"persist", "remove", "merge"})
     */
    private $rowContents;

    //...
}

类行内容:

/**
 * RowContent
 *
 * @ORM\Table(name="row_content")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="discr", type="string")
 * @ORM\DiscriminatorMap({
 *     "title" = "Kinkinweb\BaseBundle\Entity\Content\Title",
 *     "text" = "Kinkinweb\BaseBundle\Entity\Content\Text",
 *     "image" = "Kinkinweb\BaseBundle\Entity\Content\Image",
 * })
 * @ORM\Entity(repositoryClass="Kinkinweb\BaseBundle\Repository\RowContentRepository")
 */

abstract class RowContent
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
    
    //...
}

例如文本类:

/**
 * Text
 *
 * @ORM\Table(name="content_text")
 * @ORM\Entity(repositoryClass="Kinkinweb\BaseBundle\Repository\Content\TextRepository")
 */
class Text extends RowContent
{
    /**
     * @var string
     *
     * @ORM\Column(name="text", type="string", length=255)
     */
    private $text;

    //...
}

到目前为止一切顺利,但我无法处理所有这些的表单提交...... 为了处理带有这些实体的表单,到目前为止,我有以下 FormType :

class RowType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            //...
            ->add('rowContents', CollectionType::class, array(
                'entry_type'   => RowContentType::class,
                'allow_add'    => true,
                'allow_delete' => true,
                'label' => 'Contenu Flexible',
                'by_reference' => false,
                'block_name' => 'rows',
            ))
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Kinkinweb\BaseBundle\Entity\Row',
        ));
    }
}

我已经像这样处理了 buildForm 部分:

    public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->addEventListener(FormEvents::PRE_SET_DATA, function($e) {
                if (null === $e->getData()) { return; }
                $form = $e->getForm();
                if ($e->getData() instanceof Text){
                    $form->add('text',TextType::class);
                }
            });
        }
public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Kinkinweb\BaseBundle\Entity\RowContent';
        ));
    }

但是当我使用 jQuery 在表单中添加内容并提交表单时,框架无法处理提交的 RowContent 对象(对我来说似乎合乎逻辑,因为 RowContent 是抽象的)。

所以在我一根一根拔头发之前,我想知道是否有人已经遇到过这样的情况,或者对如何提交表单有任何见解。

谢谢!

【问题讨论】:

    标签: forms symfony inheritance


    【解决方案1】:

    我知道这个问题很老,但我遇到了同样的问题,我设法解决了。我会分享我的解决方案,以防将来有人遇到同样的问题。

    在撰写本文时,我使用的是 symfony 5。

    1. 我为每种类型创建了一个表单。例如,在您的情况下,它将是 Title 的一种形式,Text 的一种形式和 Image 的另一种形式(我们分别称它们为 RowTitleContentType、RowTextContentType 和 RowImageContentType)。

    我确实创建了一个 AbstractRowContentType 并从它扩展。这样我就不必在每个表单类型中重复公共字段。

    1. 然后,在您的 RowType 表单中,为您创建的每个表单类型添加一个 CollectionType 并将“mapped”属性设置为 false。换句话说,它看起来像这样:

       public function buildForm(FormBuilderInterface $builder, array $options)
       {
           $builder
               //...
               ->add('rowTitleContents', CollectionType::class, array(
                   'entry_type'   => RowTitleContentType::class,
                   'mapped' => false,
                   'allow_add'    => true,
                   'allow_delete' => true,
                   'by_reference' => false,
               ))
              ->add('rowTextContents', CollectionType::class, array(
                   'entry_type'   => RowTextContentType::class,
                   'mapped' => false,
                   'allow_add'    => true,
                   'allow_delete' => true,
                   'by_reference' => false,
               ))
              ->add('rowImageContents', CollectionType::class, array(
                   'entry_type'   => RowImageContentType::class,
                   'mapped' => false,
                   'allow_add'    => true,
                   'allow_delete' => true,
                   'by_reference' => false,
               ))
           ;
       }
      
    2. 现在我们需要设置一个监听器来合并内容并将它们添加到 RowContent 对象中:

       public function buildForm(FormBuilderInterface $builder, array $options)
       {
           //..
           $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
               $row = $event->getData();
               $form = $event->getForm();
      
               //let's get every content from the form
      
               $rowTitleContents = $form->get('rowTitleContents')->getData();
               $rowTextContents = $form->get('rowTextContents')->getData();
               $rowImageContents = $form->get('rowImageContents')->getData();
      
               //Let's merge everything
               $mergedRowContents = array_merge($rowTitleContents, 
               $rowTextContents, $rowImageContents);
      
               // And now we add everything to the object.
               $row->setRowContents(new ArrayCollection($mergedRowContents));
           });
       }
      

    就是这样。希望它可以帮助某人。

    顺便说一下,我的解决方案是基于这个帖子:Build Form with Single Table Inheritance

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-09
      • 1970-01-01
      • 2016-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多