Laravel Query Item from table with selected relationships from another table - laravel

There are two tables which are related - Product and SalesForecast. I want to query products of a certain supplier with sales forecast from a period.
Relationships are established. My query is as follows:
$products = Product::where('supplier_id', $supplier)
->whereHas('stock_forecasts_fk', function ($query) use ($begin, $end) {
$query->whereDate('date', '>', $begin);
$query->whereDate('date', '<', $end);
})
->get();
The relationship in the product model is as follows:
public function stock_forecasts_fk()
{
return $this->hasMany('App\Models\StockForecast');
}
This query does not work 100%. I want all products of that supplier to come (whether they have forecast or not). If they have a forecast, I need forecasts for that period only. Otherwise, that product will have not, forecast. But All product needs to come. Can someone advise how to fix this query so the result is as follows:
All products of suppliers come with relevant forecasts of between begin and end dates. If there is no forecast, then the product can come with no forecast.

You have to query with eager loading the relationship. with whereHas, only matching related value products will be collected. use with closure to query in related table
$products = Product::with(['stock_forecasts_fk' => function($query) {
$query->whereDate('date', '>', $begin)
->whereDate('date', '<', $end);
}])
->where('supplier_id', $supplier)
->get();

You can use with and pass a closure with your conditions to it:
$products = Product::where('supplier_id', $supplier)
->with(['stock_forecasts_fk' => function ($query) use ($begin, $end) {
$query->whereDate('date', '>', $begin)
->whereDate('date', '<', $end);
}])
->get();
From the docs:
Constraining Eager Loads
Sometimes you may wish to eager load a relationship, but also specify
additional query conditions for the eager loading query. Here's an
example:
$users = App\Models\User::with(['posts' => function ($query) {
$query->where('title', 'like', '%first%');
}])->get();
In this example, Eloquent will only eager load posts where the post's
title column contains the word first.

From the document that I've read here.
whereHas only includes Product which has stock_forecasts_fk, and discards Product which doesn't.
To select all product regardless it has stock_forecasts_fk, you should've use with.
$products = Product::where('supplier_id', $supplier)
->with([
'stock_forecasts_fk' => function ($query) use ($begin, $end) {
$query->whereDate('date', '>', $begin);
$query->whereDate('date', '<', $end);
}
])
->get();

Related

Laravel model get all results, but if model has relation then check for certain condition

Assume I have book models and borrow models. They are one-to-many relationships. I want to get a collection of all the books but return the result if the book is borrowed within the current week. But if it's not, then don't display. I've been trying whereHas(), but I think it didn't suit what I'm looking for.
$displayed_books = Books::orderBy('updated_at', 'DESC')
->ifHasborrow('borrow', function ($query) {
$query->where('start_date', '>=', currentweek())
->where('end_date', "<=", currentweek());
})
$displayedBooks = Books::orderBy('updated_at', 'DESC')
->where(function ($query) {
$query->whereHas('borrow', function($query) {
$now = Carbon::now();
$query->where([
['start_date', '>=', $now->startOfWeek()],
['end_date', '<=', $now->endOfWeek()]
]);
$query->orWhereDoesntHave('borrow');
})
})->get();

Laravel eloquont query takes long time to fetch data

