I got problems using custom class validator in collections. It founds the errors, get the good property path, but send it to the parent form. It causes that errors are listed in the parent form instead of each child of the collection...
Some code to be more relevant :
validation.yml
My\SuperBundle\Entity\Event:
properties:
conflictComment:
- NotNull: ~ # this property constraint got the right property path in the right form !
constraints:
- My\SuperBundle\Validator\DateIsAvailable: ~ # this one got the right property path, but in the parent form.
MultiEventType.php, the parent form which get the sub-form errors
$builder
->add('events', 'collection', array(
'type' => new \My\SuperBundle\Form\Type\EventDateType()
));
...
'data_class' => 'My\SuperBundle\Form\Model\MultiEvent'
EventDateType.php, the collection which should get the errors
$required = false;
$builder
->add('begin', 'datetime', array(
'date_widget' => 'single_text',
'time_widget' => 'text',
'date_format' => 'dd/MM/yyyy',
'label' => 'form.date.begin')
)
->add('automatic', 'checkbox', compact('required'))
->add('conflictComment', 'textarea', compact('required'));
...
'data_class' => '\My\SuperBundle\Entity\Event'
DateIsAvailableValidator.php
namespace My\SuperBundle\Validator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class DateIsAvailableValidator extends ConstraintValidator
{
private $container;
public function __construct($container)
{
$this->container = $container;
}
public function isValid($event, Constraint $constraint)
{
$this->setMessage($constraint->message);
return false;
}
}
Property paths are like children[events][0].data but rendered on form_errors(multiEventForm.events), instead of form_errors(prototype) as it should be in my template for collection rows...
Is there a way to get the errors in the right form or is that a bug ?
Okay, here is what I did to bypass this issue :
Simply created an extension to render the error in the right form :
SubformExtension.php
<?php
namespace My\SuperBundle\Twig;
use Symfony\Component\Form\FormView;
use Twig_Environment;
use Twig_Extension;
use Twig_Function_Method;
/**
* Add methods to render into the appropriate form
*
* @author Jérémy Hubert <[email protected]>
*/
class SubformExtension extends Twig_Extension
{
/**
* {@inheritdoc}
*/
public function initRuntime(Twig_Environment $environment)
{
$this->environment = $environment;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'subform_extension';
}
/**
* {@inheritdoc}
*/
public function getFunctions()
{
return array(
'subform_errors' => new Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
);
}
/**
* Render errors handled by parent form of a collection in the right subform
* @param FormView $view The Child/Collection form which should handle the error
* @param string $template Template used to render the error message
*
* @return string
*/
public function renderErrors(FormView $view, $template = null)
{
// Get the root form, where our (sub)collection errors bubbled
$parentForm = $view;
do {
$parentForm = $parentForm->getParent()->getParent();
} while ($parentForm->hasParent());
$parentForm = $parentForm->getVars();
// Seeking property path
$fieldName = $view->get('full_name');
$validationName = '[' . preg_replace('/^[a-zA-Z_]*/', 'children', $fieldName) . '.data]';
// Render errors
$html = '';
foreach ($parentForm['errors'] as $error) {
if (false !== strpos($error->getMessageTemplate(), $validationName)) {
$message = str_replace($validationName, '', $error->getMessageTemplate());
if (null !== $template) {
$html .= $this->environment->render($template, compact('message'));
} else {
$html .= $message;
}
}
}
return $html;
}
}
services.yml
parameters:
twig.extension.subform.class: My\SuperBundle\Twig\SubformExtension
services:
twig.extension.subform:
class: %twig.extension.subform.class%
tags:
- { name: twig.extension }
errorMessage.html.twig (using Twitter Bootstrap)
<div class="alert alert-error no-margin-bottom">
<div class="align-center">
<i class="icon-chevron-down pull-left"></i>
{{ message }}
<i class="icon-chevron-down pull-right"></i>
</div>
</div>
And in my collection prototype :
{{ subform_errors(prototype, 'MySuperBundle:Message:errorMessage.html.twig') }}
Hope this helps while not any better solution is proposed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With