Eloquent Collection query - 'WHERE' clause with $value parameter without invoked accessor - laravel

I have used some accessors for Model attributes.
And as my knowledge about Accessor in Laravel, it can use to create a virtual attribute based on another attribute value or modify the return value of the existing attribute.
So I create an Accessor with the public method getStatusAttribute to convert the status from integer to string value. For example, my Model has an attribute named is 'status'. In DB, this column is stored with integers like 0,1,2,...
In this method, the return value is a string based on the current status attribute value.
public function getStatusAttribute(int $value): string
{
$statuses = [
1 => 'New',
2 => 'Pending',
3 => 'Canceled'
];
return $statuses[$value];
}
Everything work fined when I retrieve attribute value from the model object entity.
But when I want to retrieve an Eloquent Collection with a specific 'status' attribute value, the $value parameter in the 'where' method expects a string value that is returned from the accessor method, not is an integer value which stored in a database. So the $value parameter would be 'New' or 'Pending' or 'Canceled' instead of 1,2,3.
I want to retrieve a collection that contains an Eloquent Model Object so that I won't use an Illuminate\Support\Facades\DB.
I know I still can retrieve the exact collection with the $value parameter as a string, but I was just wondering if we have another way to skip the accessor in the query of Eloquent Collection?
Please see an example below for more detail about what I expect.
For example, some records in the database look like the below, and the model was named 'DemoModel' :
id
status
1
1
2
3
3
1
4
3
5
2
public function getCanceledEntities($entityId)
{
$canceledEntities = DemoModel::all()->where('status', '=', 3);
if($canceledEntities->isNotEmpty()) {
return $canceledEntities;
}
}
Without an accessor, this query will return a collection containing two entities with id [ 2, 4 ].
But with an accessor (as defined in the model), it can't find a matched entity because the WHERE clause expects that the 3rd parameter is 'Canceled' instead of 3.
My alternative method is to use a string 'Canceled' in the 'where' clause. However, I still want to use the value stored in a database for the query, and I think maybe the framework has another way to write an Eloquent Collection query without invoking an accessor.
Any suggestions are always welcome.

Related

Yii2 ActiveRecord how to get only populated attributes from model?

When using an ActiveRecord to retrieve models it's possible to select a few columns. For example:
Product::find()->select('product_id,name')->all();
There are more columns than product_id and name in the database, but this example returns an array filled with all models, having only the product_id and name attributes filled. This is expected of course because I've passed them into select().
Now when looping through the found models and calling $model->getAttributes() it returns an array of all model attributes and their values (null for the not-selected attributes), including all columns that were not selected in the query but are in the model's attributes() function.
Is there a way to call a function similar to getAttributes() but returning only the populated attributes that were selected in the query: product_id and name?
I know it's possible to pass in exclusions to getAttributes() but I'd rather have the populated attributes returned based on the values I've selected in the ActiveRecord query.
In your case you should be able to use fields() method:
$attributes = $model->getAttributes($model->fields());
Note that fields() does not guarantee this behavior if you change model after find - unsetting (unset($model->product_id)) or setting ($model->some_field = 'some value') attributes will affect result of fields().

How to get db field from class object

