我们迁移错误了吗?从 Symfony 2.7 迁移到 4.0 的表单中没有进行验证

Did we migrate incorrectly? Validation not happening in forms migrated from Symfony 2.7 to 4.0

在从 Symfony 2.7 迁移到 4.0 的代码中,验证不再发生在我的表单上,允许错误数据通过并导致违反 Doctrine 约束

我是 Symfony 的新手,被要求将 2.7 应用程序迁移到 4.0。我在步骤 (2.7->2.8->3.x->4.0) 中完成了此操作,并在问题出现时解决了问题,但在此过程中出现的问题是自动表单验证。在原始版本中,如果我尝试创建一个新用户并将字段留空,它会正确标记这些字段并在 UI 中弹出“不能为空”消息。现在,它让那些过去,直到它尝试写入数据库,此时 Doctrine barfs 因为违反了数据库非空约束。

我试图找出我做错了什么,但我并没有牢牢掌握表单创建过程和语法是如何改变的。所有关于表单验证的示例文档都采用 createFormBuilder() 方法,而我现有的所有代码都使用 createForm()。我错过了什么?

这是与显示我希望触发验证警告的@Assert 语句的表单关联的用户对象的一部分:

/**
 * @ORM\Table(name="users")
 * @ORM\Entity(repositoryClass="Domain\CoreBundle\Repository\UserRepository")
 * @ORM\HasLifecycleCallbacks()
 * @UniqueEntity(fields="email", message="This email address is already in usage")
 * @UniqueEntity(fields="username", message="This username is already in usage")
 */
class User extends BaseUser implements JsonSerializable
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @Assert\NotBlank(message="Email should not be empty")
     * @Assert\Email(strict=true)
     * @Assert\Length(max=150, maxMessage="Email should be less than {{ limit }} characters")
     */
    protected $email;

    /**
     * @Assert\NotBlank(message="Username should not be empty")
     * @Assert\Regex(
     *     pattern = "/^\d*[a-zA-Z][ a-zA-Z0-9\!\@\#$\%\^\&\-\_\=\+\~\?\.]*$/i",
     *     message = "Username should include at least one letter"
     * )
     */
    protected $username;

    /**
     * @var string
     *
     * @Assert\NotBlank(message="First name should not be empty")
     * @ORM\Column(name="first_name", type="string", length=255)
     */
    protected $firstName;

    /**
     * @var string
     *
     * @Assert\NotBlank(message="Last name should not be empty")
     * @ORM\Column(name="last_name", type="string", length=255)
     */
    protected $lastName;

    (rest of code omitted for conciseness)

