Нашел ответ. Для краткого ответа вы можете проверить Это сообщение Мэтта Даума.
Вот полный пример, демонстрирующий не только то, как создать пользовательский валидатор формы, но и то, как внедрять службы и дополнительные данные в форму (потому что это было в моем случае).
Если вам нужен простой рецепт, идите прямо вниз.
Давайте иметь DTO:
class MyFormDTO
{
/** @var string */
private $name;
/** @var string */
private $surname;
/** @var string */
private $phone;
/** getters and setters ommited */
}
Теперь определите зависимости в форме. Первые два — это сервисы, последний (Calendar
) — дополнительные данные, необходимые от контроллера.
class MyForm extends AbstractType
{
(fields hidden)
/**
* @param Sender $sender
* @param TranslatorInterface $translator
* @param Calendar $calendar
*/
public function __construct(Sender $sender, TranslatorInterface $translator, Calendar $calendar)
{
$this->translator = $translator;
$this->sender = $sender;
$this->calendar = $calendar;
}
}
Теперь есть два пути: если вам нужны только сервисы в вашей форме, вы можете просто определить свою форму как сервис. Если вам, как и мне, нужны дополнительные данные, вам нужно написать form factory service:
class MyFormFactory
{
(fields hidden)
/**
* @param Sender $sender
* @param TranslatorInterface $translator
*/
public function __construct(Sender $sender, TranslatorInterface $translator)
{
$this->sender = $sender;
$this->translator = $translator;
}
/**
* @param Calendar $calendar
*
* @return MyForm
*/
public function getMyForm(Calendar $calendar)
{
return new MyForm($this->sender, $this->translator, $calendar);
}
}
Давайте определим эту фабрику как службу с правильными зависимостями:
mybundle.form.myform_factory:
class: MyBundle\Service\FormFactory\MyFormFactory
arguments: [ @text_message.sender, @translator ]
Как получить форму в контроллере? Просто так:
class MyController extends Controller
{
/**
* @ParamConverter("calendar", options={"mapping"={"calendarId":"id"}})
*
* @param Request $request
* @param Calendar $calendar
*
* @return Response
* @throws Exception
*/
public function myAction(Request $request, Calendar $calendar)
{
$formDTO = new MyFormDto();
$myForm = $this->get('mybundle.form.myform_factory')->getMyForm($calendar);
$form = $this->createForm($myForm, $formDTO);
(handling post hidden)
}
}
А теперь самая важная часть — мы правильно внедрили сервисы в нашу форму. Как их использовать и проверять выбранные данные? так:
class MyForm extends AbstractType
{
(fields hidden, constructor shown in previous example)
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var MyFormDTO $myDTO */
$myDTO = $options['data'];
(build form as usual, using services and data from $options and $this->calendar injected by controller and factory)
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
parent::setDefaultOptions($resolver);
$resolver->setDefaults([
'csrf_protection' => true,
'constraints' => [
new Callback(function (MyFormDTO $data, ExecutionContextInterface $context) //notice that we have access to fully propageted DTO here
{
//use injected service
$isValid = $this->sender->validateSomething($data->getSurname(), $data->getPhone());
if (false === $isValid)
{
$context
->buildViolation($this->translator->trans('wrong_surname_phone_pair'))
->addViolation();
}
return $isValid;
})
],
]);
}
}
person
ex3v
schedule
23.09.2015