Представьте себе форму примера в Symfony:
public function buildForm(FormBuilderInterface $builder)
{
$builder
->add('email', EmailType::class, [
'constraints' =>
new NotBlank(),
new IsUnique(),
],
])
->add('password', PasswordType::class, [
'constraints' =>
new NotBlank(),
new IsStrongEnough(),
],
])
}
Теперь, когда я отправляю форму и удостоверяюсь, что она действительна, я хочу, чтобы $form->getData() возвращал мой DTO с именем CreateAccountCommand:
final class CreateAccountCommand
{
private $email;
private $password;
public function __construct(string $email, string $password)
{
$this->email = $email;
$this->password = $password;
}
public function getEmail(): string
{
return $this->email;
}
public function getPassword(): string
{
return $this->password;
}
}
Пример контроллера:
$form = $this->formFactory->create(CreateAccountForm::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->commandBus->dispatch($form->getData());
return new JsonResponse([]);
}
Я не могу использовать этот класс напрямую, используя data_class, потому что форма, очевидно, ожидает, что модель будет иметь сеттеры, допускающие нулевые значения. Сама форма работает отлично, как и проверка.
Я пробовал использовать метод Картограф данных, но перед проверкой вызывается метод mapFormsToData.
Это вообще возможно? Или я должен получить данные в виде массива и создать объект вне формы?
Я обновил свой вопрос.
->add('email', EmailType::class, array('required' => true))required => true - это просто проверка html5 (и в любом случае это значение по умолчанию), не знаю, зачем вы это написали. Как я уже сказал в вопросе, часть проверки работает нормально.
Таким образом, вы можете переопределить поля модели, допускающие значение NULL, для проверки формы. Мне пришлось использовать этот трюк наоборот, в моей модели были свойства, не допускающие значения NULL, и мне пришлось использовать 'required' => false для принудительной проверки
У меня нет проблем с проверкой. Вопрос в том, как вернуть экземпляр CreateAccountCommand с getData() формы. Если у вас есть рабочее решение, опубликуйте его в качестве ответа.
Да, я опубликую способ управления формами с помощью Symfony 4






Вот как я привык обрабатывать формы в Symfony 4.1
Предположим, вам нужна простая форма добавления
Форма
class CreateAccountForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder)
{
//same method than yours
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array('data_class' => 'App\Entity\CreateAccountCommand'));
}
}
Контроллер
public function add(Request $request)
{
$createAccountCommand = new CreateAccountCommand();
//The object is "injected" to the form, that way, it's mapped when submitted
$form = $this->get('form.factory')->create(CreateAccountForm::class, $createAccountCommand);
//Form was submitted
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid())
{
//no need to getData(), the object $createAccountCommand is directly mapped to the form and can be used as is
$em = $this->getDoctrine()->getManager();
//persist, convert to json, or whatever
$em->persist($createAccountCommand);
$em->flush();
return ($this->redirectToRoute('someRoute'));
}
//form not submitted, or has been submit with errors (isValid() == false)
return ($this->render('account/add.html.twig',
array('form' => $form->createView())));
}
Способ есть, но он слишком сложен для того, чем вы хотите заниматься. Для этого вам нужно использовать Datamapper.
Я знаю, что это не лучшее решение, но самым простым решением было бы (на мой взгляд) добавление сеттеров к вашему классу модели и разрешение нулевых значений (РЕШЕНИЕ1). Другое решение - использовать другой объект для формы и построить свою команду после отправки (и вам не нужно изменять свою команду - РЕШЕНИЕ2).
public function handleForm(Request $request)
{
/// SOLUTION 1
$form = $this->createForm(RegisterFormType::class, new CreateAccountCommand());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$command = $form->getData();
// do whatever you want
}
// ...
// SOLUTION 2
$obj = new \stdClass();
$obj->login = '';
$obj->password = '';
$form = $this->createForm(LoginFormType::class, $obj);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$command = new CreateAccountCommand($data->login, $data->password);
// do whatever you want
}
}
С этой моделью:
final class CreateAccountCommand
{
//// ...
/**
* @param string $email
*/
public function setEmail(string $email): void
{
$this->email = $email;
}
/**
* @param string $password
*/
public function setPassword(string $password): void
{
$this->password = $password;
}
}
в модели некоторые поля (например, электронная почта) могут быть пустыми, но вы хотите, чтобы они были обязательными по форме?