I have a query just to retrieve the data, when I try to fetch 10k data from below query it will take more than 1.7mins. It that reasonable or how can I improve this time.
Vehicles::leftJoin('vehicle_details', 'vehicle_details.id', 'vehicles.vehi_details_id')
->leftJoin('vehicle_extras', 'vehicle_extras.vehicle_id','vehicles.id')
->leftJoin('reasons', 'reasons.vehicle_id', 'vehicles.id')
->leftJoin('revised_vehicles', 'revised_vehicles.vehicle_id','vehicles.id')
->leftJoin('makes', 'makes.model_id', 'vehicle_details.model_id')
->leftJoin('bidder_invoices', 'bidder_invoices.vehicle_id', '=', 'vehicles.id')
->leftJoin('bidding_views', 'bidding_views.vehicle_id', '=', 'vehicles.id')
->leftJoin('bidder_prs', 'bidder_prs.vehicle_id', '=', 'vehicles.id')
->leftJoin('bidder_remarks', 'bidder_remarks.vehicle_id', '=', 'vehicles.id')
->leftJoin('vehicle_pins', 'vehicle_pins.vehicle_id', '=', 'vehicles.id')
->leftJoin('translations', 'translations.vehicle_id', '=', 'vehicles.id')
->where(function ($query) {
$query->where('vehicles.approve', '=', 1)
->orWhereNull('vehicles.approve');
})
->orderBy('vehicles.site', 'ASC')
->orderBy('vehicles.auc_time', 'ASC')
->select([
'vehicles.*',
'vehicle_details.color as vehi_color',
'vehicle_details.grade as vehi_grade',
'vehicle_details.images',
'makes.model_name',
'vehicle_extras.id as extra_details_id',
'vehicle_extras.ic_remarks',
'vehicle_extras.bidder_adj',
'reasons.fob_issue',
'reasons.fob_approve',
'reasons.decline_remark',
'reasons.tc_issue',
'reasons.fob_overruling_remark',
'bidder_invoices.invoice_name',
'bidding_views.reason as bidder_reason',
'bidder_prs.bidder_mb as bidder_mb',
'bidder_remarks.bidder_remarks as bidder_remarks_daily',
'revised_vehicles.old_mb_round',
'translations.assigned_translator'
])
->paginate(10000);
I have also added indexes for every join column. Also I have used MySQL as my DB
you can use the Eloquent relationships as methods in your Eloquent model classes refer Eloquent Relationship
if the relation is 1:1 use hasOne
if the relationship is 1:M use has Many
if the relationship is M:1 use belongs to
in your Vehicle model
for example
for 1:1
public function vehicleDetails() {
return $this->hasOne(VehicleDetail::class,'vehi_details_id','id');
}
for 1:M
public function VehicleExtras() {
return $this->hasMany(VehicleExtras::class,'id','vehicle_id');
}
for M:1
public function VehicleMakes() {
return $this->belongsTo(VehicleMakes::class,'model_id','model_id');
}
and after making all the relationship you can get the vehicle table with your filters as follows
$data['vehicles'] = Vehicles::whereNull('vehicles.deleted_at')
->where(function ($query) {
$query->where('vehicles.multiple_po', 0)
->orWhereNull('vehicles.multiple_po');
})
->where(function ($query) {
$query->where('vehicles.approve', '=', 1)
->orWhereNull('vehicles.approve');
})
->orderBy('vehicles.site', 'ASC')
->orderBy('vehicles.auc_time', 'ASC')
->paginate(10000);
then,
you can the vehicles in the blade as normal and if you needed to call the relationship table belongs to a certain id,
to get a description from vehicle details you can access as follows
$data['vehicles']->vehicleDetails->description
or to get the of a make of the vehicle from the makes table
$data['vehicles']->VehicleMakes->name

laravel eloquent Filter relations Model values with Base Model id

$subTasks = SubTask::with(['task'])
->with(['users.subTaskInfos' => function ($q) {
$q->orderBy('created_at', 'desc');
$q->where('sub_task_id', '=', ?);
}])
->where('active', 1)
->get();
I want to pass Base Model SubTask id in question mark section to filter out relations data and relation collection of data. Can anyone help?
One way to achieve what you are attempting is to lazy eager load users.subTaskInfos - keeping the number of queries the same
$subtasks = SubTask::with(['task'])->where('active', 1)->get();
$subtasks->load(['users.subTaskInfos' => function($q) use($subtasks) {
$q->whereIn('sub_task_id', $subtasks->pluck('id')
->orderByDesc('created_at');
});

Getting a relationship with a condition?

I have category and article tables, they are in a many to many relationship. i have the relationships set up on the models.
How can I get all articles where a category id is = 1?
I've tried:
Article::whereHas('category', function ($query) use ($id) {
$query->where('category_id', $id);
})->get();
The above works, but seems clunky, is there a more efficient way in eloquent to do this?
You can use eager loading technique.
Article::with(['category', function ($query) use ($id) {
$query->where('category_id', $id);
}])->get();
The following groupBy code is assuming that Category::find($id)->articles is not an option for whatever reason.
What about groupBy?
Article::groupBy('category')
->having('category', $id)
->get();
whereHas will return articles that match that condition, but won’t filter articles then fetched by the same conditions. You could therefore create an anonymous function that contains the constraints, and pass it to both the whereHas and with methods:
$filter = function ($query) use ($categoryId) {
$query->where('category_id', '=', $categoryId);
};
Article::whereHas('category', $filter)
->with(['category' => $filter])
->get();

How to select instances for specific 'n' on m:n relationships?

I have two models Teacher and Category. These two have Many to Many relationship.
I want to get those teachers who have one category equal to "OLevels". Which method of eloquent is used for it or is there any other way I can get it?
Is there anyway to get it as:
$teachers = Teacher::where('category', '=', 'OLevels')->get();
You can use whereHas for that:
$category = 'OLevels';
$teachers = Teacher::whereHas('category', function($q) use ($category){
$q->where('name', $category);
})->get();
You could make use of eager loading with constraints
$teachers = Teacher::with(['category' => function($query)
{
$query->where('category', '=', 'OLevels');
}])->get();
Further info in the documentation.

Resources