В коде, перенесенном из Symfony 2.7 в 4.0, проверка больше не происходит в моей форме, позволяя проходить через плохие данные и вызывать нарушение ограничения Doctrine
Я новичок в Symfony, и меня попросили перенести приложение 2.7 на 4.0. Я делал это поэтапно (2.7-> 2.8-> 3.x-> 4.0) и решал проблемы по мере их появления, но одна вещь, которая сломалась на этом пути, - это автоматическая проверка формы. В исходной версии, если бы я попытался создать нового пользователя и оставил поля незаполненными, он правильно пометил бы их, и всплывающие сообщения «не должно быть пустыми» в пользовательском интерфейсе. Теперь он пропускает прошедшие до тех пор, пока не попытается записать в базу данных, после чего 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,
)
);
}
И файл ветки для формы:
{% 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 %}
Если я оставлю все поля пустыми и нажму «Добавить нового администратора», они не будут помечены как пустые, а переданы в Doctrine. Ожидаемое поведение заключается в том, что он помечает их в пользовательском интерфейсе и не пытается записать их в базу данных.
Я уверен, что совершил множество преступлений против Symfony, поскольку ускорил процесс обучения, так что успокойтесь. Сейчас я просто пытаюсь решить эту узкую проблему; рефакторинг для более элегантного соответствия Symfony 4 придется подождать еще один день.
Спасибо!