Search code examples
phpvalidationcakephpcakephp-2.4

CakePHP Validate a specific rule only when a couple required fields aren't empty


I wrote a custom rule method for validating whether a record exists in the DB before adding a new record. I put the method in a behavior so I could share it with other models, but I've run into a chicken and egg situation.

In order to know whether a category has a specific group name already I need to have the category id, and the group name. So I pass those keys through using my custom rule (category_id and name). But, this won't work since if I don't choose a category_id by mistake then the query will occur on just the name, so I patched this with a couple lines, but need to return true if this is the case and bank on the category_id validation being invalid.

Is there a better way to implement this kind of validation? Is this not as bad as I think? Or just don't bother and in my controller drop hasAny() under my call to validates() if it passes.

MODEL:
public $validate = [
    'category_id' => [
        'rule'    => 'notEmpty',
        'message' => 'Category is required.'
    ],
    'name'      => [
        'notEmpty'     => [
            'rule'    => 'notEmpty',
            'message' => 'Team is required.'
        ],
        'recordExists' => [
            'rule'    => [ 'recordExists', [ 'category_id', 'name' ] ],
            'message' => 'Group already exists.'
        ]
    ]
];

// BEHAVIOR:
public function recordExists( Model $Model, $conditions, $requireKeys )
{
    // Overrite conditions to
    $conditions = $Model->data[ $Model->name ];

    // Trim all array elements and filter out any empty indexes
    $conditions = array_map( 'trim', $conditions );
    $conditions = array_filter( $conditions );

    // Get the remaining non-empty keys
    $conditionKeys = array_keys( $conditions );

    // Only query for record if all required keys are in conditions
    if (empty( array_diff( $requireKeys, $conditionKeys ) )) {
        return !$Model->hasAny( $conditions );
    }

    // NOTE: seems wrong to return true based on the assumption the category_id validation has probably failed
    return true; 
}

Solution

  • Use the beforeValidate() callback of the model to check if the fields are present and if they're empty. If they're empty just unset() the recordExists validation rule in your models validation property. Copy them to a temporary variable or property in the case you want to set them back after your current operation.

    And use $Model->alias, name will break if the model is used through an association that has a different name.

    $conditions = $Model->data[ $Model->name ];