Skip to content

[Validator] Added 'Any' validator #11586

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from

Conversation

cleentfaar
Copy link

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets -
License MIT
Doc PR -

The Any constraint allows you to re-use existing constraints and is satisfied when at least one of the constraints validated.

Usecase

You have a form with a field that can either be a number greater than 3, or match a specific regex.
You could write your own Validator class and create your own little $number > 3-tests and preg_match calls. Or... you might be using an existing Constraint (e.g. Iban), and would simply like to also allow a different value to be passed (for instance a normal bank account number).

So how can we combine those constraints to satisfy a given value if any of them validates?

Here's where the AnyValidator comes in handy. You can pass it an array of constraints that will then be checked during validation. If any of them found the value to be valid, the constraint is satisfied (no errors).

Below is an example of what this would look like in your FormBuilder:

// src/Acme/DemoBundle/Form/Type/MyForm.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
    // ...
    $builder->add(
        'my_example', 
        null,
        array(
            'label'       => 'My example field',
            'constraints' => array(
                new Any(array(
                    new Regex('/here-is-my-regex/'),
                    new GreaterThan(3),
                ),
            ),
        )
    );
    // ...
}

The validation message that is used when all constraints failed can be controlled by passing it as a key/value in the $options argument. For example:

// ...
$constraint = new Any(array(
    'constraints' => array(
        new Iban(), 
        new DutchBankAccount()
    ), 
    'message' => 'You must supply either an iban number OR a valid Dutch bank account'
);
// ...

Once this PR has reached an acceptable state I will add the necessary docs to complement the changes.


/**
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it should also be allowed at class level

@cleentfaar
Copy link
Author

Thanks for the feedback so far, I will get it refactored and update this PR appropriately

Cas Leentfaar added 2 commits August 6, 2014 23:06
@cleentfaar
Copy link
Author

Ok, so I've tried to implement the creation of an ExecutionContext for each constraint that is validated against, but my code for it seems really messy; is there a better way of doing this?

Obviously the current code does not yet work as expected either, so I've tried to look at related validators; would extending the RecursiveContextualValidator be a better idea, having the context factory available on demand?

@stof
Copy link
Member

stof commented Aug 13, 2014

The 2.5 API has a startContext() method which is exactly about doing it. The 2.4 API does not allow to do it (which is one of the reason for changing the API)

@webmozart
Copy link
Contributor

You might be interested in #5749 which initially discussed the addition of such constraints. Given the little interest in the discussion back then I'm not sure whether this feature is worth adding.

@webmozart
Copy link
Contributor

A different solution for this use case might be to support constraint validation in the Expression constraint. I'm not sure how exactly this would work, but maybe one could implement something like this:

/**
 * @Assert\Expression("validate(value, Regex('/here-is-my-regex/')) or validate(value, GreaterThan(3))")
 */

maybe with support for a shortcut like

/**
 * @Assert\Expression("Regex('/here-is-my-regex/') or GreaterThan(3)")
 */

But then I don't know how to support constraints in custom namespace.

@stof
Copy link
Member

stof commented Aug 19, 2014

@webmozart this would require registering a function for each available constraint to achieve the syntax suggested here. and we don't know this list. the expression language will not help us here, because of its restrictive syntax (which is a feature of the component)

@webmozart
Copy link
Contributor

@stof Another alternative could be to extend the Expression constraint like this:

/**
 * @Expression("matchesRegex or withinRange", vars = {
 *     "matchesRegex" = @Regex("/here-is-my-regex/"),
 *     "withinRange" = @GreaterThan(3),
 * })
 */

PHP usage:

$validator->validate($value, null, new Expression(array(
    'expression' => 'matchesRegex or withinRange',
    'vars' => array(
        'matchesRegex' => new Regex('/here-is-my-regex/'),
        'withinRange' => new GreaterThan(3),
    ),
)));

What do you think?

@fabpot
Copy link
Member

fabpot commented Sep 23, 2014

I think that the last proposal from @webmozart makes sense.

@webmozart
Copy link
Contributor

I'll close this. Let's continue the discussion in #11940 and fix this issue accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants