Protected cast not working after using getAttribute (laravel) - laravel

Here is how defined inside th model. The total does not return in array form after getTotalAttribute is used
protected $casts = [
'total' => 'array'
];
public function getTotalAttribute($value)
{
return $this->_auth($value);
}

You have to make sure “total” in database is a valid JSON format.
The array cast is particularly useful when working with columns that are stored as serialized JSON.
For more info: https://laravel.com/docs/8.x/eloquent-mutators#array-and-json-casting

Related

store and update an array as json in mysql database laravel 9

I have values that I want to store in the database, I have declared the type as json and cast it as an array:
protected $casts = [
'favourites' => 'array'
];
however, I'm not sure how to add and update and read the values inside, any ideas?
controller function:
public function addtofav()
{
$id = request()->get('id');
$user = auth()->user();
json_decode($user->favourites,true);
array_push($user->favourites,$id);
dump('hello');
json_encode($user->favourites);
$user->save();
return response(['id'=> $id],200);
}
Quoting from the Laravel documenations:
adding the array cast to that attribute will automatically deserialize
the attribute to a PHP array when you access it on your Eloquent
model.
therefore, no need to make any encoding or decoding to your values before any update or create, just keep it as an array and Eloquent will take care of that, since you have the $cast array as below:
protected $casts = [
'options' => 'array',
];
Actually you should use many to many but if you want to use array, you don't encode or decode.
public function addtofav()
{
$id = request()->get('id');
$user = auth()->user();
$favourites = $user->favourites;
array_push($favourites,$id);
$user->favourites = $favourites
$user->save();
return response(['id'=> $id],200);
}

Store multiple objects in json column

I have table with some json column. I want to store multi objects in the json column in Laravel.
My json column look like this "position":[{"lat" : 500, "lon" : 5000, "date":"2020:04:21 16:58:23"}].
this is a function in a service that , i call in my controller
public static function savePositionCamion($id_tournee, $position)
{
$geoTour = GeoTournee::where('geo_tournee.id_tournee', $id_tournee)->first(['position']);
if (!$geoTour) {
$geoTournee = new self();
$geoTournee->id_tournee = $id_tournee;
$geoTournee->position = $position;
return $geoTournee->save() ? $geoTournee : false;
} else {
$arr_pos = json_decode($geoTour->position);
dd($arr_pos);
}
}
in the controller i call the function like this
$geoTourne = GeoTournee::savePositionCamion($request->id_tournee, json_encode($request->position));
anyone can help me to achieve this ! thank's
The array cast type is particularly useful when working with columns that are stored as serialized JSON. You have JSON or TEXT field type that contains serialized JSON, adding the array cast to that attribute will automatically deserialize the attribute to a PHP array when you access it on your Eloquent model:
class GeoTournee extends Model
{
...
protected $casts = [
'position' => 'array',
];
...
}
So you can save this json field like this:
$geoTournee->position = $position;
$geoTournee->save();
Accessing the position attribute and it will automatically be deserialized from JSON into a PHP array. When you set the value of the position attribute, the given array will automatically be serialized back into JSON for storage:
And you don't need to create savePositionCamion function, you can use firstOrCreate instead:
GeoTournee::firstOrCreate(['id_tournee' => $id_tournee], [ 'position' => $position ]);

Encrypted Array Won't Save in Eloquent Model

I'm having an odd error with saving an encrypted array in Laravel. The model never updates even when save() is called.
There are no console or SQL errors.
When the encryption is disabled, there are no errors and the model updates successfully.
In a Controller, I'm calling the model like so:
$userData = UserData::where('user_id', $user_id)->first();
I then pull the array:
$encryptedData = $userData->app_data;
And I want to add to this array e.g.
$encryptedData['new'] = 'axy';
$encryptedData['time'] = time();
I then update the model and save it:
$userData->app_data = $encryptedData;
$userData->save();
However, here is where the problem starts. The model does not update. It remains as if nothing happens. Hence if I refresh(), I get the same data as if I had never added the two new entries. When I log it, it looks like this:
Array
(
[token] => xyz
[access_token] => abc
)
After the addition of two new entries:
Array
(
[token] => xyz
[access_token] => abc
[new] => 'axy'
[time] => 1234
)
And after the save() and refresh():
Array
(
[token] => xyz
[access_token] => abc
)
The model looks like this:
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Contracts\Encryption\DecryptException;
class UserData extends Model
{
protected $fillable = [
'user_id', 'app_data'
];
protected $casts = [
'user_id' => 'int',
'app_data' => 'array'
];
public function getAppDataAttribute($value)
{
try {
return decrypt($value);
}
catch (DecryptException $e) {
return $value;
}
}
public function setAppDataAttribute($value)
{
$this->attributes['app_data'] = encrypt($value);
}
}
Why are my additions to the array not being saved?
Edit: The strangeness continues
If I call:
UserData::where('id', $userData->id)->update(['app_data' => $encryptedData]);
Then the model does update and does not encrypt, HOWEVER, when I refresh and log the new 'app_data' field, it is returned as a JSON string and not an array as before. I need to cast/decode it to an array each time I want to use it.
Couple of things to look for.
1) The Laravel encrypter uses the app key. Make sure you have one in your .env file. If not, run php artisan key:generate
2) I assume the array is correctly formatted like this:
Array
(
'token' => 'xyz', // You have a = here and no commas after any other value
'access_token' => 'abc'
)
3) Depending on what you are storing this as, you can test by serializing the array before encrypting it:
$arr = serialize($encryptedData); // After you have added new data to the array
$userData->app_data = $arr;
$userData->save();
This is automatic in Laravel, but may give you a help hunting the bug. Test with your mutator using encryptString() and manually unserialize / decryptString() to see if any odd behavior by stepping through the values as they are mutated.

