Get sum of nested Collection failed Laravel Eloquent - laravel

I need the sum of nested relationship collection. When I try this code, the field could not be found.
return Offer::where("id", $offer_id)
->with(['rooms.products' => function($sql) use ($product_id) {
$sql->where('product_id', $product_id);
}]
)->sum("value");
Value field exists in rooms.products table.
Thanks in Advance

Right now, the sum() method will look for the value column in the offers table, while you need the value in the products table. Try this approach:
return Product::where('product_id', $product_id)
->whereHas('room.offer', function($sql) use ($offer_id) {
$sql->where("id", $offer_id)
})->sum("value");

Related

Why does groupBy() work but Count() does not in laravel eloquent model function?

I need to get counts of all the records based on belongsToMany relationship. normally I can use groupBy() in a function inside the model. but if I use count() or withCount() inside a model function, i get the error as followed:
function code:
public function TaskCount(){
return $this->belongsToMany(User::class)->count();
}
Error message:
Symfony\Component\Debug\Exception\FatalThrowableError: Call to a member function addEagerConstraints() on int in file /Users/dragonar/Dev/iyw/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php on line 560
If I do the following...
public function TaskCount(){
return $this->belongsToMany(User::class)->Count();
}
//expected record is 4(int)
//output is 4(array) user records.
...it gives me data but like 4 records of the user instead of a number 4. The user data is useless. The only thing needed is totalCount for those records.
Relationship methods have to return Relation type objects. You are returning the result of a query, count() returns a number not the Relation object / Builder. Remove the count from that statement you are returning. Renamed the relationship tasks here.
public function tasks()
{
return $this->belongsToMany(User::class);
// this returns a Relation type object a BelongsToMany object
}
Where you need to use that relationship you can then use count:
$something->tasks()->count();
Or you can load the count of the relationship using loadCount:
$something->loadCount('tasks');
$something->tasks_count;
Or via eager loading for a collection:
$results = Something::withCount('tasks')->get();
foreach ($results as $model) {
echo $model->tasks_count;
}
If you really wanted to you could create an accessor to get the count as well, you just may want to avoid the N+1 issue by preloading the relationship and using the dynamic property to access it in the accessor.
These relation objects are Builders. When you called groupBy on it previously that is returning the Builder, it isn't executing the query. You can add where conditions and order by statements because they are just building the query, not executing it, they return the builder you are calling the method on.
Laravel 6.x Docs - Eloquent - Relationships - Counting Related Models withCount loadCount
Why not use: Task::all()->count(); ?
you can use the withCount method while calling relation like this
User::withCount('images')->get();
You can add get the data and just count it.
public function TaskCount(){
return $this->belongsToMany(User::class)->get()->count();
}
You can call it like
$taskCount = $task->TaskCount();

Does "where" works with mutattors in eloquent?

I have a mutattor in an eloquent model that generates a "status" atribute.
public function getStatusAttribute(){
if(){
return "enabled";
}
else
{
return "disabled";
}
}
Can I use?
$query = Anuncio::query();
$query->where('status', "enabled" );
return $query->get();
I seems that I cannot. I getting "status" column not defined. How can I get around this problem?
No doesn't works it works on model level
you can use after query from database in collection result
No, when you are doing a query you are asking the database, therefor there needs to be a column status.
There is a way, retrieve x elements from the database and use the Laravel Collection method where(). This is not optimal if you have many elements in teh database, it will retrieve all of them.
Anuncio::all()->where('status', 'enabled')->all();

sort laravel eloquent by custom (appended) attribute

I have a model with a custom attribute like this
public function getOpenStatusAttribute()
{
//some logic...
//returns '1-order' or '2-pre-order' or '3-closed'
}
Now i want to sort the collection in the eloquent query. I use order by name etc.. those are columns in the table but i want to order it by the custom attribute first, and then by name etc..
is it possible to do this in the query? or do i have to loop the collection and do some resorting ?
ok so the solution is:
->sortBy(['open_status'])->sortBy(['name']);
after the ->get()
hope it can help someone in the future

How to re-order an Eloquent collection?

I've got a collection of records retrieved via a relationship, and I'd like to order them by the created_at field. Is this possible in Eloquent?
Here is how I am retrieving the collection:
$skills = $employee->skills;
I'd like to order this $skills collection by their creation. I've tried $skills->orderBy('created_at', 'desc'); but the Collection class does not have an orderBy method.
I guess this problem is very simple and I'm missing something..
You can do this in two ways. Either you can orderBy your results while query, as in
$employee->skills()->orderBy('created_at', 'desc')->get();
OR
You can use sortBy and sortByDesc on your collection
The reason this is failing is that orderBy is a query method not a collection method.
If you used $skills = $employee->skills()->orderBy('created_at', 'desc')->get();, this would query the skills in the order you want.
Alternatively, if you already had a collection that you wanted to re-order, you could use the sortBy or sortByDesc methods.
You need to add the orderBy constraint on the query instead of the relationship.
For e.g,
$employees = Employee::where('salary', '>', '50000') // just an example
->with('skills') // eager loading the relationship
->orderBy('created_at', 'desc')
->get();
and then:
foreach($employees as $employee)
{
var_dump($employee->skill);
}
If you want the results to always be ordered by a field, you can specify that on the relationship:
Employee.php
public function skills() {
return $this->hasMany(Skills::class)->orderBy('created_at');
}
If you just want to order them sometimes, you can use orderBy(), but on the relationship, not the property:
$skills = $employee->skills()->orderBy('created_at')->get();
Collection has sortBy and sortByDesc
$skills = $skills->sortBy('created_at');
$skills = $skills->sortByDesc('created_at');
This Stackoverflow question askes how to order an Eloquent collection. However, I would like to propose a different solution to use instead given the example in the question. I would like to recommend to use an ordering on the query itself for performance reasons.
Like #Don't Panic proposes you can specify a default ordering on the relationship for great reusability convenience:
app/Models/Employee.php
public function skills() {
return $this->hasMany(Skills::class)->orderBy('created_at');
}
However, if you have already set an ordering on your query like we do in the code above, any additional orderings will be ignored. So that is a bummer if you want to use a different sorting in another situation. To overwrite this default ordering and re-order the query with a new ordering, one needs to use the reorder() method. For example:
// Get a Collections of Skill-models ordered by the oldest skill first.
$skills = $employee->skills()->reorder()->orderByDesc('created_at')->get();
// Same result as the previous example, but different syntax.
$skills = $employee->skills()->reorder()->oldest()->get();
// Or just give some arguments to the reorder() method directly:
$skills = $employee->skills()->reorder('created_at', 'desc')->get();

A More Efficient Way to Query Relationships

I'm building a query on a table (product).
product has a foreign key, wrapper_id,
each wrapper, in turn, has a foreign key wrapper_classification_id.
This part of the query needs to modify the search based on an array of wrapper_classification_id's that may have been passed as input parameters.
This is what I'm doing:
// $query is already being built from above this line....
// wrapper_classification_id in input is an array
if (Input::get('wrapper_classification_id'))
{
$wrappers = Wrapper::whereIn('wrapper_classification_id', Input::get('wrapper_classification_id'))->get();
$wrapperArray = [];
foreach($wrappers as $wrapper) {
$wrapperArray[] = $wrapper->id;
}
$query->whereIn('wrapper_id', $wrapperArray );
}
Is there a more efficient way of approaching this scenario where you're querying against a value in a relationship?
Thanks in advance,
Rich
$query->whereIn(
'wrapper_id',
DB::raw(
Wrapper::whereIn(
'wrapper_classification_id',
Input::get('wrapper_classification_id')
)->toSql()
)
);
Or, try joins.
$query->join('wrapper AS w', 'w.wrapper_classification_id', 'IN', Input::get('wrapper_classification_id'))
->whereIn('product.wrapper_id', 'w.id');
Not sure if that exact code would work though.
Assuming the product model has a wrapper relationship:
if (Input::has('wrapper_classification_id'))
{
$query->whereHas('wrapper', function($q){
$q->whereIn('wrapper_classification_id', Input::get('wrapper_classification_id'));
});
}

Resources