通过 Symfony 的表单生成器将新实体保存在按字段索引的 One-To-Many 关联中
Saving new entities in a One-To-Many association indexed by field through Symfony's form builder
我花了很长时间才把标题弄对,我还没有写问题:/
这里是:
我的菜单是从实体加载的。
为了允许用户将菜单翻译成多种语言,我创建了一个 Menu
实体和一个 LocalizedMenu
实体,通过 ManyToOne
关联与 Menu
关联。
在 this short guide 之后,我索引了与 LocalizedMenu->locale
字段的关联。这确保数据库中每个区域设置只有一个 LocalizedMenu
,并且该原则会覆盖现有的区域设置。
这是它的样子:
/**
* @ORM\Entity(repositoryClass="App\Repository\MenuRepository")
*/
class Menu
{
/**
* @Groups({"menu"})
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ...
/**
* References translated menus.
* @Groups({"localized_menus"})
*
* @ORM\OneToMany(
* targetEntity="LocalizedMenu"
* ,mappedBy="parentMenu"
* ,indexBy="locale"
* ,cascade={"persist", "remove"}
* )
* @ORM\OrderBy({"locale" = "ASC"})
*/
private $localizedMenus;
// ...
public function getLocalizedMenu($locale) {
if (!isset($this->localizedMenus[$locale])) {
return new LocalizedMenu($locale, $this);
}
return $this->localizedMenus[$locale];
}
public function addLocalizedMenu($localizedMenu): self
{
$this->localizedMenus[$localizedMenu->getLocale()] = $localizedMenu;
return $this;
}
}
LocalizedMenu
是一个包含用户翻译菜单字段的实体:
/**
* @ORM\Entity(repositoryClass="App\Repository\LocalizedMenuRepository")
*/
class LocalizedMenu
{
public function __construct($locale, $menu) {
$this->locale = $locale;
$this->parentMenu = $menu;
$this->parentMenu->addLocalizedMenu($this);
}
// region FIELDS
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @Groups({"localized_menu", "localized_menus"})
* @var $locale string
* @ORM\Column(
* type = "string"
* ,unique = true
* )
*/
private $locale = "";
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Column(
* type = "string",
* length = 75
* )
*/
private $title = "";
/**
* @Groups({"localized_menu", "localized_menus"})
* @var $description string Extra description for this menu item
* @ORM\Column(
* type = "text",
* name = "description"
* )
*/
private $description = "";
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Column(
* type = "datetime",
* name = "creation_date"
* )
* @Assert\DateTime()
*/
private $creationDate;
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Column(
* type = "datetime",
* name = "edit_date"
* )
* @Assert\DateTime()
*/
private $editDate;
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Column(type="text")
* @Assert\NotBlank(
* message = "Een menu item is een pagina die inhoud nodig heeft, vergeet dit niet"
* )
*/
private $content = "";
/**
* @var $parentMenu Menu Parent menu for this localized menu
*
* @Groups({"localized_menu", "localized_parent_menu"})
* @ORM\ManyToOne(
* targetEntity="Menu",
* inversedBy="localizedMenus"
* )
*/
private $parentMenu;
}
为了将这一切呈现给用户进行编辑,我创建了一个 MenyType
表单:
$builder
->add('localizedMenus', CollectionType::class, array(
'entry_type' => LocalizedMenuType::class,
"entry_options" => [
"choice_locale" => $options["choice_locale"]
],
'allow_add' => true,
'allow_delete' => true,
'required' => false
))
还有一个LocalizedMenuType
表格:
$builder
->add('title', TextType::class, array(
'label' => 'Titel',
'trim' => true
))
->add('description', TextareaType::class, array(
'label' => 'Omschrijving',
'trim' => true
))
->add('content', TextareaType::class, array(
'label' => 'Inhoud',
'trim' => true,
'attr' => array('class' => 'tinymce'),
'data' => " "
))
->add('locale', LocaleType::class, array(
"choice_translation_locale" => $options["choice_locale"]
))
;
我"think"这个逻辑是正确的,但是在使用javascript创建新的LocalizedMenu
表单后,我得到这个错误:
Too few arguments to function App\Entity\LocalizedMenu::__construct(),
0 passed in
/Users/robbievercammen/Projects/web/base/vendor/symfony/form/Extension/Core/Type/FormType.php
on line 136 and exactly 2 expected
我怎样才能让我的表单按照我的逻辑优雅地工作?
编辑 - 真正的问题
此处的错误消息并不是真正的错误。正如我之前所说,如果我删除构造函数参数,它会将记录保存到数据库中。即让学说使用关联将新的 LocalizedMenu
记录关联到 Menu
记录。这是它在数据库中的样子:
Menu
|id| //...
| 7| //...
LocalizedMenu
| id | locale | title | description | creation_date | edit_date
| content | parent_menu_id |
----
| 4 | nl | Contact | Contact | 2019-02-21 14:02:47 | 2019-02-21 14:02:47 |
Contact | NULL |
问题是 LocalizedMenu -> parent_menu_id
为 NULL。
出于某种原因,我的设置不会为 parent 菜单生成 ID。
下次从数据库中获取菜单时,$menu->getLocalizedMenus()
returns 一个空数组,因为它们没有正确关联。
在我提到的 guide 之后,这似乎是我告诉学说按 $localizedMenu -> locale
索引的唯一方法
您的 LocalizedMenu
构造函数需要两个参数 - $locale
和 $menu
。当 Symfony 为你新提交的数据实例化新的 LocalizedMenu
实例时,它会直接 new LocalizedMenu()
来填充它的数据。
如果您需要自定义如何为表单中的 new/dynamic 内容创建对象(例如,当您有构造函数参数时),您必须在 [=17] 上设置 empty_data
选项=] class.
有关详细信息,请参阅 https://symfony.com/doc/current/form/use_empty_data.html。
您的 LocalizedMenu
构造函数参数之一是菜单实例。此菜单实例需要作为必需选项传递给您的 LocalizedMenuType
。
class LocalizedMenuType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setRequired('menu');
$resolver->setAllowedTypes('menu', Menu::class);
$resolver->setDefault('empty_data', function (Options $options) {
return new LocalizedMenu($options['choice_locale'], $options['menu']);
});
}
}
我花了很长时间才把标题弄对,我还没有写问题:/
这里是:
我的菜单是从实体加载的。
为了允许用户将菜单翻译成多种语言,我创建了一个 Menu
实体和一个 LocalizedMenu
实体,通过 ManyToOne
关联与 Menu
关联。
在 this short guide 之后,我索引了与 LocalizedMenu->locale
字段的关联。这确保数据库中每个区域设置只有一个 LocalizedMenu
,并且该原则会覆盖现有的区域设置。
这是它的样子:
/**
* @ORM\Entity(repositoryClass="App\Repository\MenuRepository")
*/
class Menu
{
/**
* @Groups({"menu"})
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
// ...
/**
* References translated menus.
* @Groups({"localized_menus"})
*
* @ORM\OneToMany(
* targetEntity="LocalizedMenu"
* ,mappedBy="parentMenu"
* ,indexBy="locale"
* ,cascade={"persist", "remove"}
* )
* @ORM\OrderBy({"locale" = "ASC"})
*/
private $localizedMenus;
// ...
public function getLocalizedMenu($locale) {
if (!isset($this->localizedMenus[$locale])) {
return new LocalizedMenu($locale, $this);
}
return $this->localizedMenus[$locale];
}
public function addLocalizedMenu($localizedMenu): self
{
$this->localizedMenus[$localizedMenu->getLocale()] = $localizedMenu;
return $this;
}
}
LocalizedMenu
是一个包含用户翻译菜单字段的实体:
/**
* @ORM\Entity(repositoryClass="App\Repository\LocalizedMenuRepository")
*/
class LocalizedMenu
{
public function __construct($locale, $menu) {
$this->locale = $locale;
$this->parentMenu = $menu;
$this->parentMenu->addLocalizedMenu($this);
}
// region FIELDS
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @Groups({"localized_menu", "localized_menus"})
* @var $locale string
* @ORM\Column(
* type = "string"
* ,unique = true
* )
*/
private $locale = "";
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Column(
* type = "string",
* length = 75
* )
*/
private $title = "";
/**
* @Groups({"localized_menu", "localized_menus"})
* @var $description string Extra description for this menu item
* @ORM\Column(
* type = "text",
* name = "description"
* )
*/
private $description = "";
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Column(
* type = "datetime",
* name = "creation_date"
* )
* @Assert\DateTime()
*/
private $creationDate;
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Column(
* type = "datetime",
* name = "edit_date"
* )
* @Assert\DateTime()
*/
private $editDate;
/**
* @Groups({"localized_menu", "localized_menus"})
* @ORM\Column(type="text")
* @Assert\NotBlank(
* message = "Een menu item is een pagina die inhoud nodig heeft, vergeet dit niet"
* )
*/
private $content = "";
/**
* @var $parentMenu Menu Parent menu for this localized menu
*
* @Groups({"localized_menu", "localized_parent_menu"})
* @ORM\ManyToOne(
* targetEntity="Menu",
* inversedBy="localizedMenus"
* )
*/
private $parentMenu;
}
为了将这一切呈现给用户进行编辑,我创建了一个 MenyType
表单:
$builder
->add('localizedMenus', CollectionType::class, array(
'entry_type' => LocalizedMenuType::class,
"entry_options" => [
"choice_locale" => $options["choice_locale"]
],
'allow_add' => true,
'allow_delete' => true,
'required' => false
))
还有一个LocalizedMenuType
表格:
$builder
->add('title', TextType::class, array(
'label' => 'Titel',
'trim' => true
))
->add('description', TextareaType::class, array(
'label' => 'Omschrijving',
'trim' => true
))
->add('content', TextareaType::class, array(
'label' => 'Inhoud',
'trim' => true,
'attr' => array('class' => 'tinymce'),
'data' => " "
))
->add('locale', LocaleType::class, array(
"choice_translation_locale" => $options["choice_locale"]
))
;
我"think"这个逻辑是正确的,但是在使用javascript创建新的LocalizedMenu
表单后,我得到这个错误:
Too few arguments to function App\Entity\LocalizedMenu::__construct(), 0 passed in /Users/robbievercammen/Projects/web/base/vendor/symfony/form/Extension/Core/Type/FormType.php on line 136 and exactly 2 expected
我怎样才能让我的表单按照我的逻辑优雅地工作?
编辑 - 真正的问题
此处的错误消息并不是真正的错误。正如我之前所说,如果我删除构造函数参数,它会将记录保存到数据库中。即让学说使用关联将新的 LocalizedMenu
记录关联到 Menu
记录。这是它在数据库中的样子:
Menu
|id| //...
| 7| //...
LocalizedMenu
| id | locale | title | description | creation_date | edit_date
| content | parent_menu_id |
----
| 4 | nl | Contact | Contact | 2019-02-21 14:02:47 | 2019-02-21 14:02:47 |
Contact | NULL |
问题是 LocalizedMenu -> parent_menu_id
为 NULL。
出于某种原因,我的设置不会为 parent 菜单生成 ID。
下次从数据库中获取菜单时,$menu->getLocalizedMenus()
returns 一个空数组,因为它们没有正确关联。
在我提到的 guide 之后,这似乎是我告诉学说按 $localizedMenu -> locale
您的 LocalizedMenu
构造函数需要两个参数 - $locale
和 $menu
。当 Symfony 为你新提交的数据实例化新的 LocalizedMenu
实例时,它会直接 new LocalizedMenu()
来填充它的数据。
如果您需要自定义如何为表单中的 new/dynamic 内容创建对象(例如,当您有构造函数参数时),您必须在 [=17] 上设置 empty_data
选项=] class.
有关详细信息,请参阅 https://symfony.com/doc/current/form/use_empty_data.html。
您的 LocalizedMenu
构造函数参数之一是菜单实例。此菜单实例需要作为必需选项传递给您的 LocalizedMenuType
。
class LocalizedMenuType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setRequired('menu');
$resolver->setAllowedTypes('menu', Menu::class);
$resolver->setDefault('empty_data', function (Options $options) {
return new LocalizedMenu($options['choice_locale'], $options['menu']);
});
}
}