没有默认值的 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 次无法运行。

抱歉下面的混乱,'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 中工作正常