eloquent - get sum of relationship column - laravel

My models are structured this way:
Stock (hasMany) StockItem
StockItem (belongsTo) Shipment
Shipment (hasMany) StockItem
- item_cost = double
So I'm trying to get all items and sum of their total_cost (located at Shipment model).
public function getStockItemsTotalCost($stockItemId=null){
$q = $this->stockItems()->where('id','=',$stockItemId)->with('shipment')->withCount([
'shipment AS cost_total' => function ($query) {
$query->select(DB::raw("SUM(item_cost) AS cost_total"));
}
]);
print_r($q);exit;
}
This is returning Allowed memory size of 268435456 bytes exhausted error.
Is this the best way to do this query?
I got a small progress
public function getStockItemSumCost($stockItemId){
$q = $this->stockItems()->whereHas('stockPart', function($q) use ($stockItemId) {
return $q->where('id', '=', $stockItemId);
})->with('shipment')
->get();
print_r($q->toArray());exit;
}
This is returning a StockItem list with Shipment inside each one...
What I need is a single column with the SUM() of all Shipment.item_cost column.

Solved with:
public function getStockItemSumCost($stockItemId){
$q = $this->stockItems()
->whereHas('stockPart', function($q) use ($stockItemId) { return $q->where('id', '=', $stockItemId); })
->join('shipments', 'stock_items.shipment_id', '=', 'shipments.id')
->sum('item_cost');
return $q;
}

Related

Filter items based on average rating in Laravel

I am trying to get all items with an average rating that is greater than or equal to the input value.
Here is my attempt:
$filters['ratings'] = array_filter($filters['ratings']);
if ($filters['ratings']) {
$this->model = $this->model
->leftJoin('ratings', 'ratings.item_id', '=', 'items.id')
->where(function ($query) use ($filters) {
return $query
->whereIn('ratings.star', $filters['reviews']);
});
}
But with the above code, I get all items that have a rating of the input value and not as an average rating greater than or equal to the input value. Which is wrong.
Here's is also another try:
$filters['ratings'] = array_filter($filters['ratings']);
if ($filters['ratings']) {
$this->model = $this->model
->leftJoin('ratings', 'ratings.item_id', '=', 'items.id')
->where(function ($query) use ($filters) {
return $query
->select('ratings.item_id')
->selectRaw('AVG(ratings.star) AS rating_avg')
->groupBy('ratings.item_id')
->havingRaw('AVG(ratings.star) >= ?', $filters['ratings']);
});
}
But the above returns all items with no filter. I have read a bunch of posts related to this issue but can't really figure it out. No thanks to my beginners knowledge of relationships. Can anyone show me what to do?
Does your model have a relationship with Rating ? If so, you could just use withAvg to get your average.
# Your model (Item ?)
public function ratings()
{
return $this->hasMany(Rating::class, 'item_id', 'id');
}
https://laravel.com/docs/eloquent-relationships#other-aggregate-functions
$this->model
->withAvg('ratings', 'star')
->where('ratings_avg_star', '>=', $filters['ratings']);
$this->model
->withAvg('ratings as your_alias_here', 'star')
->where('your_alias_here', '>=', $filters['ratings']);

How to implement relationship in where condition Laravel's eloquent?

I have two tables:
Students
Results
The two tables have one to may relationship.
Student model:
public function results()
{
return $this->hasMany('App\Result');
}
Result model:
public function student()
{
return $this->belongsTo('App\Student');
}
In the students table I have a field called average_score.
How can I execute the following query, this is not working it says "Undefined property: Illuminate\Database\Query\Builder::$student":
$data = Result::with('student')->where('score', '>=', function($q){
$average_score = $q->student->average_score;
return $average_score;
})->get();
In order to get the results that are only higher or equal to than the "average_score".
If score and average_score are columns of the same table (student), try this;
$data = Result::with(['student' => function ($query) {
$query->whereColumn('score', '>=', 'average_score');
}])->get();
If score and average_score are columns of the different tables, try this;
$data = Result::with('student')->whereHas('student', function($q) {
$q->whereColumn('students.average_score', '<=', 'results.score');
})->get();
You can use whereHas to find those results:
$data = Result::with('student')->whereHas('student', function($q) {
$q->whereColumn('average_score', '<=', 'Results.score');
})->get();
get value from column Results.average_score then compare to student.score:
Result::with([
'student' => function($query) {
$query->where("score",">=", "Results.average_score"););
}
])

