没有默认值的 symfony 3.4 形式的新实体中的空值
Null values in new entities in symfony 3.4 forms without defaults
我有一个实体 'administration',它有一个字段 'firstPeriod'。此字段不可为空(在 class 定义和数据库中均不可以),也不应该为空,因为此字段永远不应为空,应用程序将失败。
但是,此字段没有默认值,因为如果一个不经意的用户只是简单地提交表单而不更改默认值,就会出现混乱。用户必须在这里做出有意识的选择。已进行验证以确保该字段不为空且在可接受的范围内。
当我尝试呈现表单时,formbuilder 组件的 'propertyAccessor' 抛出此异常:
Type error: Return value of AppBundle\Entity\Administration::getFirstPeriod() must be of the type integer, null returned
看起来表单生成器试图在设置字段之前获取该字段的值,这当然会导致上述异常。
如何处理这种情况,以便在不向用户提供默认值的情况下呈现表单?
进一步说明:Null 不行,但我也不能提供任何默认值,用户必须做出有意识的决定。这同样适用于直接实例化该实体的任何开发人员。它必须在实体持久化之前提供,但我不能给出默认值,因为如果保留默认值,应用程序将在 13 次中有 12 次无法运行。
- 如果我在实体字段“?int”上允许空值,我实际上是在使一个字段可为空值 永远不应该为空值
- 如果我提供默认值,默认值可能会被盲目接受,这会导致应用程序进一步出现错误结果,而大多数用户很难发现这些错误结果。
- 我已经尝试在 formType 中设置 'empty_data => 0',但无济于事
抱歉下面的混乱,'Code Sample' 没有很好地处理这段代码
我的(截断的)实体:
namespace AppBundle\Entity;
使用Doctrine\ORM\Mapping作为ORM;
/**
* 行政
*
* @ORM\Table(名字="administration")
* @ORM\Entity(repositoryClass="AppBundle\Repository\AdministrationRepository")
*/
class 管理
{
/**
* @var 整数
*
* @ORM\Column(名称="id",类型="integer")
* @ORM\Id
* @ORM\GeneratedValue(策略="AUTO")
*/
私人 $id;
/**
* @var int
*
* @ORM\Column(name="first_period", type="smallint", nullable=false)
*/
private $firstPeriod;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @return int
*/
public function getFirstPeriod(): int
{
return $this->firstPeriod;
}
/**
* @param int $firstPeriod
*/
public function setFirstPeriod(int $firstPeriod): void
{
$this->firstPeriod = $firstPeriod;
}
}
我的(截断的)formType(尽我所能在此处格式化):
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstPeriod', null, [
'label' => 'First period'
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Administration::class
]);
}
public function getBlockPrefix()
{
return 'app_bundle_administration_type';
}
}
我的控制器:
namespace AppBundle\Controller\Admin;
class AdministrationController extends Controller
{
public function editAction(
EntityManager $em,
Router $router,
Security $security,
Session $session,
LoggerInterface $logger,
Request $request,
Administration $administration = null
): Response {
if ($administration === null) {
$new = true;
$administration = new Administration();
$pageTitle = 'New';
} else {
$new = false;
$pageTitle = 'Edit';
}
$breadcrumbs->add($crumbs);
$form = $this->createForm(AdministrationType::class, $administration);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var Administration $administration */
$administration = $form->getData();
try {
$em->persist($administration);
$em->flush();
} catch (ORMException $e) {
$logger->critical($e->getMessage());
$session->getFlashBag()->add('error', 'Database error.');
if ($new) {
return $this->redirectToRoute('administration_new');
} else {
return $this->redirectToRoute(
'administration_edit',
['administration' => $administration->getId()]
);
}
}
$session->getFlashBag()->add('success', 'Success!');
return $this->redirectToRoute('administration_index');
}
return $this->render(':Admin/Administration:edit.html.twig', [
'administrationForm' => $form->createView(),
'pageTitle' => $pageTitle
]);
}
}
我的验证:
AppBundle\Entity\Administration:
properties:
firstPeriod:
- NotBlank:
message: 'adm.firstperiod.empty'
- Range:
min: 1
max: 13
minMessage: 'adm.firstperiod.too_low'
maxMessage: 'adm.firstperiod.too_high'
由于 symfony 表单使用 属性 访问器,正如@Cerad 所说,您可以向表单添加一个未映射的字段,并且 get/set 表单事件中的字段,添加一个特定的方法来获取未初始化$first_period ...
示例代码可能是...
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\AdministrationRepository")
*/
class Administration
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="integer")
*/
private $first_period;
public function getId(): ?int
{
return $this->id;
}
public function getFirstPeriod(): int
{
return $this->first_period;
}
public function setFirstPeriod(int $first_period): self
{
$this->first_period = $first_period;
return $this;
}
public function getFirstPeriodOrNull(): ?int
{
return $this->first_period;
}
}
表格
<?php
namespace App\Form;
use App\Entity\Administration;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class AdministrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('first_period', null, [
'mapped' => false,
'required' => false,
])
->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
/** @var Administration */
$a = $event->getData();
$event->getForm()->get('first_period')->setData($a->getFirstPeriodOrNull());
})
->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
$f = $event->getForm()->get('first_period')->getData();
if (is_int($f)) {
/** @var Administration */
$a = $event->getData();
$a->setFirstPeriod($f);
}
});
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Administration::class,
]);
}
}
这在 Symfony 4.2 中工作正常
我有一个实体 'administration',它有一个字段 'firstPeriod'。此字段不可为空(在 class 定义和数据库中均不可以),也不应该为空,因为此字段永远不应为空,应用程序将失败。
但是,此字段没有默认值,因为如果一个不经意的用户只是简单地提交表单而不更改默认值,就会出现混乱。用户必须在这里做出有意识的选择。已进行验证以确保该字段不为空且在可接受的范围内。
当我尝试呈现表单时,formbuilder 组件的 'propertyAccessor' 抛出此异常:
Type error: Return value of AppBundle\Entity\Administration::getFirstPeriod() must be of the type integer, null returned
看起来表单生成器试图在设置字段之前获取该字段的值,这当然会导致上述异常。
如何处理这种情况,以便在不向用户提供默认值的情况下呈现表单?
进一步说明:Null 不行,但我也不能提供任何默认值,用户必须做出有意识的决定。这同样适用于直接实例化该实体的任何开发人员。它必须在实体持久化之前提供,但我不能给出默认值,因为如果保留默认值,应用程序将在 13 次中有 12 次无法运行。
- 如果我在实体字段“?int”上允许空值,我实际上是在使一个字段可为空值 永远不应该为空值
- 如果我提供默认值,默认值可能会被盲目接受,这会导致应用程序进一步出现错误结果,而大多数用户很难发现这些错误结果。
- 我已经尝试在 formType 中设置 'empty_data => 0',但无济于事
抱歉下面的混乱,'Code Sample' 没有很好地处理这段代码
我的(截断的)实体:
namespace AppBundle\Entity;
使用Doctrine\ORM\Mapping作为ORM;
/** * 行政 * * @ORM\Table(名字="administration") * @ORM\Entity(repositoryClass="AppBundle\Repository\AdministrationRepository") */ class 管理 { /** * @var 整数 * * @ORM\Column(名称="id",类型="integer") * @ORM\Id * @ORM\GeneratedValue(策略="AUTO") */ 私人 $id;
/**
* @var int
*
* @ORM\Column(name="first_period", type="smallint", nullable=false)
*/
private $firstPeriod;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* @return int
*/
public function getFirstPeriod(): int
{
return $this->firstPeriod;
}
/**
* @param int $firstPeriod
*/
public function setFirstPeriod(int $firstPeriod): void
{
$this->firstPeriod = $firstPeriod;
}
}
我的(截断的)formType(尽我所能在此处格式化):
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('firstPeriod', null, [
'label' => 'First period'
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Administration::class
]);
}
public function getBlockPrefix()
{
return 'app_bundle_administration_type';
}
}
我的控制器:
namespace AppBundle\Controller\Admin;
class AdministrationController extends Controller
{
public function editAction(
EntityManager $em,
Router $router,
Security $security,
Session $session,
LoggerInterface $logger,
Request $request,
Administration $administration = null
): Response {
if ($administration === null) {
$new = true;
$administration = new Administration();
$pageTitle = 'New';
} else {
$new = false;
$pageTitle = 'Edit';
}
$breadcrumbs->add($crumbs);
$form = $this->createForm(AdministrationType::class, $administration);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var Administration $administration */
$administration = $form->getData();
try {
$em->persist($administration);
$em->flush();
} catch (ORMException $e) {
$logger->critical($e->getMessage());
$session->getFlashBag()->add('error', 'Database error.');
if ($new) {
return $this->redirectToRoute('administration_new');
} else {
return $this->redirectToRoute(
'administration_edit',
['administration' => $administration->getId()]
);
}
}
$session->getFlashBag()->add('success', 'Success!');
return $this->redirectToRoute('administration_index');
}
return $this->render(':Admin/Administration:edit.html.twig', [
'administrationForm' => $form->createView(),
'pageTitle' => $pageTitle
]);
}
}
我的验证:
AppBundle\Entity\Administration:
properties:
firstPeriod:
- NotBlank:
message: 'adm.firstperiod.empty'
- Range:
min: 1
max: 13
minMessage: 'adm.firstperiod.too_low'
maxMessage: 'adm.firstperiod.too_high'
由于 symfony 表单使用 属性 访问器,正如@Cerad 所说,您可以向表单添加一个未映射的字段,并且 get/set 表单事件中的字段,添加一个特定的方法来获取未初始化$first_period ...
示例代码可能是...
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="App\Repository\AdministrationRepository")
*/
class Administration
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="integer")
*/
private $first_period;
public function getId(): ?int
{
return $this->id;
}
public function getFirstPeriod(): int
{
return $this->first_period;
}
public function setFirstPeriod(int $first_period): self
{
$this->first_period = $first_period;
return $this;
}
public function getFirstPeriodOrNull(): ?int
{
return $this->first_period;
}
}
表格
<?php
namespace App\Form;
use App\Entity\Administration;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class AdministrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('first_period', null, [
'mapped' => false,
'required' => false,
])
->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
/** @var Administration */
$a = $event->getData();
$event->getForm()->get('first_period')->setData($a->getFirstPeriodOrNull());
})
->addEventListener(FormEvents::POST_SUBMIT, function(FormEvent $event) {
$f = $event->getForm()->get('first_period')->getData();
if (is_int($f)) {
/** @var Administration */
$a = $event->getData();
$a->setFirstPeriod($f);
}
});
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Administration::class,
]);
}
}
这在 Symfony 4.2 中工作正常