laravel model mutators not accessing the defined field - php

I am trying to define a model mutator
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Custom_fields extends Model
{
protected $table = 'custom_fields';
public function setContentAttribute($value){
return unserialize($value);
}
}
and on test controller, tried
Custom_fields::all()[0]->content;
Unfortunately, it is not working; the value is still not an array (serialize)
I want to access the field content and unserialize the value of the field so I can get an array automatically on retrieve, but it's not working.

You're trying to "get an array automatically on retrieve" with a method called setContentAttribute. You are confusing accessors and mutators. As the method name implies, the mutator is called when you set the content attribute.
To create an accessor that is called when you get the content attribute:
public function getContentAttribute($value)
{
return unserialize($value);
}
Likely if this data is to be stored in a serialized form you want your mutator to look like this:
public function setContentAttribute($value)
{
$this->attributes["content"] = serialize($value);
}
But really, you should not be using these serialization functions at all. Laravel can natively convert objects to JSON for storage in a database. Simply define the property in your model's $casts property and data will be converted automatically when getting or setting the property.
protected $casts = [
"content" => "array",
];

Related

Laravel 9 Mutating Attribute not returning mutated attribute

Environment
Laravel 9
php 8.0
I have this mutator function to transform a value from 4 decimal places to 2 decimal places. I want to test out but the Attribute::make function not returning value, below is code for the model
class SubjectWithFee extends Model
{
protected $table = 'sjfee';
protected $primaryKey = 'IndexID';
protected $fillable = [
'Amount',
'CurrencyID',
'ProgramID',
];
public $timestamps = false;
protected function Amount(): Attribute
{
return Attribute::make(
get: fn ($value) => sprintf('%0.2f', $value),
);
}
Although when I did the test it access the attribute correctly when putting dd('test') before the return but on the get function cannot be access
Anyone knows the problem?
Update
The column "Amount" starts with capital letter
Initially the source code was from laravel version 5 and someone upgraded to 9.
Update 2
All my setters are working, but only getters are not.
Try this different approach, instead of the current function you have:
public function getAmountAttribute($value)
{
return sprintf('%0.2f', $value);
}
Add the attribute name to the appends property of your model.
Appending Values To JSON
Occasionally, when converting models to arrays or JSON, you may wish
to add attributes that do not have a corresponding column in your
database. To do so, first define an
accessor for the
value:
After creating the accessor, add the attribute name to the appends
property of your model. Note that attribute names are typically
referenced using their "snake case" serialized representation, even
though the accessor's PHP method is defined using "camel case":
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The accessors to append to the model's array form.
*
* #var array
*/
protected $appends = ['amount'];
}

Laravel model - uncast specific column

I have a laravel table with a column I've defined like this in the migration:
$table->json('json');
And in the model, I cast it to an array:
protected $casts = [
'json' => 'array'
];
This works perfectly the majority of the time I need it, but there's one api call I'm making where I actually want my collection of that Model to give me the raw string rather than casting it to the array.
So, assuming my model is called Model, my api call looks like this:
$systemModels = Model::whereNull('user_id')->get();
$userModels = Model::where('user_id', $user->id)->get();
return response()->json([
'user_models' => $userModels->toArray(),
'system_models' => $systemModels->toArray()
]);
This is where I'd like the 'json' column of my Model to be rendered as a string rather than cast to an array. Is there a reliable way to do that?
Inside your model you can define a custom attribute which is added when the model is serialized:
class YourModel extends Model
{
protected $appends = ['json_raw'];
public function getJsonRawAttribute()
{
return $this->attributes['json'];
// or return json_encode($this->attributes['json']);
}
}
And then when doing the toArray() you can do $userModels->makeHidden('json')->toArray(); to remove the casted field you do not want.

Laravel: Sum Collection by Custom Method