How to clean / reduce properties of a model instance to reduce json size?

I have a process where some models are updated and after that I send the updated object to pusher for real time tracking through an control board but the object has several other objects as relationships so the size of the serialized object exceed the pusher limit size for a message so my question is how can I delete some properties of the related objects?
I already tried pluck function but I don't know how to use on neastead objects
$vehicleEntry = VehicleEntry::with('vehicle')->find($request->entryId);
// I need just the id and plate of the object
$vehicleEntry->pluck('vehicle.id', 'vehicle.plate');
but it gets error
{id: 1, vehicle: {id: 2, plate: 'JIS575'}, created_at: '2019-07-11'}
One way I personally prefer is to make use of API resources. This way you always have full control over the data that is being returned.
Example:
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\Resource;
class VehicleEntryResource extends Resource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => (int) $this->resource->id,
// Watch out with this. Make sure the vehicle relation is loaded.
// Otherwise it will always add this execute another query for
// every vehicle entry you send to this class, would be bad when
// you want to send multiple. You could also use
// $this->whenLoaded('vehicle'), however this needs another
// resource.
'vehicle' => [
'id' => (int) $this->resource->vehicle->id,
'plate' => $this->resource->vehicle->plate,
],
'created_at' => $this->resource->created_at,
];
}
}
Now you can call this anywhere you want:
new VehicleEntryResource($vehicleEntry);
Not sure if Pusher messages works as nice as the JsonResponse you generally would return in your controller. When returning it in a response it would convert them to arrays automatically. But you could also do the following to get the array representation:
(new VehicleEntryResource($vehicleEntry))->toArray(null);
A simple way to do this is to add a $hidden property to your model and give it an array of strings that are property names you'd like to hide from json output:
protected $hidden = [
'hide',
'these',
'attributes',
'from',
'json'
];
When your object is converted to json it will automatically prevent any attributes listed in the $hidden array from showing up.
See the docs here: https://laravel.com/docs/5.8/eloquent-serialization#hiding-attributes-from-json

Change date format before validation

I have to change the date format before validation.
I use the german date format (dd.mm.yyyy). But for the validation i need the format yyyy-mm-dd.
here my rules from the requests file:
public function rules()
{
return [
'title' => 'required|min:5',
'start' => 'required|date_format:d.m.Y|after:+1 week|unique:talks,start',
'end' => 'required|date_format:d.m.Y|after:start|unique:talks,end',
'interval' => 'required'
];
}
Now i found this function:
public function all()
{
$input = parent::all();
//modify input here
return $input;
}
But how can i modify the input here???
Thanks for your help
The $input variable is just an associative array with key -> value pairs of the request input. You can directly modify the array:
public function all()
{
$input = parent::all();
$input['start'] = date("Y-m-d", strtotime($input['start']));
$input['end'] = date("Y-m-d", strtotime($input['end']));
return $input;
}
This will translate your date values for the purposes of validation. Because the validator calls the all() method.
However, this does not modify the original values in your input.
Whenever you access the input values by a different method than all(), the original value will appear. E.g. $request->input('start') will give you the original German format but $request->all()['start'] will give you the translated international formal.
Such a situation is a potential source of bugs and it's hard to maintain. The right solution for your problem is to write a tiny custom middleware that will modify the request values. See here: https://laracasts.com/discuss/channels/general-discussion/laravel-5-modify-input-before-validation?page=2

Resources