Symfony 版本: 2.8.5
上下文:我有一个实体Restaurant
与一个实体具有 OneToOne 关系,该实体Coordinates
与与坐标信息相关的其他实体有多种关系。在我的后端,我创建了一个与Restaurant
实体相关的表单,其中包含一个与Coordinates
.
注意:我使用EasyAdminBundle来生成我的后端。
实体关系计划:
Restaurant
1 ________ 1 `Coordinates`
* ________ 1 `CoordinatesCountry`
1 ________ 1 `CoordinatesFR`
* ________ 1 `CoordinatesFRLane`
* ________ 1 `CoordinatesFRCity`
后端视图:
此时我尝试以下场景:
- 我创建了一家新餐厅,所以我第一次填写了相关字段的表格。我让
Coordinates
嵌套表单空白(空字段)。所以在表单提交后,会显示验证消息(见下图)。
我编辑了以前的表单,这次我填写了
Coordinates
嵌套表单的字段。表单提交后,新实体被水合,并在和Coordinates
之间创建关系。Restaurant
Coordinates
我再次编辑以前的表单,这次我清除了
Coordinates
嵌套表单的所有字段。验证未触发,我收到以下错误:
类型为“FBN\GuideBundle\Entity\CoordinatesFRCity”的预期参数,给出“NULL”
我准确地说,在CoordinatesFRType
(见下面的代码)中,第一次触发验证消息时,我必须使用empty_data
带有闭包的选项(如官方文档中所述)以在空数据的情况下建立一个新CoordinatesFR
实例(所有字段为空白) . 但是在这里,在这篇文章(由 Symfony 表单组件的创建者编写)中,解释了(参见 empty_data 和 datta mappers 段落)empty_data
仅在对象创建时调用。所以我认为这就是为什么我的验证在版本的情况下不再起作用的原因。
问题:为什么在编辑我的表单并清除所有嵌入表单时验证不再有效?
代码(只有必要的):
餐厅实体
use Symfony\Component\Validator\Constraints as Assert;
class Restaurant
{
/**
* @ORM\OneToOne(targetEntity="FBN\GuideBundle\Entity\Coordinates", inversedBy="restaurant", cascade={"persist"})
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
* @Assert\Valid()
*/
private $coordinates;
}
坐标实体
use Symfony\Component\Validator\Constraints as Assert;
class Coordinates
{
/**
* @ORM\ManyToOne(targetEntity="FBN\GuideBundle\Entity\CoordinatesCountry")
* @ORM\JoinColumn(nullable=false)
*/
private $coordinatesCountry;
/**
* @ORM\OneToOne(targetEntity="FBN\GuideBundle\Entity\CoordinatesFR", inversedBy="coordinates", cascade={"persist"})
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
* @Assert\Valid()
*/
private $coordinatesFR;
/**
* @ORM\OneToOne(targetEntity="FBN\GuideBundle\Entity\Restaurant", mappedBy="coordinates")
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
*/
private $restaurant;
}
坐标FR实体
use Symfony\Component\Validator\Constraints as Assert;
class CoordinatesFR extends CoordinatesISO
{
/**
* @ORM\ManyToOne(targetEntity="FBN\GuideBundle\Entity\CoordinatesFRLane")
* @ORM\JoinColumn(nullable=true)
* @Assert\NotBlank()
*/
private $coordinatesFRLane;
/**
* @ORM\ManyToOne(targetEntity="FBN\GuideBundle\Entity\CoordinatesFRCity")
* @ORM\JoinColumn(nullable=false)
* @Assert\NotBlank()
*/
private $coordinatesFRCity;
/**
* @ORM\OneToOne(targetEntity="FBN\GuideBundle\Entity\Coordinates", mappedBy="coordinatesFR")
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
*/
private $coordinates;
}
Easy Admin 配置(相当于 RestaurantType)
easy_admin:
entities:
Restaurant:
class : FBN\GuideBundle\Entity\Restaurant
form:
fields:
- { property: 'coordinates', type: 'FBN\GuideBundle\Form\CoordinatesType' }
坐标类型
class CoordinatesType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('CoordinatesCountry', EntityType::class, array(
'class' => 'FBNGuideBundle:CoordinatesCountry',
'property' => 'country',
))
->add('coordinatesFR', CoordinatesFRType::class)
;
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'FBN\GuideBundle\Entity\Coordinates',
));
}
}
坐标FRType
class CoordinatesFRType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('laneNum', TextType::class)
->add('coordinatesFRLane', EntityType::class, array(
'class' => 'FBNGuideBundle:CoordinatesFRLane',
'property' => 'lane',
'placeholder' => 'label.form.empty_value',
))
->add('laneName', TextType::class)
->add('miscellaneous', TextType::class)
->add('locality', TextType::class)
->add('metro', TextType::class)
->add('coordinatesFRCity', EntityType::class, array(
'class' => 'FBNGuideBundle:CoordinatesFRCity',
'property' => 'display',
'query_builder' => function (CoordinatesFRCityRepository $repo) {
return $repo->getAscendingSortedCitiesQueryBuilder();
},
'placeholder' => 'label.form.empty_value',
))
;
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'FBN\GuideBundle\Entity\CoordinatesFR',
// Ensures that validation error messages will be correctly displayed next to each field
// of the corresponding nested form (i.e if submission and CoordinatesFR nested form with all fields empty)
'empty_data' => function (FormInterface $form) {
return new CoordFR();
},
));
}
}