I have a model that has a custom method (assume it has a field called 'fieldname')
e.g.
class X extends \Eloquent
{
function custommethodtest()
{
return rand(1);
}
}
I want to use the collection and SUM by the custom method ie:
X:all()->sum('custommethodtest');
but it appears that laravel will only sum by an actual field, not a custom method. For instance, this would work:
X::all()->sum('fieldname');
Any ideas on how to make this work?
Instead of just a method, create an attribute accessor:
public function getCustommethodtestAttribute(){
return rand();
}
And then:
X:all()->sum('custommethodtest');
You may use custom attribute to achieve same result;
class X extends \Eloquent
{
protected $appends = [
'custom_test'
];
public function getCustomTestAttribute()
{
//NB: rand() expects exactly 2 parameters, am using 1234 for parameter 2 just for testing sake
return $this->attributes['custom_test'] = rand(1,1234);
}
}
Then:
X:all()->sum('custom_test');
The method above better than using only:
public function getCustomTestAttribute(){
return rand(1);
}
For this reason:
Once the attribute has been added to the appends list, it will be
included in both the model's array and JSON forms. Attributes in the
appends array respect the visible and hidden configuration on the
model.

Laravel 4 Present Model

In Laravel 4, I have a model linking to a database table. Let's call it Model.
Say that this model has a database column called Property A and Property B.
When I make get request call to my model, i.e. Model::all() or Model::find($id), I don't want to return Property A or Property B, but some kind of function of the two, which appears to the front-end as some kind of read-only field, i.e. Property C.
Do I need to use a presenter library here, or is there a way by overriding model functions within Laravel 4?
The key for me here is that the property shows up when I call Model::all()
EDIT:
From my understanding this should return an attribute with the name foo constantly with the value "foo":
Model
class DiscountLink extends Eloquent {
protected $table = 'discountLinks';
protected $hidden = array('tag');
protected $fillable = array('name', 'currency', 'language', 'price', 'instalments', 'expires', 'active', 'foo');
public function getFooAttribute()
{
return "foo";
}
}
Controller
class DiscountLinkController extends \BaseController {
public function index()
{
return DiscountLink::all();
}
}
Use an accessor in your Model. To concatenate A and B, for instance:
public function getPropertyCAttribute()
{
return $this->attributes['property_a'] . ' ' . $this->attributes['property_b'];
}
And then you can access Model::find($id)->propertyC.
If you want the attribute to be automatically included in your model's results array (e.g. if you're sending the results of Model::all() or Model::get() as JSON, for example), add an $appends declaration to the top of your model:
protected $appends = array('PropertyC');
If the function is something that can be done in the database (like concatenation, sum, etc.), you could also add a DB::raw command to your query, like:
Model::select(*, DB::raw('CONCAT(PropertyA, " ", PropertyA) AS PropertyC'))->...

Laravel Eloquent Save to DB Using Create - Unhelpful Error

I get a very unhelpful error when I try and insert a new record in to my db. Does anyone have any idea where to start to solve this error?
user_id
That's all it says. (This is the name of one of my required fields in the table I'm saving to but it's not very descriptive.)
For reference here's my code:
$data = array(
'user_id'=>1,
'post_type'=>'0',
'post_title'=>'blah',
'description'=>'blah');
//it fails on this line
$id = Post::create($data);
Here's what my model looks like:
class Post extends Eloquent{
public static $rules = array(
'user_id'=>'required',
'post_type'=>'required',
'post_title' => 'required|max:25',
'description'=>'required'
);
public static function validate($data){
return Validator::make($data, static::$rules);
}
public function user(){
return $this->hasOne('User', 'id', 'user_id');
}
}
Tried getting rid of all relationships and validation in the model. That didn't work.
This is called mass-assignment in Laravel, what you need to do to get it working on your model is to set a protected array called $guarded to be empty. Add this to your model.
protected $guarded = array();
What this array does, it can prevent attributes to be mass-assigned with an array, if you don't want an attribute to be filled with the Model::create() method, then you need to specify that attribute in the $guarded array.
If instead you want to specify only the fillable attributes, Laravel's Eloquent also provides an array called $fillable where you specify only the attributes that you want to fill via the mass-assigment way.
Further reading:
Mass Assignment:
http://laravel.com/docs/eloquent#mass-assignment
Create Method:
http://laravel.com/docs/eloquent#insert-update-delete

Categories