Search code examples
phplaravellaravel-4laravel-validation

Input validation exclusive or


I have a model called Answer.

This model has two possible relations. A Question or a Product. But, Answer should only ever have one relation. Either Question or Product.

There is a form to create answers.

This form has three inputs. two of them are <select> inputs. The other is a text input, called name.

I want my validation to only allow one to be filled out.

My current validation:

$validator = Validator::make(
    Input::all(),
    array('name' => array('required'))
);

$validator->sometimes('product', array('required', 'numeric'), function ($input) {
    return !is_numeric($input->question);
});

$validator->sometimes('question', array('required', 'numeric'), function ($input) {
    return !is_numeric($input->product);
});

Requires at least one of the selected to be filled out, but will also allow two.

So, my question is: How can I change my validation to only allow one of the selects to be filled out. But one of them must always be filled out.

Select 1:

<select name="question" class="form-control">
    <option>None</option>
    <option value="1" selected="selected">Question 1</option>
    <option value="2">Question 2</option>
</select>

Select 2:

<select name="product" class="form-control">
    <option>None</option>
    <option value="2" selected="selected">Product 1</option>
    <option value="3">Product 2</option>
</select>

Solution

  • First off. You must specify an empty value in "None" option:

    <option value=''>None</option>
    

    You are looking for XOR operator, you need to create a custom validation rule in this case:

    Validator::extendImplicit('xor', function($attribute, $value, $parameters)
    {
        if( $value XOR app('request')->get($parameters[0]))
            return true;
        return false;
    });
    Validator::replacer('xor', function($message, $attribute, $rule, $parameters)
    {
        // add indefinite articles
        $IAattribute = (stristr('aeiou', $attribute[0]) ? 'an ' : 'a ') . $attribute;
        $parameters[0] = (stristr('aeiou', $parameters[0][0]) ? 'an ' : 'a ') . $parameters[0];
    
        if(app('request')->get($attribute))
            return 'You cannot choose both '.$IAattribute.' and '.$parameters[0].'.';
        return $IAattribute.' or '.$parameters[0].' is required.';
    });
    
    
    $validator = Validator::make(
        app('request')->all(),
        array('name' => array('required')
        ,'product' => array('xor:question')
    ));