I execute this update statement which works fine.
$g = Group::where([['id', $idgroup], ['admin', $currentUser]]);
$g->update(['admin' => $iduser]);
After this statement I need the field name from my Group object. But with the following statement I get Undefined property: Illuminate\Database\Eloquent\Builder::$name.
$groupname = $g->name;
Group is my model (DB table) which contains the field id, name, admin. How can I get the field name from this object?
Thank you
Without finder it will return you a query builder but when you need to fetch model instance then apply get (for Collection of model) or first(for single model instance) on query builder. For your case you need to call first() method like this
$g = Group::where([['id', $idgroup], ['admin', $currentUser]])->first();
$g->update(['admin' => $iduser]);
Then you can do
$groupname = $g->name;
You can check the details here for retrieving model
If you want to get one record from database then use first():
$g = Group::where([['id', $idgroup], ['admin', $currentUser]])->first();
$g->name; //it will output name
OR
If you want to get all record from database then use get():
$groups = Group::where([['id', $idgroup], ['admin', $currentUser]])->get();
To get name from here you will have to run loop because get() will return a collection
foreach($groups as $g){
$g->name; //it will give name
}
Your $g variable is a Illuminate\Database\Eloquent\Builder which represent a query, not a single object, which means it doesn't have the Group properties. It's very likely you have more than one record under $g, you have two options retrieve all the names fields like this
$g->pluck('name')
This will give you a collection of all the names, or if you're certain there's one single object, you can do $g->first()->name, it will give you the name of the first object (only one if you're sure there is only one object)

Fetch the value of a given column in Eloquent

I am trying to get the value of a single column using Eloquent:
MyModel::where('field', 'foo')->get(['id']); // returns [{"id":1}]
MyModel::where('field', 'foo')->select('id')->first() // returns {"id":1}
However, I am getting anything but the value 1. How can get that 1?
NOTE: It is possible that the record with field of foo does not exist in the table!
EDIT
I am ideally looking for a single statement that either returns the value (e.g. 1) or fails with a '' or null or other. To give you some more context, this is actually used in Validator::make in one of the rules:
'input' => 'exists:table,some_field,id,' . my_query_above...
EDIT 2
Using Adiasz's answer, I found that MyModel::where('field', 'foo')->value('id') does exactly what I need: returns an integer value or an empty string (when failed).
Laravel is intuitive framework... if you want value just call value() method.
MyModel::find(PK)->value('id');
MyModel::where('field', 'foo')->first()->value('id');
You're using the Eloquent query builder, so by default, it'll return an Eloquent model with only the value you wish.
The method you're looking for is pluck() which exists in the normal query builder (of which the Eloquent one extends) so your code should look as follows:
MyModel::where('field', 'foo')->pluck('id'); // returns [1]
The value() method that is being used in the other answers is an Eloquent model method. Using that means that the framework queries the database, hydrates the model and then returns only that value. You can save yourself a few keystrokes and few CPU cycles by using pluck() and have it handled simply in one query.

Eloquent Collections Where Condition

I want to get alternative products pictures.
dd($alternativeProduct->pictures);
When die and dump i get this result.
I need to get only the picture which is main. If main equals to 1. It is main picture.
When I write
dd($alternativeProduct->pictures->where('main', 1))
I got an empty array.
Here is my relation with Product and Picture relation
public function pictures(){
return $this->hasMany('App\Models\ProductPicture');
}
What can i do ?
The where method in a collection has three parameters: $key, $value and $strict, the last one defaults to true if not passed when calling the method. When $strict is true the comparison is done via === which does not do type coercion, meaning "1" === 1 is false.
From your dump data, I can see that "main" => "1" which means it's a string and you're passing an integer 1 to the where method, resulting in what I described above as a false condition and returning an empty result set. You can fix that by disabling strict comparison:
$alternativeProduct->pictures->where('main', 1, false);
// or the equivalent helper whereLoose
$alternativeProduct->pictures->whereLoose('main', 1);
Or passing a string as the value:
$alternativeProduct->pictures->where('main', "1");
That being said, if that's the only place you're using that collection in that request's context, I suggest that you filter the results at the database level, not after they are fetched, like so:
$alternativeProduct->pictures()->where('main', 1)->get();
Accessing the relations as a method ->pictures(), instead of as a property ->pictures, will return a Query Builder instance that allows you to add conditions to the database query and only fetch the actual items you need from the database, instead of fetching them all and filtering them out afterwards.
You may want to use whereLoose instead:
dd($alternativeProduct->pictures->whereLoose('main', 1))
From the Laravel docs:
The where method uses strict comparisons when checking item values. Use the whereLoose method to filter using "loose" comparisons.

Get Attribute from Elequent where clause

So I have the following where clause where I am searching the name "Gold" inside of my tier table.
$tier = Tier::where(['name' => 'Gold'])->get(['id']);
dd($tier);
When I do the above the "id" is listed inside of the attributes but when I do as below it gives me
Undefined property: Illuminate\Database\Eloquent\Collection::$id
dd($tier->id);
All I want is to get the Id of the record where the name is "Gold".
To get actual model you should use first() instead of get(). Because there are potentially multiple rows that match get() will return a collection.
$tier = Tier::where(['name' => 'Gold'])->first(['id']);
dd($tier->id);
If you really need only the id there's even a simpler function to that, pluck().
It will get one attribute from the first result:
$id = Tier::where(['name' => 'Gold'])->pluck('id');

Resources