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

16
首先,我不确定这是 Sonata 问题还是 Symfony2 的问题。这是我第一次使用 Sf2 表单来编辑关系。以下是问题描述:
我有两个类,我们简称为 "Car" 和 "Wheel"。 "Car" 可以选择性地与 "Wheel" 建立一对一的关系(这只是一个示例,请跟着走...)。我已经设置了 SonataAdmin,使用 "sonata_type_admin" 将 "WheelAdmin" 内嵌到 "CarAdmin" 中,并尝试创建一辆汽车,而没有输入任何与 "Wheel" 相关的数据。
然而,在提交表单时(据我追踪,应该是 $form->bind()/$form->submit() 的某个位置),Symfony 和/或 Sonata 实例化了一个 "Wheel" 并尝试持久化它(其中所有的值均为 null)。由于 "Wheel" 具有一些非空约束条件,因此会抛出 DBALException 异常,指出无法插入一个具有 null 值的 "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 留空,则不应创建轮子,并且数据库中的 test_cars.leftFrontWheelId 应保持为 null

  • 如果输入了 car.wheel.diameter,则应创建一个轮子并将其链接到汽车上(现有配置似乎可以正常工作)

问题是:如何使系统按照上述方式运行?


4
我想我已经想出了一个解决方法,但一定有更好的方法!(在CarAdmin中,我添加了prePersist()和preUpdate()方法,检查一个轮子是否缺少直径。如果缺少直径(即无效),则$car->setLeftFrontWheel(null)会清除关系并阻止尝试持久化空轮子。) - caponica
Sonata很容易出现随机的错误,但是频率相当高。尝试检查新版本,因为你描述的行为在我的(旧)版本中不会发生。祝你好运! - likeitlikeit
嗯,我正在使用最新的(dev-master)版本的SonataAdmin,所以我不认为有更新的版本 :) 我对Symfony还不够熟练,不知道这是Symfony问题还是Sonata的问题,确定后我会提出相应的问题。 - caponica
这绝对是一个 SonataAdmin 的问题。 - likeitlikeit
2个回答

0

将您的Wheel表单字段更改为以下内容:

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

查看 by_reference 文档

如果不够用,可以在父级管理类中使用 prePersist 钩子,并管理存储哪个字段。例如:

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

0

这可能是由于缺少 '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))
    ;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接