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

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;
}

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 ];

Related

How to not accept custom parameter when another already exists

I've custom validation code in my controller
Code
$validator = Validator::make($request->all(), [
'query' => 'required_without:fruits|string|min:2|max:50',
'page' => 'integer|max:10000000',
"fruits" => "required_without:query|array|min:1|max:100",
"fruits.*" => "required|string|min:2|max:50|distinct|exists:fruits,name"
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
$query = $request->get('query');
$fruits = $request->get('fruits');
// How to replace this block with Laravel core validation rules
if(($query && mb_strlen($query) > 1) && (is_array($fruits) && count($fruits))) {
return response()->json([
'errors' => [
'You cannot use parameter "query" when parameter "fruits" already exists'
]
], 422);
}
When in request present query and fruits laravel doesn't return me error. When present field query I not need to filed fruits or when has fruits to query.
This isn't really the responsibility of validation.
You could make a custom rule for this (documentation available here), but I'd probably just check if fruits is present and then use that if it's there instead of query (I think you said fruits is higher priority right?)

Laravel - exclude current email from unique validation

I am trying to use unique on an email address in Laravel 5.5 validation like this..
$rules = [
'email' => 'email|unique:users,email,
];
This is working and is checking the 'users' table in the 'email' column
But if the email address is the same as the currently saved one then this also fails validation.
Is there a way I can add an exception rule to the validation to ignore $user-email?
The unique rule takes a third parameter: a record to ignore, see "Forcing A Unique Rule To Ignore A Given ID".
Pass in the ID of the record you do not wish to be included in the unique test, e.g:
$rules = [
'email' => 'email|unique:users,email,' . $user->id
];
THIS IS AN EASY SOLUTION
Just add $this->route('id') as the third parameter
if your route was defined like this:
Route::put('{company}', 'CompanyController#update')
->name('update');
then your parameter name is "company"
So in your FormRequest:
public function rules()
{
$rules = [
'url' => [
'required',
'url',
'unique:companies,url,'.$this->route('company') ?? 0
],
];
// dd($rules); << check yourself
return $rules;
}
This FormRequest works the same for insert or update.
this line will instruct to ignore "0" in case of insert
$this->route('company') ?? 0

cakephp: model validation based on another field

I am trying to setup Model validation on one field that only need to be checked if another field equals a particular value.
My first field is query which is a dropdown with many values, one value is 'Other' if this is selected then I need the second field 'query_other' to not be empty.
I have this setup in my Item Model:
public $validate = array(
'query' => array(
'notempty' => array(
'rule' => array('notempty'),
'message' => 'THE QUERY IS REQUIRED',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
'query_other' => array(
'notempty' => array(
'rule' => array('if_query_other', 'query'),
'message' => 'REASON IS REQUIRED',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
);
I then have this custom function which is being called above.
function if_query_other(&$data, $check, $query_field) {
if($this->data[$this->name][$query_field] == 'Other' && $check == NULL)
{
return false;
}
else
{
return true;
}
}
It's not working, I am currently getting this error: Parameter 1 to Item::if_query_other() expected to be a reference, value given
CakePHP Version 2.3.6
Thanks
The error message is pretty clear, parameter 1 is passed by value, not by reference as your method signature requires. There is nothing to be passed by reference to a custom validation method, so simply remove the &, respectively remove the first parameter from the signature altogether.
The first parameter passed to a custom validation method will always be the data of the field to validate (in key => value format), followed by possible parameters defined in the rule array, like for example your fieldname. So $check would have never been null unless you would have defined null in the rule array, ie 'rule' => array('if_query_other', null), consequently your third parameter would have never been the fieldname.
Long story short, you need to define two parameters only, the first will contain the data of the field to validate, the second one the additional value defined in the rule array.
Here's an example, it checks whether the field passed in $query_field exists and whether its value equals Other, if it does it returns whether the value of the current field is not empty (I'm assuming the built-in Validation::notEmpty() is sufficient enough for your "not empty" check needs). If the value of the $query_field field doesn't equal Other, then this rule will always validate successfully, ie the value then isn't required to be not empty.
...
App::uses('Hash', 'Utility');
App::uses('Validation', 'Utility');
class Item extends AppModel
{
...
public function if_query_other($check, $query_field)
{
if(Hash::get($this->data[$this->alias], $query_field) === 'Other')
{
return Validation::notEmpty(current($check));
}
return true;
}
}

Codeigniter Update Multiple Records with different values

I'm trying to update a certain field with different values for different records.
If I were to use MySql syntax, I think it should have been:
UPDATE products
SET price = CASE id
WHEN '566423' THEN 49.99
WHEN '5681552' THEN 69.99
END
WHERE code IN ('566423','5681552');
But I prefer to use Active Record if it's possible.
My input is a tab delimited text which I convert into an array of the id and the desired value for each record:
$data = array(
array(
'id' => '566423' ,
'price' => 49.99
),
array(
'id' => '5681552' ,
'price' => 69.99
)
);
I thought this is the proper structure for update_batch, but it fails. Here's what I've tried:
function updateMultiple()
{
if($this->db->update_batch('products', $data, 'id'))
{
echo "updated";
}
else
{
echo "failed )-:";
}
}
And I get failed all the time. What am I missing?

Validation rule for a composite unique index (non-primary)

I am sure I am not the first who has composite unique keys in tables and who wants to validate them. I do not want to invent the bicycle so I ask here first. I have several tables that have 'id' columns as primary keys and two other columns as unique composite keys. It would be nice to have a validation rule to check that the submitted entry is unique and display a validation error if it is not. In Cakephp it could be done by a custom validation rule. I am pretty sure somebody has created such method already.
Ideally it would be a method in app_model.php that could be used by different models.
I am using that function:
function checkUnique($data, $fields) {
if (!is_array($fields)) {
$fields = array($fields);
}
foreach($fields as $key) {
$tmp[$key] = $this->data[$this->name][$key];
}
if (isset($this->data[$this->name][$this->primaryKey]) && $this->data[$this->name][$this->primaryKey] > 0) {
$tmp[$this->primaryKey." !="] = $this->data[$this->name][$this->primaryKey];
}
//return false;
return $this->isUnique($tmp, false);
}
basically the usage is:
'field1' => array(
'checkUnique' => array(
'rule' => array('checkUnique', array('field1', 'field2')),
'message' => 'This field need to be non-empty and the row need to be unique'
),
),
'field2' => array(
'checkUnique' => array(
'rule' => array('checkUnique', array('field1', 'field2')),
'message' => 'This field need to be non-empty and the row need to be unique'
),
),
So basically this will show the warning under each of the fields saying that it's not unique.
I am using this a lot and it's working properly.
In the CakePHP/2.x versions released in the last few years, the isUnique rule optionally accepts several columns:
You can validate that a set of fields are unique by providing multiple
fields and set $or to false:
public $validate = array(
'email' => array(
'rule' => array('isUnique', array('email', 'username'), false),
'message' => 'This username & email combination has already been used.'
)
);
I'm not sure of the exact version when the feature was available but there's a bug fixed in core as late as October 2014 filed against 2.3 and 2.4 branches.
You could put it in app model, but my suggestion would just be to add it to the model directly by placing the rule with it's $validate property.
Check out the built in isUnique rule.

Resources