tags:

views:

304

answers:

2

I have 4 optional fields, but at least 1 field (any field) must be filled?

any easy way to do this?

A: 

You're probably going to need to implement the validation manually using the beforeValidate() callback. Example (in your model, which we'll call Item):

function beforeValidate(){
    $valid = false;
    if(!empty($this->data['Item']['foo'])){
        $valid = true;
    }
    // do that same thing for the other three fields, setting $valid to true if any of the fields has a value.
    return $valid && parent::beforeValidate(); 
}

You could also do one long comparison assignment like this, but I find this type of crap really hard to read:

function beforeValidate(){
    $valid = !empty($this->data['Item']['foo']) || !empty($this->data['Item']['bar']) || !empty($this->data['Item']['baz']) || !empty($this->data['Item']['bling'])
    return $valid && parent::beforeValidate(); 
}

Good luck!

inkedmn
+1 I would agree with using a beforeValidate in the model. You could also setup an array of field names and loop through it. This would be especially helpful if you had more than 3-4 fields you're checking.
Dooltaz
The problem with this approach is that is subverts the normal validation process and you won't get error messages for other fields in the same form, unless and until the four "special fields" validate.
deceze
deceze - true, but it would be trivial to add a call to invalidate() if you needed to show field-specific error messages.
inkedmn
No, I'm saying the **other** fields in the form, fields not covered by the *at-least-one* rule, won't get validated. You submit the form, it gives you errors only for the 4 *at-least-one* fields, you fill one in, submit the form again, and only then will it give you errors for the other fields in the form. Not good for the user experience. The manual also states that the `beforeValidate` callback in meant to prepare data for validation, not to validate it. http://book.cakephp.org/view/682/beforeValidate
deceze
+1  A: 

A custom validation rule is the way to go!

var $validate = array(
    'myField1' => array('atLeastOne'),
    'myField2' => array('atLeastOne'),
    'myField3' => array('atLeastOne'),
    'myField4' => array('atLeastOne')
);

function atLeastOne($data) {
    return !empty($this->data[$this->name]['myField1'])
           || !empty($this->data[$this->name]['myField2'])
           || !empty($this->data[$this->name]['myField3'])
           || !empty($this->data[$this->name]['myField4']);
}

You could also pass in extra parameters of all the fields you want to compare and make a more general function out of it.

var $validate = array(
    'myField1' => array('atLeastOne', 'myField2', 'myField3', 'myField4'),
    ...
);

// just pulled out of thin air (i.e. untested)
function atLeastOne($data) {
    $args = func_get_args();  // will contain $data, 'myField2', 'myField3', ...

    foreach ($args as $name) {
        if (is_array($name)) {
            $name = current(array_keys($name));
        }
        if (!empty($this->data[$this->name][$name])) {
            return true;
        }
    }

    return false;
}
deceze
this is scary, i almost wrote the exact same code line for line, a few days ago????
bananarepub
There are only so many sane ways to do it... ;)
deceze