我在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类型,但是我会有更多的集合。
非常感谢您的帮助,我希望我已经足够清楚了!
欢呼
答案 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。
如果有人找到了一种优雅的方式来用一种表格来管理所有内容,而又不删除数据原型中可用的内容,请随时发贴:)