Can't figure out why JSON field is being ignored.
This one doesn't work:
Registries::create([
'nr' => $old_document->no,
'metas->name' => 'r01',
]);
In model I have set:
protected $casts = [
'metas' => 'array',
];
And:
protected $fillable = [
'nr',
'metas'
];
I think the problem is in attributes casting, because this one is working:
Registries::create([
'nr' => $old_document->no,
'metas' => json_encode(['name'=>'r01']),
]);
I'm not getting any errors just JSON column stays empty.
I'm not fan of attribute casting in relational dabatases.
So it's just my guess based on documentation about array.
The array cast type is particularly useful when working with columns that are stored as serialized JSON.
You're trying to put associative array which for JSON is an object.
I might guess that it's a bug.
So try to typecast it on insert:
Registries::create([
'nr' => $old_document->no,
'metas' => (object)['name'=>'r01'],
]);
attribute casting
and make it object:
protected $casts = [
'metas' => 'object',
];
So it seams that $metas->name is working only for updating, in my case I need to insert like this:
Registries::create([
'nr' => $old_document->no,
'metas' => ['name'=>'r01']
]);
Related
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() after forceFill
$data = [
'users' => [
(new \App\Models\User) -> forceFill([
'name' => 'USER #1',
'age' => 30,
'best_friend' => (new \App\Models\User) -> forceFill(['name' => 'User X'])
])->toArray(),
]
];
If you need validation for best_friend.name you need to call toArray() on that too. But without validation you will get the object as it is.
I have the following mutator:
public function setFormattedCriteriaAttribute($value)
{
$this->attributes['formatted_criteria'] = serialize($value);
}
When I call the following why doesn't it update the formatted_criteria value - note the field is listed in my fillable attributes array ?
$jobAlert = JobAlert::findOrFail($id);
$jobAlert->update([
'frequency' => $request->frequency,
'criteria' => $criteria,
'formatted_criteria' => ['test']
]);
Be sure formated_criteria in your $fillable variable.
Update
if you have casts array in your model modify else add.
protected $casts = [
'formatted_criteria' => 'array',
];
then update your field as LONGTEXT with binary
i am trying to pass some values into a single column in laravel database table.
The values are like this 20,45,67,89
but i want them to enter into the colume like this
===USER_ID====
20
45
67
89
I have tried like below, but not working..any suggestions ?
foreach ($request->val2 as $value){
$str_explode = explode(",",$value);
DB::table('retirement')->insertGetId([
'user_id' => $str_explode,
'amount' => $request->val1,
'week' => $request->week
]);
}
Hope this will work
foreach ($request->val2 as $value){
$str_explode = explode(",",$value);
$insert = [];
foreach($str_explode as $str){
$insert[] = [
'user_id' => $str,
'amount' => $request->val1,
'week' => $request->week
];
}
DB::table('retirement')->insert($insert);
I'm not sure i understood your question clearly, i'm assuming you want to insert array to a column:
did you try to set the column in migration to Json?
did you set the $casts in the model to json or array?
protected $casts = [ 'user_id' => 'array' ];
then when you do this, you can have an array added to that column like
Posts::create(['user_id'=>[1,2,3,4]]);
normally the user_id field is set to unsignedBigInt(), that type will not accept anything but integers, you gotta check the migration column type first.
explode() is returning an array, not a single value, that's why it will fail. Instead, you should loop through all values like this:
foreach ($request->val2 as $value){
$str_explode = explode(",",$value);
foreach($str_explode as $str){
DB::table('retirement')->insertGetId([
'user_id' => $str,
'amount' => $request->val1,
'week' => $request->week
]);
}
}
As a side advice, as you are not saving the id returned by insertGetID, you can simply use insert. Moreover, it's usually a good practice to use create because this way you will also save timestamps for created and updated.
$this->assertDatabaseHas() not working with JSON/JSONb columns.
So how can I tests these types of columns in Laravel?
Currently, I have a store action. How can I perform an assertion, that a specific column with pre-defined values was saved.
Something like
['options->language', 'en']
is NOT an option, cause I have an extensive JSON with meta stuff.
How can I check the JSON in DB at once?
UPD
Now can be done like that.
I have solved it with this one-liner (adjust it to your models/fields)
$this->assertEquals($store->settings, Store::find($store->id)->settings);
Laravel 7+
Not sure how far back this solution works.
I found out the solution. Ignore some of the data label, Everything is accessible, i was just play around with my tests to figure it out.
/**
* #test
*/
public function canUpdate()
{
$authUser = UserFactory::createDefault();
$this->actingAs($authUser);
$generator = GeneratorFactory::createDefault();
$request = [
'json_field_one' => [
'array-data',
['more-data' => 'cool'],
'data' => 'some-data',
'collection' => [
['key' => 'value'],
'data' => 'some-more-data'
],
],
'json_field_two' => [],
];
$response = $this->putJson("/api/generators/{$generator->id}", $request);
$response->assertOk();
$this->assertDatabaseHas('generators', [
'id' => $generator->id,
'generator_set_id' => $generator->generatorSet->id,
// Testing for json requires arrows for accessing the data
// For Collection data, you should use numbers to access the indexes
// Note: Mysql dose not guarantee array order if i recall. Dont quote me on that but i'm pretty sure i read that somewhere. But for testing this works
'json_field_one->0' => 'array-data',
'json_field_one->1->more-data' => 'cool',
// to access properties just arrow over to the property name
'json_field_one->data' => 'some-data',
'json_field_one->collection->data' => 'some-more-data',
// Nested Collection
'json_field_one->collection->0->key' => 'value',
// Janky way to test for empty array
// Not really testing for empty
// only that the 0 index is not set
'json_field_two->0' => null,
]);
}
Note: The below solution is tested on Laravel Version: 9.x and Postgres version: 12.x
and the solution might not work on lower version of laravel
There would be two condition to assert json column into database.
1. Object
Consider Object is in json column in database as shown below:
"properties" => "{"attributes":{"id":1}}"
It can assert as
$this->assertDatabaseHas("table_name",[
"properties->attributes->id"=>1
]);
2. Array
Consider array is in json column as shown below:
"properties" => "[{"id":1},{"id":2}]"
It can assert as
$this->assertDatabaseHas("table_name",[
"properties->0->id"=>1,
"properties->1->id"=>2,
]);
Using json_encode on the value worked for me:
$this->assertDatabaseHas('users', [
'name' => 'Gaurav',
'attributes' => json_encode([
'gender' => 'Male',
'nationality' => 'Indian',
]),
]);
I have a form with three fields: title, body and photo[]. I'm trying to validate it so that at least one item is filled in, but I can't seem to get it to work. If I upload a file I still receive an error for title and body.
public function rules()
{
return [
'title' => 'required_without_all:body,photo.*',
'body' => 'required_without_all:title,photo.*',
'photo.*' => 'required_without_all:title,body',
'photo.*' => 'mimes:jpeg,gif,png',
];
}
Update: Jonathan pointed out that I had my rules wrong. I've fixed them and am now using this. It's still not working; when I try to upload a photo I get the error message that the other fields are required.
public function rules()
{
return [
'title' => 'required_without:body,photo.*',
'body' => 'required_without:title,photo.*',
'photo.*' => 'required_without:title,body|mimes:jpeg,gif,png',
];
}
If you're looking to ensure the photo field is an array then you need 'photo' => 'array' and then you can use 'photo.*' => '' for the other validations of the array's children.
The rules are separated by a pipe character | so if you were going to combine the two in your example it would be 'photo.*' => 'required_without_all:title,body|mimes:jpeg,gif,png',. I don't see you using the pipe to separate rules so I can't be sure you are aware of it.
This may have been where you were going wrong in the first place (two keys in the associative array that are identical) and some kind of precedence taking affect negating one of the rules.
You could try something like this (for the record I think you were on the right track to begin with using required_without_all as this stipulates the need to be required if all of the given fields are missing):
public function rules()
{
return [
'title' => 'required_without_all:body,photo',
'body' => 'required_without_all:title,photo',
'photo' => 'array',
'photo.*' => 'required_without_all:title,body|mimes:jpeg,gif,png',
];
}
Reference