在普通表单类上使用奏鸣曲字段类型
Posted
技术标签:
【中文标题】在普通表单类上使用奏鸣曲字段类型【英文标题】:Use Sonata Field Type on normal Form Class 【发布时间】:2015-01-03 08:33:02 【问题描述】:我正在尝试在首页插入自定义奏鸣曲表单字段类型,而不是在 SonataAdmin 中,如下所示:
$form = $this->createFormBuilder($content)
->add('titleEs', 'text', array('required' => true, 'label' => 'label.title.spanish', 'attr' => array('class' => 'col-xs-12 form-control input-lg')))
->add('contentEs', 'ckeditor', array('required' => true,'label' => 'label.content.spanish', 'attr' => array('class' => 'col-xs-12')))
->add('titleEn', 'text', array('required' => true,'label' => 'label.title.english', 'attr' => array('class' => 'col-xs-12 form-control input-lg')))
->add('contentEn', 'ckeditor', array('required' => true, 'label' => 'label.content.english', 'attr' => array('class' => 'col-xs-12')))
->add('header', 'sonata_type_model', array('required' => true,'label' => 'label.content.headerImage'), array('link_parameters' => array('context' => 'content/front', 'size' => 'big')))
//->add('coverImage', 'sonata_type_model_list', array('required' => true,'label' => 'label.content.coverImage'), array('link_parameters' => array('context' => 'content/front', 'size' => 'small')))
//->add('sliderImage', 'sonata_type_model_list', array('required' => false,'label' => 'label.content.sliderImage'), array('link_parameters' => array('context' => 'content/slider', 'size' => 'normal')))
->getForm();
但是当我执行它时,它会抛出一个错误:
Catchable Fatal Error: Argument 1 passed to Sonata\AdminBundle\Form\ChoiceList\ModelChoiceList::__construct() must implement interface Sonata\AdminBundle\Model\ModelManagerInterface, null given
如果 Sonata 表单字段类型是服务,我不明白为什么 Symfony 会抛出该错误。
【问题讨论】:
【参考方案1】:感谢@gabtzi 的回答,我在 Sonata Admin 的源代码中四处寻找,并提出了一个非常相似的解决方案。假设我们有两个实体Movie
和Genre
,它们之间是多对多关系(Movie
是拥有方),Symfony 4 和 Sonata Admin 3.x 中的解决方案将如下所示:
<?php
namespace App\Form\Type;
use App\Entity\Movie;
use App\Entity\Genre;
use Symfony\Component\Form\AbstractType;
use Sonata\AdminBundle\Form\Type\ModelType;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class MovieType extends AbstractType
private $container;
public function __construct(ContainerInterface $container)
$this->container = $container;
public function buildForm(FormBuilderInterface $builder, array $options)
$builder
// add fields
->add('genres', ModelType::class, [
'multiple' => true,
'class' => Genre::class,
'property' => 'name', // assuming Genre has property name
'model_manager' => $this->container->get('sonata.admin.manager.orm'),
'by_reference' => false
])
// add more fields
;
public function configureOptions(OptionsResolver $resolver)
$resolver->setDefaults(array(
'data_class' => Movie::class,
));
这是一个非常基本的示例,但应该给出如何进一步进行的想法。重要的是:
如果您使用自动装配,则不必将表单类型注册为服务。检查autowire
是否在您的config/services.yaml
中设置为true
。更多详细信息请阅读官方documentation;
将ContainerInterface
传递给构造函数获取容器;
你不再使用sonata_type_model
。你必须使用ModelType::class
。注意使用说明;
M2M关系可以设置mutiple
为true
,否则默认为false
;
您必须将实体类传递给class
——在本例中为Movie::class
;
您可以指定property
以使用Genre
的某些属性。如果您在实体类中定义了__toString
方法,则不必声明这一点。然后会使用这个方法的返回值;
最重要的是:现在你有了容器,获取服务sonata.admin.manager.orm
并将它传递给model_manager
。没有这个,一切都会落入水中。
然而,我还没有设法显示按钮 + 添加新。值得一提的是,相关属性的管理类必须存在并且可以访问(设置了适当的权限)——在这种情况下,GenreAdmin
是必需的,否则理论上该按钮甚至无法工作。
【讨论】:
【参考方案2】:我刚刚遇到了与您相同的问题,但由于这是我在 Google 中遇到的第一个热门问题,因此我只是发布了我为解决该问题所做的工作。
我假设您在控制器中动态创建表单。如果不是,您需要将您的类声明为服务并向其注入sonata.admin.manager.orm
服务。
$form = $this->createFormBuilder()
->add('<name_of_field>', 'sonata_type_model', array(
'multiple' => true,
'class' => <className>::class,
'property' => '<propertyName>',
'model_manager' => $this->get('sonata.admin.manager.orm')
))
;
之后它为我正确呈现,就像在管理上下文中一样。
【讨论】:
你更喜欢$this->container->get
而不是$this->get
?假设 ContainerInterface $this->container
是在构造函数中创建的。
自从我发布这个答案已经有 2 年了,我真的不记得为什么我没有$this->container->get
但你可能是对的,所以我会编辑答案。跨度>
是的,这是旧答案,但非常有价值。我只是用一些扩展的解释写了我的版本。功劳归于您,因为您提示了 model_manager
的用途。
看到你的代码我顿悟了。这对我来说是正确的,因为正如我提到的,我在控制器中使用它。当时在我的 symfony 版本上的控制器可以使用$this->get('service')
通过它的别名直接检索服务。所以这个用例非常有效。阅读您的答案,我可以看到它在 FormType 类中。实际上,您可以在 Type 上注入容器,然后使用$this->container->get('service')
,但如果我没记错的话,symfony 不鼓励这样做,因为它会产生紧密耦合。他们总是建议只注入需要的服务
是的,我的答案是如何在 FormType 类中使用它。这就是问题的标题。这个解决方案是我现在能想到的最好的解决方案。另一方面,在控制器中创建表单不是 DRY。如果您在其他地方需要它,则必须重复代码,因为它不可重复使用。【参考方案3】:
sonata_type_model 需要知道什么样的实体与你的领域相关。 如果您在管理类中定义它,奏鸣曲使用自己的内部方法来检查关系。 因此,如果您在 admin 之外定义它,则将其设置为 null 而不是 entity
【讨论】:
好的,那我怎么指定类呢?以上是关于在普通表单类上使用奏鸣曲字段类型的主要内容,如果未能解决你的问题,请参考以下文章
使用更多表单字段扩展奏鸣曲用户包,获取无法加载类型“Application\Sonata\UserBundle\Form\RegistrationType”