How get data in method from others tables?

This is my simple db schema. Now in my group table i have 'group_A' and 'group_B'. In questions table i have 10 questions 5 for group_A and 5 for group_B. Users table is one user with ID = 1. What i try to do is get data like this:
SELECT answer
FROM answers
JOIN questions q ON (q.id = answer.question_id)
JOIN group g ON (g.id = q.group_id)
WHERE user_id = 1 AND g = 'group_A'
I have model in users class and i would like create method to get answers depend from group:
public function getAnswers($group) {
return $this->hasMany('App\Answers', 'question_id', 'id');
}
How can i get this in that method ? Should i create method group in questions class ?
Not sure about how to define such relation in model which takes input parameter but you could define a scope in your model to get the answers filtered by group like
class Answer extends Model {
public function user()
{
return $this->belongsTo('App\User');
}
public function question()
{
return $this->belongsTo('App\Question');
}
public function scopeGroup($query, $name)
{
return $query->whereHas('question.group', function($q) use($name){
$q->where('name', '=', $name);
});
}
}
I assume in answers model you have defined relationship for question and similarly in question model you have defined relationship for group model.
In query builder you could write it as
$answers = Answer::group('group_A')
->whereHas('user', function($q) use($userid){
$q->where('id', '=', $userid);
})->get();
Or you could apply filter on eager loaded relations as
$users = User::with(['answers' => function($query)
{
$query->whereHas('question.group', function($q){
$q->where('name', '=', 'group_A');
});
}])->get();
If you already have user object you can get answers for specific group as
$answers = $user->answers()
->whereHas('question.group', function($q){
$q->where('name', '=', 'group_A');
})->get();

Laravel 5.5 - How to fetch related products in many to many relation

I am developing a e-commerce website.
I have 3 tables
1.prodcts table
2.conditions table
3.condition_product table
A product can have many conditions.
particular condition belongs to many products.
Now I have a product which belongs to 3 conditions.
I want to get all related products which belongs to these 3 conditions.
How can i achieve this?
product.php
public function conditions(){
return $this->belongsToMany(Condition::class)->withTimestamps();
}
condition.php
public function products(){
return $this->belongsToMany(Product::class)->withTimestamps();
}
This is my code:
public function productDetails(Product $product)
{
$a = $product->id;
$relatedProducts = Product::whereHas('conditions.products', function($q) use($a) {
$q->where('id', $a);
})
->get();
return view('pages/product', compact('product','relatedProducts'));
}
But I am getting error.
Use the whereHas() method with nested relationships:
Product::whereHas('conditions.products', function($q) use($productId) {
$q->where('id', $productId);
})
->get();
This will give you products that have conditions of a specified product.
Alternatively, you could do this:
$conditionIds = Product::find($productId)->conditions->pluck('id');
$products = Product::whereHas('conditions', function($q) use($conditionIds) {
$q->whereIn('id', $conditionIds);
})
->get();
A good practice is to always specify witch table the 'id' field is referencing too, because if not, it could both reference the 'conditions' table or the 'products' table 'id' field :
$relatedProducts = Product::whereHas('conditions.products', function($q) use($a) {
$q->where('products.id', $a);
})
->get();
You could see this answer about SQL ambiguous column name.

Laravel 4 Eager Loading constraints

I want to get all Items (topics) WITH their comments, if comments user_id = $id. I try something like this, but it isn't working. So if Item hasn't got any comment with user_id = $id, then I don't need this Item.
In DiscussionsItem model I have methode:
public function discussionsComments() {
return $this->hasMany('DiscussionsComment', 'discussionsitem_id');
}
My query in controller is like this:
$items = DiscussionsItem::whereBrandId($brand_id)
->whereBrandCountryId($country_id)
->with(['discussionsComments' => function($query) use ($id) {
$query->where('user_id', '=', $id);
}])
->whereHas('discussionsComments', function($query) use ($id) {
$query->where('user_id', '=', $id);
})
->with(['views' => function($query) use ($id) {
$query->where('user_id', $id)->count();
}])
->orderBy('created_at', 'DESC')->get();
My problem is that I get items with comments, where comments user_id != $id.
P.S. I need to take 5 comments, but I cant imagine how to do that, because ->take(5) in my eager load is not working.
You can do a custom scope, or just limit the amount returned by your relation:
public function discussionsComments() {
return $this->hasMany('DiscussionsComment', 'discussionsitem_id')
->latest()
->take(5);
}

Resources