这是来自控制器的 addNew 操作(AdministratorController 扩展了 UserController):

    /**
     * Add new administrator
     *
     * @param Request $request
     *
     * @return Response
     */
    public function addNewAction(Request $request)
    {
        $company = $this->getCurrentCompany();
        $form = $this->createForm(AddAdministratorType::class, null,
          array('current_user'=> $this->user, 'restricted_admin'=>$this->getRestrictedAdmin(), 'company'=>$company));

        if ($request->getMethod() == Request::METHOD_POST) {
            $form->handleRequest($request);

            // check if the user already exists
            $userManager = $this->get('fos_user.user_manager');
            $user = $form->getData();

            $oldUser = $userManager->findUserByUsername($user['email']);
            if ($oldUser)
            {
                $alreadyExists = false;
                if ($user["isSuperAdmin"] &&$oldUser->isGrantedSuperAdmin())
                    $alreadyExists = true;
                if ($user["isCompanyAdmin"] && $oldUser->isGranted(UserRepository::ROLE_COMPANY_ADMIN, $company))
                    $alreadyExists = true;
                if (!$user["isCompanyAdmin"] && !$user["isSuperAdmin"] && $oldUser->isGranted(UserRepository::ROLE_ADMIN,$company))
                    $alreadyExists = true;

                if ($alreadyExists)
                    $form->get('email')->addError(new FormError('This email address is already in use'));
            }


            if ($form->isValid()) {
                $user = $form->getData();


                if ($oldUser) // if the user already exists, we just need to add the role
                {
                    if (!$this->getUser()->isGrantedSuperAdmin() && 
                                !in_array($company->getId(), array_map(function($x){return $x->getId();}, $oldUser->getCompaniesWithRole())))
                    {
                        // the user isn't currently in this company and the user adding the role
                        // isn't a super admin, so we have to create a shadow user entry to hide 
                        // the real user info from other in the company until the user logs into
                        // the company
                        $oldShadow=$this->em->getRepository(ShadowUser::class)->findOneBy(array("user" => $oldUser, "company"=>$company));
                        if (!$oldShadow)
                        {
                            $shadow = new ShadowUser();
                            $shadow->setUser($oldUser);
                            $shadow->setFirstName($user["firstName"]);
                            $shadow->setLastName($user["lastName"]);
                            $shadow->setCompany($company);
                            $shadow->setIsVydioUsed($user["isVydioUsed"]);
                            $shadow->setVydioRoomLink($user["vydioRoomLink"]);
                            $shadow->setCreatedDate(new \DateTime());
                            $this->em->persist($shadow);
                        }
                    }

                    if ($user["isSuperAdmin"])
                    {
                        $oldUser->addMyRole(UserRepository::ROLE_SUPER_ADMIN, $company);
                        $this->get('pp_mailer')->onAddNewRole($oldUser,UserRepository::ROLE_SUPER_ADMIN, $company );
                    }
                    if ($user["isCompanyAdmin"])
                    {
                        $oldUser->addMyRole(UserRepository::ROLE_COMPANY_ADMIN, $company);
                        $this->get('pp_mailer')->onAddNewRole($oldUser,UserRepository::ROLE_COMPANY_ADMIN, $company );
                    }
                    if (!$user["isSuperAdmin"] && !$user["isCompanyAdmin"])
                    {
                        $oldUser->addMyRole(UserRepository::ROLE_ADMIN, $company);
                        $this->get('pp_mailer')->onAddNewRole($oldUser,UserRepository::ROLE_ADMIN, $company );
                    }

                    $programRepo = $this->em->getRepository(ProgramUser::class);
                    foreach($user["programs"] as $program)
                    {
                        $oldRelation = $programRepo->findOneBy(array("user"=> $oldUser, "program"=>$program));
                        if (!$oldRelation)
                        {
                            $relation = new ProgramUser();
                            $relation->setUser($oldUser);
                            $relation->setProgram($program);
                            $relation->setCompany($company);
                            $this->em->merge($relation);
                        }
                    }
                    $this->em->persist($oldUser);
                    $this->em->flush();

                }
                else
                {
                    $newUser = new User();
                    $newUser->setPassword($this->get('domain_core_service')->generatePassword());
                    $newUser->setDefaultCompany($company);
                    $newUser->setFirstName($user["firstName"]);
                    $newUser->setLastName($user["lastName"]);
                    $newUser->setEmail($user["email"]);
                    $newUser->setUsername($user["email"]);
                    $newUser->setEnabled($user["enabled"]);
                    $newUser = $this->em->getRepository('DomainCoreBundle:User')->addUserInSystem($userManager, $newUser);

                    $token = $this->get('domain_core_service')->generateToken();
                    $newUser->setConfirmationToken($token);
                    if ($user["isSuperAdmin"])
                    {
                        $newUser->addMyRole(UserRepository::ROLE_SUPER_ADMIN, $company);
                        $this->get('pp_mailer')->onAddNewUser($newUser,UserRepository::ROLE_SUPER_ADMIN, $company );
                    }
                    if ($user["isCompanyAdmin"])
                    {
                        $newUser->addMyRole(UserRepository::ROLE_COMPANY_ADMIN, $company);
                        $this->get('pp_mailer')->onAddNewUser($newUser,UserRepository::ROLE_COMPANY_ADMIN, $company );
                    }
                    if (!$user["isSuperAdmin"] && !$user["isCompanyAdmin"])
                    {
                        $newUser->addMyRole(UserRepository::ROLE_ADMIN, $company);
                        $this->get('pp_mailer')->onAddNewUser($newUser,UserRepository::ROLE_ADMIN, $company );
                    }

                    foreach($user["programs"] as $program)
                    {
                        $relation = new ProgramUser();
                        $relation->setUser($newUser);
                        $relation->setProgram($program);
                        $relation->setCompany($company);
                        $this->em->merge($relation);
                    }

                    $this->em->persist($newUser);
                    $this->em->flush();

                }
                return $this->redirect($this->generateUrl('domain_admin_show_all_administrators_page'));
            }
        }

        return $this->render(
            'DomainAdminBundle:Administrators:add-new.html.twig',
            array(
                 'form'                => $form->createView(),
                 'page_title'          => 'Add New Administrator',
                 'currentSidebar'      => $this->currentSideBar,
                 'currentSidebarItem'  => $this->currentSidebarItem,
            )
        );
    }

以及表单的 twig 文件:

{% extends 'DomainAdminBundle::base-admin-layout.html.twig' %}
{% import '::widgets/form_errors.html.twig' as form_custom_errors %}
{% import '::widgets/label.html.twig' as form_custom_labels %}
{% block title %} My Application| {{ page_title }} {% endblock %}

