如何在CollectionType的表单事件中获取数据?

时间:2019-02-04 21:06:22

标签: mongodb forms symfony event-listener

我在Symfony 4上有一个问题,该问题已在Github(此处为https://github.com/symfony/symfony/issues/5694#issuecomment-110398953)上确定并描述,但我找不到适用此答案的方法。

当我尝试在ChildType表单中使用POST_SET_DATA表单事件时,函数getData()为我提供了一个空值,因为在ParentType表单(即CollectionType)中将“ allow_add”选项设置为true。

我有3个集合:页面,模块和模块。模块化文档用于嵌入模块表单的集合。目的是能够按照此Symfony文章https://symfony.com/doc/current/form/form_collections.html的一个请求将多个表单添加到Page集合。

我有2个不同的嵌入式文档:标签和任务。两者都嵌入在模块文档(EmbedOne)中。我想做的是能够使用表单事件侦听器自定义ModuleType字段,以便我只需要在控制器中设置Module的标题,然后Symfony知道它需要在ModuleType中使用TaskType或TagType 。

那么首先,这是我的控制器

id          author      body          created_at  href        post_id     likes       comments  
----------  ----------  ------------  ----------  ----------  ----------  ----------  ----------
1           Bob         test comment  1990-12-17              test        1           [2,3]     
2           Jane        test comment  1990-12-18                          0           []        
3           Jill        test comment  1990-12-19                          0           [4,5]     
4           Bortus      test comment  1990-12-20                          2           []        
5           John        test comment  1990-12-21                          0           []  

现在,这是我的三个集合:页面,模块和模块

class TaskingController extends Controller
{
    /**
     * The controller from which I set the module title, "task" here
     *
     * @Route("/{slug}/task/create", name="tasking_create")
     * 
     * @ParamConverter("page", options={"mapping": {"slug": "slug"}})
     * 
     * @return Response
     */
    public function createTasking(DocumentManager $dm, $id, Module $module, Moduling $moduling)
    {
        $page = $dm->find(Page::class, $id);

        $module->setTitle('task');

        $moduling->addModule($module);
        $page->addModuling($moduling);

        $form = $this->createForm(ModulingType, $moduling);
        $form->handleRequest($request);

        if ($form->isValid() && $form->isSubmitted() {
            // Form validation then redirection
        }
        // Render form template}
    }
}

我的嵌入式文档任务。标签文档具有相同的结构。

/**  
 * My page document
 *
 * @MongoDB\Document(collection="pages")
 */
class Page
{
    /** 
     * @MongoDB\Id(strategy="AUTO")
     */
    protected $id;

    /**
     * @MongoDB\ReferenceMany(targetDocument="App\Document\Moduling")
     * 
     * @var Moduling
     */
    protected $moduling = array();

    public function __construct()
    {
        $this->moduling = new ArrayCollection();
    }

    /**
     * Get the value of id
     */ 
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return Collection $moduling
     */    
    public function getModuling()
    {
        return $this->moduling;
    }

    /**
     * @param Moduling $moduling
     */
    public function addModuling(Moduling $moduling)
    {
        $this->moduling[] = $moduling;
    }

    /**
     * @param Moduling $moduling
     */
    public function removeModuling(Moduling $moduling)
    {
        $this->moduling->removeElement($moduling);
    }
}

/**
 * @MongoDB\Document(collection="moduling")
 */
class Moduling
{
    /**
     * @MongoDB\Id(strategy="AUTO")
     */
    protected $id;

    /**
     * @MongoDB\ReferenceOne(targetDocument="App\Document\Page", storeAs="id")
     * 
     * @var Page
     */
    protected $parentPage;

    /**
     * @MongoDB\ReferenceMany(targetDocument="App\Document\Module", mappedBy="moduling")
     */
    protected $module = array();

    public function __construct()
    {
        $this->module = new ArrayCollection();
    }

    /**
     * Get the value of id
     */
    public function getId()
    {
        return $this->id;
    }

    public function getModule()
    {
        return $this->module;
    }

    public function addModule(Module $module): self
    {   
        $this->module[] = $module;
    }

    public function removeModule(Module $module)
    {
        $this->module->removeElement($module);
    }

    /**
     * Get the value of parentPage
     *
     * @return  Page
     */ 
    public function getParentPage()
    {
        return $this->parentPage;
    }

    /**
     * Set the value of parentPage
     *
     * @param  Page  $parentPage
     *
     * @return  self
     */ 
    public function setParentPage(Page $parentPage)
    {
        $this->parentPage = $parentPage;

        return $this;
    }
}

/**  
 * @MongoDB\Document(collection="modules")
 */
class Module
{
    /**
     * @MongoDB\Id(strategy="AUTO")
     */
    public $id;

    /**
     * @MongoDB\Field(type="string")
     */
    public $title;

    /**
     * @MongoDB\ReferenceOne(targetDocument="App\Document\Moduling", inversedBy="module", storeAs="id")
     */
    public $moduling;

    /**
     * @MongoDB\EmbedOne(targetDocument="App\Document\Task", strategy="set")
     * @Assert\Valid
     */
    public $task;

    public function getTitle()
    {
        return $this->title;
    }

    /**
     * @return  self
     */ 
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    public function getTask()
    {
        return $this->task;
    }

    public function setTask(Task $task = null)
    {
        $this->task = $task;
    }
}

我的ModulingType,它是ModuleType的集合

/**  
 * @MongoDB\EmbeddedDocument
 */
class Task
{
    /**
     * @MongoDB\Id(strategy="AUTO")
     */
    protected $id;

    public function getId()
    {
        return $this->id;
    }
}

因此,我已经确定了问题所在。当我尝试使其工作时,Symfony向我发送此错误消息:“在null上调用成员函数getTitle()”。似乎getData()什么也没得到。

实际上,在阅读了有关Github的几篇文章之后,我意识到设置为“ true”的“ allow_add”选项是此问题的根源。确实,当我将其设置为“ false”时,我没有任何错误消息。但是这样做的结果是,如果我愿意的话,我的JQuery不允许我重复表格,必须使用“ allow_add”选项。

在我上传的Github帖子中,他们说解决方案是首先在ModuleType中编写以下代码:

class ModulingType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('module', CollectionType::class, [
                'entry_type' => ModuleType::class,
                'entry_options' => [
                    'label' => false,
                ],
                'by_reference' => false,
                'allow_add' => true,
                'allow_delete' => true
                ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Moduling::class
        ]);
    }
}

class ModuleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
            $module = $event->getData();
            $form = $event->getForm();

            if ('task' == $module->getTitle()) {
                $form->add('task', TaskType::class);
            }
        });
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Module::class
        ]);
    }
}

这是我所做的,但没有任何改变。我写了这个,然后是写在ModuleType中的代码,但是我仍然有同样的错误消息……也许我不知道如何正确地将其插入ModuleType中。

我希望有人能解决。我知道我仍然可以直接在ModulingType中添加Tag和Task类型,但是我会有更多的集合。

非常感谢您的帮助,我希望我已经足够清楚了!

欢呼

2 个答案:

答案 0 :(得分:0)

您尝试过此操作吗

if (!is_null($module) && 'task' == $module->getTitle()) {
    $form->add('task', TaskType::class);
}

答案 1 :(得分:0)

所以实际上我找到了一个解决方案,我确实很封闭,但是遇到了一个新问题...

class ModuleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            $module = $event->getData();
            $form = $event->getForm();

            if (null != $event->getData()) {
                if ('task' == $module->getTitle()) {
                    $form->add('task', TaskType::class);
                }
            }
        });
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Module::class
        ]);
    }
}

这是解决方案,因此您可以看到它并不是那么复杂,但是在ModuleType中使用form事件会创建一个新问题。

在我的ModulingType中,添加一个选项

'allow_add' => true,

这个非常有用的工具允许在我的表单中自动添加“数据原型”,这样我就可以复制/粘贴一些此处(https://symfony.com/doc/current/form/form_collections.html)可用的jQuery行,然后可以复制或删除我的表单。但是,使用表单事件时,数据原型不会注册任何东西,因为它是在TaskType之前创建的。

因此,在花了数小时阅读有关Github的讨论并试图找到解决方案之后,我得出的结论是我必须创建一个TaskingType和TagingType,如下所示:

class TaskingType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('task', TaskType::class);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => Module::class
        ]);
    }
}

好的,这个解决方案不是完美的,我有一些代码重复。但是至少它只允许我只有3个集合:Page,Moduling和Module。

如果有人找到了一种优雅的方式来用一种表格来管理所有内容,而又不删除数据原型中可用的内容,请随时发贴:)