Optimising Eloquent Relational Queries - laravel

We are looking at optimising our Laravel Eloquent queries on our search page, we have got the number of queries executed down to a good number.
However, we do see a consistent query that is running based on a relationship. That query is :
select * from `property_types` where `property_types`.`id` = 7 limit 1
This query is running if there's a unique property type per property.
I've taken a look at has and with queries in Laravel.
The relationship is set :
public function type()
{
return $this->belongsTo('App\Models\PropertyType', 'property_type_id', 'id');
}
We create a Property Title and URL using the relationship, so for a URL we use :
public function getUrlAttribute($pdf = false)
{
$property_route = ($pdf) ? 'property-pdf' : 'property';
// Format: /property/[NUMBER OF BEDS]-bed-[PROPERTY TYPE]-[FOR-SALE / TO-RENT]-in-[CITY]/[PROPERTY ID]
$items = [];
if ($this->beds) $items[] = $this->beds.' bed';
$items[] = $this->type->name ?? 'property';
}
So everytime a matching property is found, It's going back over that relationship and we're seeing the property_types query running up to 15 times per page.
Is there any suggestions on optimising this?

Related

Laravel eloquent relationships : difference betwen auth()->user()->favorites()->get() and auth()->user()->favorites

User Model :
public function favorites()
{
return $this->hasMany(Favorite::class);
}
Controller :
//return auth()->user()->favorites()->get();
//return auth()->user()->favorites;
can someone explain the difference betwen the two codes ?
and when i should use each one?
Let me explain it practically:
If the above query looks like this
// return auth()->user()->favorites()->where('active', true)->get();
// return auth()->user()->favorites->where('active', true);
then its SQL query be like this
// SQL of first query
select * from `favorites` where `favorites`.`user_id` = 1 and `favorites`.`user_id` is not null and `active` = 1
// SQL of second query
select * from `favorites` where `favorites`.`user_id` = 1 and `favorites`.`user_id` is not null
Did you see differences in two SQL?
Now, Let me explain it in words:
If you add some condition after favorites() like above, it will fetch all filtered result from database. But if you add some condition after favorites it will filter the result which is already fetched from the database.
Although, I'm not good at explanations, I hope you understand clearly between the two codes.

Laravel GroupBy with Sum added to Object

I searched all other questions before. I have to simple groupBy select and get sum out of column. But how to make 1 query out of this ( without merge ). Possible?
$Todo = Todo::selectRaw('sum(estimated_time) as amount')->groupBy('user_name')->get();
$Todo = Todo::get()->groupBy('user_name');
I would suggest you avoid using any raw SQL statements in Laravel.
If your goal is to get the sum of the estimated duration of all todos for each user, you can use eager loading.
For example you could first query all your users and eager load the todos.
$users = User::query()->with('todos')->get();
And then you can retrieve the sum of the estimated duration for all todos.
foreach($users as $user) {
$user->totalEstimatedTodoTime = $user->todos->sum('estimated_time')
}
If you use the total estimated todo time of a user often. You could even define an accessor
For example in your user model:
public function getTotalEstimatedTodoTimeAttribute() {
return $this->todos->sum('estimated_time');
}
Then you can retrieve the value like this:
$user->totalEstimatedTodoTime
Write this code in Model :
public function setXXXAttribute($value)
{
$this->XXX= Model::where('user_name' , $this->user_name)->sum('estimated_time');
}
public function getXXXAttribute($value)
{
return $this->XXX
}

many to many and one to many relationship in Eloquent