{% block javascripts %}
    {{ parent() }}

    <script type="text/javascript" src="{{ asset('assets/scripts/admin-add-new.js') }}"></script>
{% endblock %}

{% block stylesheets %}
    {{ parent() }}

    <link rel="stylesheet" type="text/css" href="{{ asset('assets/styles/admin-add-new.css') }}">
{% endblock %}

{% block admin_main_content %}
    <div class="content-block administrator-controller" ng-controller="AdministratorController">
        <div class="content-title-bar">
            <div class="pull-left">
                <h2>{{ page_title }}</h2>
            </div>
        </div>

        <div class="content-block" ng-controller="AdminController">
            {{ form_start(form, {"attr": { "action":"{{ path('domain_admin_add_new_administrator_page') }}", 'enctype': 'multipart/form-data', "method":"POST", "novalidate":"novalidate", "autocomplete":"off", "class":"form-horizontal add-user", "ng-submit":"disableAddButton()" }}) }}
                <div class="base-box info-block">
                    <div class="control-group">
                        <div class="controls">
                            {{ form_widget(form.enabled) }}
                            {{ form_label(form.enabled, 'Active') }}
                        </div>
                    </div>
                    {% if app.user.isGrantedSuperAdmin() %}
                        <div class="control-group">
                            <div class="controls">
                                {% set companyAdminValue = form.isCompanyAdmin.vars.checked ? 'true' : 'false' %}
                                {{ form_widget(form.isCompanyAdmin, { 'attr':{ 'ng-model':'adminForm.isCompanyAdmin', 'ng-init': 'adminForm.isCompanyAdmin=' ~ companyAdminValue } }) }}
                                {{ form_label(form.isCompanyAdmin, 'Company Admin') }}
                                {% set superAdminValue = form.isSuperAdmin.vars.checked ? 'true' : 'false' %}
                                {{ form_widget(form.isSuperAdmin, { 'attr':{ 'ng-model':'adminForm.isSuperAdmin', 'ng-init': 'adminForm.isSuperAdmin=' ~ superAdminValue } }) }}
                                {{ form_label(form.isSuperAdmin, 'Super Admin') }}
                            </div>
                        </div>
                    {% endif %}
                    <div class="control-group" ng-init="initMultiSelect(true)">
                        {{ form_custom_labels.widget(form.programs) }}
                        <div class="controls">
                            {{ form_widget(form.programs) }}
                            {{ form_custom_errors.widget(form.programs) }}
                        </div>
                    </div>
                    <div class="control-group">
                        {{ form_custom_labels.widget(form.firstName) }}
                        <div class="controls">
                            {{ form_widget(form.firstName) }}
                            {{ form_custom_errors.widget(form.firstName) }}
                        </div>
                    </div>
                    <div class="control-group">
                        {{ form_custom_labels.widget(form.lastName) }}
                        <div class="controls">
                            {{ form_widget(form.lastName) }}
                            {{ form_custom_errors.widget(form.lastName) }}
                        </div>
                    </div>
                    <div class="control-group">
                        {{ form_custom_labels.widget(form.email) }}
                        <div class="controls">
                            {{ form_widget(form.email) }}
                            {{ form_custom_errors.widget(form.email) }}
                        </div>
                    </div>
                    <div class="control-group">
                        {{ form_custom_labels.widget(form.timezone) }}
                        <div class="controls">
                            {{ form_widget(form.timezone) }}
                            {{ form_custom_errors.widget(form.timezone) }}
                        </div>
                    </div>
                </div>
                <div class="text-right">
                    <button id="add-admin-submit" type="submit" class="btn btn-do" ng-disabled="isDisabled">Add new administrator</button>
                    <a href="{{ path('domain_admin_show_all_administrators_page') }}" class="btn btn-redo" ng-disabled="isDisabled">Cancel</a>
                </div>
                {{ form_rest(form) }}
            {{ form_end(form) }}
        </div>
    </div>
{% endblock %}

如果我将所有字段留空并单击 "Add New Administrator" 它不会将它们标记为空白,而是将它们传递给 Doctrine。预期的行为是它在 UI 标记它们并且不尝试将它们写入数据库。

我确定我已经对 Symfony 制造了多起犯罪,因为我已经加快了学习曲线,所以放轻松。现在我只是想解决这个狭隘的问题;重构以更优雅地适应 Symfony 4 将不得不再等一天。

谢谢!

您似乎想根据您请求的数据验证用户 class。

您是否在表单类型 class 中设置了 data_class 选项?

如果您想使用来自另一个 class 的验证规则(因为您使用一些 @Assert* 注释标记了您的属性),则它是必需的。

https://symfony.com/doc/current/forms.html#creating-form-classes

另一种进行验证的方法是直接在您的 FormType 中选择验证规则。