Search code examples
symfonyfosuserbundlesymfony-sonatasonata-user-bundle

Disable default constraints defined in FOSUserBundle


I'm currently working on a Symfony2 project which uses Sonata.

Previous developers have made modifications inside the vendors directly which is obviously a huge mistake so I'm in the process of refactoring this. I'm currently stuck with some modifications that have been made to constraint mapping from the FOSUserBundle.

The file is: /vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Resources/config/validation/orm.xml

They actually disabled the UniqueEntity constraint on the class FOS\UserBundle\Model\User based on the field usernameCanonical, like this:

<?xml version="1.0" ?>
<constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
        http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">

    <class name="FOS\UserBundle\Model\User">
        <!-- <constraint name="Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity">
            <option name="fields">usernameCanonical</option>
            <option name="errorPath">username</option>
            <option name="message">fos_user.username.already_used</option>
            <option name="groups">
                <value>Registration</value>
                <value>Profile</value>
            </option>
        </constraint> -->

        <constraint name="Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity">
            <option name="fields">emailCanonical</option>
            <option name="errorPath">email</option>
            <option name="message">fos_user.email.already_used</option>
            <option name="groups">
                <value>Registration</value>
                <value>Profile</value>
            </option>
        </constraint>
    </class>

</constraint-mapping>

How can I reproduce this change in the overriden FOSUserBundle, or the overriden SonataUserBundle ?


Solution

  • As much as the answer from @Nico pointed me to the right direction, here is the complete setup I've had to do in order to integrate it into the SonataUserBundle and have my custom validation groups taken into account.

    As a matter of fact, it seemed that my custom orm.xml file was not loaded. So after changing the validation groups, no more validation occurred at all.

    1. Create a file orm.xml

    In Application\Sonata\UserBundle\Resources\config\validation\:

    <?xml version="1.0" ?>
    <constraint-mapping xmlns="http://symfony.com/schema/dic/constraint-mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/dic/constraint-mapping
            http://symfony.com/schema/dic/constraint-mapping/constraint-mapping-1.0.xsd">
    
        <class name="FOS\UserBundle\Model\User">
            <!-- <constraint name="Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity">
                <option name="fields">usernameCanonical</option>
                <option name="errorPath">username</option>
                <option name="message">fos_user.username.already_used</option>
                <option name="groups">
                    <value>Registration</value>
                    <value>Profile</value>
                </option>
            </constraint> -->
    
            <constraint name="Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity">
                <option name="fields">emailCanonical</option>
                <option name="errorPath">email</option>
                <option name="message">fos_user.email.already_used</option>
                <option name="groups">
                    <value>CustomRegistration</value>
                    <value>CustomProfile</value>
                </option>
            </constraint>
        </class>
    
    </constraint-mapping>
    

    2. Tell FOSUserBundle and SonataUserBundle

    To use the custom validation groups (I've stripped down the non-relevant configuration lines):

    app/config/fos/fos_user.yml:

    fos_user:
    
        registration:
            form:
                validation_groups:
                    - CustomRegistration
    
        profile:
            form:
                validation_groups:
                    - CustomProfile
    

    app/config/sonata/sonata_user.yml:

    sonata_user:
        profile:
            form:
                validation_groups:
                    - CustomProfile
    

    3. Tell the compiler to load the "overriden" validation file

    Add Application\Sonata\UserBundle\DependencyInjection\Compiler\ValidationPass.php class. This is based on the original class from FOSUserBundle:

    class ValidationPass implements CompilerPassInterface
    {
        /**
         * {@inheritDoc}
         */
        public function process(ContainerBuilder $container)
        {
            if (!$container->hasParameter('fos_user.storage')) {
                return;
            }
    
            $storage = $container->getParameter('fos_user.storage');
    
            if ('custom' === $storage) {
                return;
            }
    
            $validationFile = __DIR__ . '/../../Resources/config/validation/' . $storage . '.xml';
    
            if ($container->hasDefinition('validator.builder')) {
                // Symfony 2.5+
                $container->getDefinition('validator.builder')
                    ->addMethodCall('addXmlMapping', array($validationFile));
    
                return;
            }
        }
    }
    

    Edit Application\Sonata\UserBundle\ApplicationSonataUserBundle:

    class ApplicationSonataUserBundle extends Bundle
    {
        /**
         * {@inheritdoc}
         */
        public function build(ContainerBuilder $container)
        {
            parent::build($container);
            $container->addCompilerPass(new ValidationPass());
        }
    }
    

    4. Override the default validation groups in the UserAdmin class of Sonata (within an overloaded UserAdmin class)

    /**
     * {@inheritdoc}
     */
    public function getFormBuilder()
    {
        $this->formOptions['data_class'] = $this->getClass();
    
        $options = $this->formOptions;
        $options['validation_groups'] =
            (!$this->getSubject() || is_null($this->getSubject()->getId())) ? 'CustomRegistration' : 'CustomProfile';
    
        $formBuilder = $this->getFormContractor()->getFormBuilder( $this->getUniqid(), $options);
    
        $this->defineFormBuilder($formBuilder);
    
        return $formBuilder;
    }
    

    I don't know if there was a simpler way to achieve this. Nevertheless, it works for me.