I have three tables: "courriers" which is connected with "reponses" by one to-many relationship (1 courrier could have many reponses), and "structures" which is connected with "courriers" by many-to-many relationship
I want to find the courriers which are connected to a certain structure and doesn't have a reponse in table "reponses".
For example, for the structures "DMO" that has 1 as identifiant in "structures", I wish find the courriers that belongs to this structure and doesn't appear in "reponses".
Am using Laravel 8, I want to do this with Eloquent ORM.
Am trying this
public function dmoDG()
{
$structure = Structure::find(1);
$cou = $structure->courriers;
$courr = $cou->where('repondre','=',1)-
>where('dmo_sec','<>',NULL);
$courriers = $courr->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Method Illuminate\Database\Eloquent\Collection::doesntHave does not exist.
When you use $model->relation, it fetches all related records and returns them as a Collection.
If you want to use query builder on a relation, you need to use it as a method: $model->relation()
So, if you access your relation as a property, you got Collection.
But if you access your relation as a method, you got query builder and add your where clauses on it.
In your example;
public function dmoDG()
{
$structure = Structure::find(1);
// $cou = $structure->courriers; // for using without parentheses you got a collection, not a query builder
$cou = $structure->courriers(); // now you will have a query builder and Where clauses will work on this
$courr = $cou->where('repondre', '=', 1)->where('dmo_sec', '<>', NULL);
$courriers = $courr->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Actually you can pipe them to one liner:
public function dmoDG()
{
$structure = Structure::find(1);
$courriers = $structure->courriers()->where('repondre', '=', 1)->where('dmo_sec', '<>', NULL)->doesntHave('reponses')->get();
return view("DG\dmoDG", compact('courriers'));
}
Make sure your relations and column names correctly specified.

Laravel get result from query in side query by Eloquent in one object

I have two tables:
main_presentations
so here i have "id" and "isEnabled";
child_presentations
And here i have "id" , "isEnabled" and "idParent";
I want to select in one object this is my code:
public function MainSlider(MainPresentation $MainPresentations, ChildPresentation $ChildPresentations)
{
$MainPresentations = MainPresentation::where('isEnabled', true)->get();
foreach ($MainPresentations as $MainPresentation) {
$AnArray[] = ChildPresentation::where([
['idParent', $MainPresentation['id']],
['isEnabled', true]
])->get();
}
return $AnArray;
}
but this is the result:
enter image description here
What you are doing is executing a query per result, which can be ineffective when it starts getting bigger.
You can:
Use querybuilder
As it follows, you just build a query starting on ChildPresentation, set a relation to MainPresentation table by id and get the collection
public function MainSlider()
{
$childPresentations = ChildPresentation::join('main_presentations','main_presentations.id','child_presentations.idParent')
->where('child_presentations.isEnabled', true)->where('main_presentations.isEnabled', true)->get();
return $childPresentations;
}
If you want all the MainPresentations with their respective ChildPresentations, only the enables ones.
You can take advantage of Laravel relationships and eager loading.
https://laravel.com/docs/5.6/eloquent-relationships
First, set the relationships in your MainPresentation model
In MainPresentation.php
public function childPresentation {
return $this->hasMany('App\ChildPresentation', 'idParent', 'id');
}
Your MainSlider function would be:
(Btw, no idea why you're receiving two arguments if you're overriding them but doesn't matter)
public function MainSlider() {
$mainPresentations = MainPresentation::with(['childPresentations' => function ($advancedWith) {
child_presentation.isEnabled is true
$advancedWith->where('isEnabled', true);
}])
->where('isEnabled', true)->get()->toArray();
return $mainPresentations;
}
This will return an array of MainPresentations that contain an array of child_presentations, with all their childs.
This translates to two queries:
Select * from main_presentations where isEnabled = true;
Select * from child_presentations where isEnabled= true and id in (in the first query);
Laravel then does background work to create the structure you desire when you write ->toArray()
Note: If you have a $visible array in your MainPresentation model, be sure to add: 'childPresentation' to it, otherwise the toArray will not agregage the childs to the parent.
Second note: I advise following some standards whenever you're writing code, usually functions are named camelCase and variables are camelCase.

Retrieving records from database using eloquent with optional query parameters

i have the following block of code in my Resource Controller:
$travel_company_id = Input::get('travel_company_id');
$transport_type = Input::get('transport_type');
$route_type = Input::get('route_type');
$travelRoutes = TravelRoute::where('travel_company_id', $travel_company_id)
->where('transport_type', $transport_type)
->where('route_type', $route_type)
->get();
Now what this does is it gets travelRoutes based on the parameters supplied. What i want is for it to do is perform a search based on the available parameters, that way if $route_type is empty the search will be performed only on travel_company_id and transport type.
Also if all the parameters are empty then it will simply do a get and return all available records.
I know i can do this with lots of if statements but then if i add a new parameter on the frontend i will have to add it to the backend as well, I was wondering if there was a much simpler and shorter way to do this in laravel.
The where method accepts an array of constraints:
$constraints = array_only(Input::all(), [
'travel_company_id',
'transport_type',
'route_type',
]);
$routes = TravelRoute::where($constraints)->get();
Warning: do not use Input::only() instead of array_only(). They're not the same.
Input::only() fills in any missing items with null, which is not what you want here.
This is pretty hacky and if you spend some time developing a solution I'm sure it could be much nicer. This assumes all the fields in the getSearchFields() function match the input names from the form and database.
/**
* Search fields to retrieve and search the database with. Assumed they match the
* column names in the database
*/
private function getSearchFields()
{
return ['travel_company_id', 'transport_type', 'route_type'];
}
public function search()
{
// Get a new query instance from the model
$query = TravelRoute::query();
// Loop through the fields checking if they've been input, if they have add
// them to the query.
foreach($this->getSearchFields() as $field)
{
if (Input::has($field))
{
$query->where($field, Input::get($field));
}
}
// Finally execute the query
$travelRoutes = $query->get();
}

Resources