I am having trouble using the Laravel Validator to validate some data. The validator is modifying properties of the input to null. The data passed to be validated is a mix of array and objects (in this case, a model instance).
Just for clarification: I know how to use FormRequest in controllers, I am full aware that Laravel would inject the FormRequest in the methods, and FormRequest is primaly to be used to validate user data, etc, etc. The point is why the validator need to modify the data I sent to validation?
Here's an example that you can directly paste in a php artisan tinker session:
$rules = [
'users' => [
'required',
'array',
'min:1',
],
'users.*' => [
'required',
],
'users.*.name' => [
'required',
'string',
'max:255',
],
'users.*.age' => [
'required',
'integer',
],
'users.*.best_friend' => [
'required',
],
];
$data = [
'users' => [
(new \App\Models\User)->forceFill([
'name' => 'USER #1',
'age' => 30,
'best_friend' => (new \App\Models\User)->forceFill(['name' => 'User X'])
]),
],
];
echo 'BEFORE: ' . data_get($data, 'users.0.name'); // USER #1
$validator = Validator::make($data, $rules);
echo 'AFTER: ' . data_get($data, 'users.0.name'); // NULL
dd($data);
OK, the data PASSES. But the problem is that the validation modified the variable $data, setting null to the fields with these patterns: users.*.name, users.*.age and users.*.best_friend.
If I dare to validate any model attribute, it sets to null.
I debugged and I reached the source of the modification:
/vendor/laravel/framework/src/Illuminate/Validation/ValidationData.php:42:
/**
* Gather a copy of the attribute data filled with any missing attributes.
*
* @param string $attribute
* @param array $masterData
* @return array
*/
protected static function initializeAttributeOnData($attribute, $masterData)
{
$explicitPath = static::getLeadingExplicitAttributePath($attribute);
$data = static::extractDataFromPath($explicitPath, $masterData);
if (! str_contains($attribute, '*') || str_ends_with($attribute, '*')) {
return $data;
}
// here some debug info:
// $explicitPath="users"
// $attribute="users.*.name"
// $data=User
return data_set($data, $attribute, null, true);
}
I know data_set modifies by reference.
But I could not understand why the code modifies the data if there is data already there. Should not it check for data before setting to null?
The validator is making the validated properties of my model to be null. Why and how to fix? Maybe a different approach? Maybe this could be considered an bug/improvement for the Illuminate lib?
Any help would be apreciated.
VERSIONS: Laravel Framework 9.33.0
PHP 8.1.2
Laravel transforms the keys from your validation rules: name and age. But that didn't work as expected because users are objects. To solve that you need to call
toArray()afterforceFillIf you need validation for
best_friend.nameyou need to calltoArray()on that too. But without validation you will get the object as it is.