How do I automatically set model properties when retrieved from the db? - laravel

In my route I have:
$products = Product::where('category',$category)->get();
return $products[0]->x;
How can I set up my Product model so that the route returns abcd1234?
This is what I currently have in my model:
public function __construct(){
$this->x = 'abcd1234';
}

You need an accessor.
Add this to your Product model:
public function getXAttribute()
{
return 'abcd1234';
}
Access it with:
$product = Product::where('category', $category)->first();
return $product->x;
Having a 1 character attribute name may cause a problem though, id recommend changing the X to something with more than one character in camel case for the accessor.

Related

Add specific related field to response

I have a controller with the action:
public function getCities(): JsonResponse
{
return response()->json([City::all()], 200);
}
Entity City has relation to Country.
How I can add country.id for every item in the result City::all()?
Laravel has amazing feature for creating virtual attributes. Add these lines to your City model for this:
NOTIFICATION: I assume you have the CountryCity model
public $appends = ['country_id'];
public function getCountryIdAttribute()
{
$country = CountryCity::where('city_id',$this->id)
if($country){
return $country->id;
}
return null;
}
You should look at the documentation : https://laravel.com/docs/8.x/eloquent-relationships
The best way is to use eager loading, using :
City::with('country')->get();
Then, you can access country id like :
$city->country->id;

Laravel - How to change response format for specific fields

I've "Product" model.
And need to change some value formats for only responses.
For example;
I've "price" on database as decimal (11,2).
I want this as "1.000.000,00" format on response.
Or created_at field to "Carbon::parse($this->created_at)->toDayDatetimeString()"
Or I want to add 3 specific columns with my user attribute, on response. (is_allowed etc.)
How can this be possible on model?
How can I response like that?
You can use Mutator and Accessor to set format :
https://laravel.com/docs/8.x/eloquent-mutators#accessors-and-mutators
public function setDateAttribute($date) {
$this->attributes['date'] = Carbon::createFromFormat('Y-m-d', $date);
}
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
As a best practice in Laravel you can use Eloquent Resources: Eloquent Resources
It's basically a "transformer" between models data and API/Responses Output.
The only one thing to notice is that in the Resource files yout must specify all fields and relations (if needed) of the Model manually.
In the toArray() function you can modify the type of all data of your model as you prefer.
If not, you can access the new field by $model->my_custom_field (Laravel can resolve the name of the getter function automatically).
public function toArray($request)
{
$editedFieldValue = doSomething();
return [
'my_field' => $editedFieldValue,
'other_field' => '',
];
}
If you want to do that in Model, you can create customs fields:
class MuModel extends Model
{
protected $appends = ['my_custom_field'];
public function getMyCustomFiledAttribute(){
$newData = doSomething($this->existent_field);
return $newData;
}
}
The $appends variable add the new fields to all responses generated from the Model, as a normal database field.
P.S.: You can create a getAttribute() function for existent database attribute and return the value as you want!
For example: getCreatedAtAttribute()

Additional attributes in Laravel all request

Good day.
For example, I have a model People with fields/attributes:
name
surname
and the model also has this method:
public function FullName()
{
return "{$this->name} {$this->surname}";
}
if I make the next request:
$p = $people->all();
I'll get collection with names and surnames as attributes
how i can make function execution for each in all() request?
What is the best practice?
Well, depends on what kind of result do you want.
OPTION A: Have name, surname and full_name in all the items of the array.
Eleazar's answer is correct, but a little bit incomplete.
1. Define a new accessor in your model.
This will define a new attribute in your model, just like name or surname. When the new attribute is defined, you can just do $user->full_name to get the attribute.
As the documentation says, to define an accessor you need to add a method in your model:
// The function name will need to start with `get`and ends with `Attribute`
// with the attribute field in-between in camel case.
public function getFullNameAttribute() // notice that the attribute name is in CamelCase.
{
return $this->name . ' ' . $this->surname;
}
2. Append the attribute to the model
This will make the attribute to be considered just like any other attribute, so whenever a record of the table is called, this attribute will be added to the record.
To accomplish this you'll need to add this new value in the protected $appends configuration property of the model, as you can see in the documentation:
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* The accessors to append to the model's array form.
*
* #var array
*/
// notice that here the attribute name is in snake_case
protected $appends = ['full_name'];
}
3. Make sure that this attribute is visible
Notice this important part of the docs:
Once the attribute has been added to the appends list, it will be
included in both the model's array and JSON representations.
Attributes in the appends array will also respect the visible and
hidden settings configured on the model.
4. Query your data.
When doing the following:
$p = $people->all();
The $p array should have name, surname and also the new full_name attribute for each item.
OPTION B: Just get the full_name for specific purposes.
You can do the following when querying, iterate each result to get the attribute.
Now to do this you can iterate the collection with a foreach sentence, but given that whenever querying data, the array returned is always a Collection instance, so you simply use the map function:
$full_names = $p->map(function ($person) {
// This will only return the person full name,
// if you want additional information just custom this part.
return $person->fullname;
});
Using collection higher order messages it can be even shorter:
$full_names = $p->map->fullname;
I use the following:
public function getFullNameAttribute()
{
return "{$this['name']} {$this['lastname']}";
}
and then, I add it in appends:
class User extends Authenticatable {
protected $appends = ['fullname'];
}
What do you think?
In your model write a function to concatenate the name
public function getFullNameAttribute() {
return ucfirst($this->first_name) . ' ' . ucfirst($this->last_name);
}
now you can call it this way
$user = User::find(1);
echo $user->full_name;
or
Auth::user()->full_name;

