通过Symfony的表单构建器将按字段索引的一对多关联中的新实体保存

问题描述 投票:0回答:1

我花了很长时间才能获得正确的标题,而我还没有写下这个问题:/

这里是:我的菜单是从实体加载的。为了让用户将菜单翻译成多种语言,我创建了一个Menu实体和一个LocalizedMenu实体,该实体通过Menu关联与ManyToOne相关联。

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"]
        ))
    ;

我“认为”这个逻辑是正确的,但在使用javascript创建新的LocalizedMenu表单后,我收到此错误:

函数App \ Entity \ LocalizedMenu :: __ construct()的参数太少,0在第136行的/Users/robbievercammen/Projects/web/base/vendor/symfony/form/Extension/Core/Type/FormType.php中传递2预期

如何让我的表单与我的逻辑优雅地工作?

编辑 - 真正的问题

错误消息不是真正的错误。正如我之前所说,如果删除构造函数参数,它会将记录保存到数据库中。即让教条使用该关联将新的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。出于某种原因,我的设置不会为父菜单生成ID。下次从数据库中获取菜单时,$menu->getLocalizedMenus()返回一个空数组,因为它们没有正确关联。

在我提到的guide之后,这看起来似乎是我能用$localizedMenu -> locale告诉学说索引的唯一方法

php symfony doctrine-orm associations symfony-forms
1个回答
1
投票

你的LocalizedMenu构造函数需要两个参数--$locale$menu。当Symfony为您新提交的数据实例化新的LocalizedMenu实例时,它会直接填充new LocalizedMenu()来填充其数据。

如果需要自定义如何为表单中的新/动态内容创建对象(例如,当您有构造函数参数时),则必须在empty_data类上设置LocalizedMenuType选项。

有关更多信息,请参阅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']);
        });
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.