1

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 ?

Didier Ghys
  • 30,396
  • 9
  • 75
  • 81

2 Answers2

3

I have a solution, but don't know if it will fit your needs. You can change de validation groups name in your config.yml for fos_user.

For example :

fos_user:
    ...
    registration:
        ...
        form:
            validation_groups: [YourBundleNameRegistration, Default] #before it was [Registration, Default]
    profile:
        ...
        form:
            ...
            validation_groups: [YourBundleNameProfile, Default] #before it was [Profile, Default]

Then the validation constraints defined in /vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Resources/config/validation/orm.xml will not applied anymore.

You have to copy the original orm.xml, and paste it in your YourBundle/Resources/config directory. And in this copy you replace Registration and Profile by YourBundleNameRegistration and YourBundleNameProfile. And then you remove the unique constraint on usernameCanonical.

Nico
  • 3,430
  • 4
  • 20
  • 27
3

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.

Didier Ghys
  • 30,396
  • 9
  • 75
  • 81