What are Mutators and Accessors in Laravel

I am trying to understand accessors & mutators and why I need them. And my another ask is the middle part of an attribute's method for an example:
Accessor:
public function getFirstNameAttribute($value)
{
return ucfirst($value);
}
Mutator:
public function setFirstNameAttribute($value)
{
$this->attributes['first_name'] = strtolower($value);
}
Here, we can see getFirstNameAttribute and setFirstNameAttribute methods and I haven't been able to clear the middle part FirstName of them. I will really be grateful for a better explanation and kind cooperation.
Accessors create a "fake" attribute on the object which you can access as if it were a database column. So if your person has first_name and last_name attributes, you could write:
public function getFullNameAttribute()
{
return $this->first_name . " " . $this->last_name;
}
Then you can call $user->full_name and it will return the accessor. It converts the function name into a snake_case attribute, so getFooBarBazAttribute function would be accessible through $user->foo_bar_baz.
Mutator is a way to change data when it is set, so if you want all your emails in your database to be lowercase only, you could do:
public function setEmailAttribute($value)
{
$this->attributes['email'] = strtolower($value);
}
Then if you did $user->email = "EMAIL#GMAIL.com"; $user->save(); in the database it would set email#gmail.com
From the docs accessor and mutator both are public function in Laravel model for getting and setting model's attributes
An accessor will automatically be called by Eloquent when attempting to retrieve the value of the first_name attribute:
$fullName = $user->full_name;
It's for customizing a model's attributes or adding fake attributes
On the other hand mutator is for setting a real attribute of a model
Mutator will be automatically called when we attempt to set the value of the an attribute
Sometimes it happens that you have to modify the column value that was stored in a database. For Example, if you want to show column values that qualify some condition then you have to use Accessor and Mutator. where Accessor get value and Mutator set value.
By $append=[]; you can create any random column that fulfills your application demand.
here I am only telling about Accessor. In this code total_holiday_hours is my DB column name and i want to modify that value so that
i used $this->attributes['total_holiday_hours'] otherwise you can access this way
$this->total_holiday_hours.
function getTotalHolidayHoursAttribute()
{
if( $this->attributes['total_holiday_hours'] && $this->holiday_session == date('Y') )
{
return $this->attributes['total_holiday_hours'];
}
return 225;
}

Laravel oneToMany accessor usage in eloquent and datatables

On my User model I have the following:
public function isOnline()
{
return $this->hasMany('App\Accounting', 'userid')->select('rtype')->latest('ts');
}
The accounting table has activity records and I'd like this to return the latest value for field 'rtype' for a userid when used.
In my controller I am doing the following:
$builder = App\User::query()
->select(...fields I want...)
->with('isOnline')
->ofType($realm);
return $datatables->eloquent($builder)
->addColumn('info', function ($user) {
return $user->isOnline;
}
})
However I don't get the value of 'rtype' for the users in the table and no errors.
It looks like you're not defining your relationship correctly. Your isOnline method creates a HasMany relation but runs the select method and then the latest method on it, which will end up returning a Builder object.
The correct approach is to only return the HasMany object from your method and it will be treated as a relation.
public function accounts()
{
return $this->hasMany('App\Accounting', 'userid');
}
Then if you want an isOnline helper method in your App\User class you can add one like this:
public function isOnline()
{
// This gives you a collection of \App\Accounting objects
$usersAccounts = $this->accounts;
// Do something with the user's accounts, e.g. grab the last "account"
$lastAccount = $usersAccounts->last();
if ($lastAccount) {
// If we found an account, return the rtype column
return $lastAccount->rtype;
}
// Return something else
return false;
}
Then in your controller you can eager load the relationship:
$users = User::with('accounts')->get(['field_one', 'field_two]);
Then you can do whatever you want with each App\User object, such as calling the isOnline method.
Edit
After some further digging, it seems to be the select on your relationship that is causing the problem. I did a similar thing in one of my own projects and found that no results were returned for my relation. Adding latest seemed to work alright though.
So you should remove the select part at very least in your relation definition. When you only want to retrieve certain fields when eager loading your relation you should be able to specify them when using with like this:
// Should bring back Accounting instances ONLY with rtype field present
User::with('accounts:rtype');
This is the case for Laravel 5.5 at least, I am not sure about previous versions. See here for more information, under the heading labelled Eager Loading Specific Columns
Thanks Jonathon
USER MODEL
public function accounting()
{
return $this->hasMany('App\Accounting', 'userid', 'userid');
}
public function isOnline()
{
$rtype = $this->accounting()
->latest('ts')
->limit(1)
->pluck('rtype')
->first();
if ($rtype == 'Alive') {
return true;
}
return false;
}
CONTROLLER
$builder = App\User::with('accounting:rtype')->ofType($filterRealm);
return $datatables->eloquent($builder)
->addColumn('info', function (App\User $user) {
/*
THIS HAS BEEN SUCCINCTLY TRIMMED TO BE AS RELEVANT AS POSSIBLE.
ARRAY IS USED AS OTHER VALUES ARE ADDED, JUST NOT SHOWN HERE
*/
$info[];
if ($user->isOnline()) {
$info[] = 'Online';
} else {
$info[] = 'Offline';
}
return implode(' ', $info);
})->make();

Resources