阻止 SonataAdmin / Symfony2 使用 sonata_type_admin 嵌入式管理员创建空对象

Posted

技术标签:

【中文标题】阻止 SonataAdmin / Symfony2 使用 sonata_type_admin 嵌入式管理员创建空对象【英文标题】:Stop SonataAdmin / Symfony2 from creating empty objects with sonata_type_admin embedded admins 【发布时间】:2013-08-12 00:33:01 【问题描述】:

首先,我不确定这是奏鸣曲问题还是 Symfony2 问题,这是我第一次使用 Sf2 表单来编辑关系。

问题来了:

我有两个课程,我们称它们为旧的最爱:CarWheel。 Car 与 Wheel 具有可选的一对一关系(仅作为示例,随它去吧……)。我已经使用sonata_type_admin 嵌入了WheelAdminCarAdmin 类设置了SonataAdmin,并尝试在不输入任何Wheel 数据的情况下创建汽车。

但是,在提交时(据我所知,在 $form->bind()/$form->submit() 中的某个位置)Symfony 和/或 Sonata 正在实例化一个 Wheel 并尝试将其持久化(及其所有值为null)。由于 Wheel 有一些非空约束,这会引发 DBALException,抱怨您无法插入具有空值的 Wheel。

这很顽皮,我想阻止它发生。如果我不输入 Wheel 的任何详细信息,我不希望幻影 Wheel 威胁我的代码和数据库。我期望的是,如果我没有输入任何数据,就没有任何东西可以插入/保留,所以它就不用管了。但这不是正在发生的事情......有什么想法可以将其驯服为明智的吗?


这是长版本,包含代码块和所有内容:

首先是 ORM 定义:

# MyNS\MyBundle\Resources\Config\Doctrine\Car.orm.yml
MyNS\MyBundle\Entity\Car:
  type: entity
  repositoryClass: MyNS\MyBundle\Entity\Repositories\CarRepository
  table: test_cars
  id:
    id:
      type:                     integer
      generator:                 strategy: AUTO 
  fields:
    color:
      type:                     string
      length:                   50
    owner:
      type:                     string
      length:                   50
      nullable:                 true
  oneToOne:
    leftFrontWheel:
      targetEntity:             Wheel
      cascade:                  [ persist ]
      joinColumn:
        name:                   leftFrontWheelId
        referencedColumnName:   id


# MyNS\MyBundle\Resources\Config\Doctrine\Wheel.orm.yml
MyNS\MyBundle\Entity\Wheel:
  type: entity
  repositoryClass: MyNS\MyBundle\Entity\Repositories\WheelRepository
  table: test_wheels
  id:
    id:
      type:                     integer
      generator:                 strategy: AUTO 
  fields:
    diameter:
      type:                     integer
      length:                   5

然后是 SonataAdmin 类:

namespace MyNS\MyBundle\Admin

use ...

class CarAdmin extends Admin

    protected function configureFormFields(FormMapper $formMapper)
    
        $formMapper
            ->add('color',              null, array('required' => true))
            ->add('owner',              null, array('required' => false))
            ->add('leftFrontWheel',     'sonata_type_admin', array('delete' => false))
        ;
    

    protected function configureListFields(ListMapper $listMapper)  ... 

namespace MyNS\MyBundle\Admin;

use ...

class WheelAdmin extends Admin

    protected function configureFormFields(FormMapper $formMapper)
    
        $formMapper
            ->add('diameter',   null,   array('required' => false))
        ;
    

    protected function configureListFields(ListMapper $listMapper)  ... 

最后是 admin.yml 条目:

services:
    sonata.admin.car:
        class: MyNS\MyBundle\Admin\CarAdmin
        tags:
            -  name: sonata.admin, manager_type: orm, label: "Car" 
        arguments:
            - ~
            - MyNS\MyBundle\Entity\Car
            - 'SonataAdminBundle:CRUD'
        calls:
            - [ setTranslationDomain, [MyNS\MyBundle]]
    sonata.admin.wheel:
        class: MyNS\MyBundle\Admin\WheelAdmin
        tags:
            -  name: sonata.admin, manager_type: orm, label: "Wheel" 
        arguments:
            - ~
            - MyNS\MyBundle\Entity\Wheel
            - 'SonataAdminBundle:CRUD'
        calls:
            - [ setTranslationDomain, [MyNS\MyBundle]]

预期/要求的行为:

显示一个包含三个字段的表单:

car.color(必填) car.owner(可选) car.wheel.diameter(可选)

如果 car.wheel.diameter 留空,则不应创建 Wheel,test_cars.leftFrontWheelId 应在数据库中保留 null

如果输入了 car.wheel.diameter,则应创建一个 Wheel 并将其链接到 Car(这似乎与现有配置一起工作)

问题:我如何让这个系统像上面那样运行?

【问题讨论】:

我想我已经设法解决这个问题,但一定有更好的方法! (在 CarAdmin 中,我添加了 prePersist() 和 preUpdate() 方法来检查车轮是否缺少其直径。如果是(即无效),则$car->setLeftFrontWheel(null) 清除关系并停止它尝试保留空车轮。 ) 奏鸣曲很容易出现随机出现的错误,但相当频繁。尝试检查新版本,因为您描述的行为不会发生在我的(旧)版本中。祝你好运! 嗯,我使用的是 SonataAdmin 的最新(dev-master)版本,所以我认为没有更新的版本 :) Symfony 经验不足,还不知道这是否是 Symfony 问题或奏鸣曲,当我确定我会提出适当的问题时。 这肯定是 SonataAdmin 的问题。 【参考方案1】:

将您的 Wheel 表单字段更改为:

$formMapper
    // ...
    ->add('leftFrontWheel', 'sonata_type_admin', array(
        'delete' => false,
        'by_reference => true,
        'required' => false,
    ))
    // ...

by_referencedocumentation

如果还不够,请在您的父管理类中使用 prePersist 挂钩并管理您存储的字段。即:

// Fire on submit, before the object persisting
public function prePersist($object)
    if ($wheel = $object->getLeftFrontWheel()) 
        if (!$wheel->getYourNonNullableField()) 
            $object->setLefTFrontWheel(null);
        
    

【讨论】:

【参考方案2】:

可能是因为缺少'required' => false,不是吗?

protected function configureFormFields(FormMapper $formMapper) 
    $formMapper
        ->add('color',              null, array('required' => true))
        ->add('owner',              null, array('required' => false))
        ->add('leftFrontWheel',     'sonata_type_admin', array('required' => false, 'delete' => false))
    ;

【讨论】:

以上是关于阻止 SonataAdmin / Symfony2 使用 sonata_type_admin 嵌入式管理员创建空对象的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法阻止 Symfony2 为某些路径发送会话 cookie?

Symfony2 - 如何阻止 Form->handleRequest 清空帖子数据中不存在的字段

symfony2 多对多奏鸣曲 admin model_list

Sonata Admin 编辑表单多对多不起作用 - symfony2.1.6

在 Symfony 2 项目的 Sonata Admin 中使用自定义视图页面进行图像预览

Sonata Admin:isGranted() 在 Admin 类中总是返回 true,但在模板中返回正确的布尔值