Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

symfony2 collection validator class

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 ?

like image 475
Ze Big Duck Avatar asked Jul 01 '26 14:07

Ze Big Duck


1 Answers

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.

like image 88
Ze Big Duck Avatar answered Jul 04 '26 20:07

Ze Big Duck



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!