Laravel Eloquent: Reduce number of queries - php

Let's say I have 250 users in users table and each user has one or many books, and each book has one or many chapters. Now I would like to print the user names, with their book names.
Controller:
$users = User::all();
in blade:
#foreach($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>
#foreach($user->books as $book)
{{ $book->name }},
#endforeach
</td>
</tr>
#endforeach
# of queries 252
Now to overcome the n+1 problem, the query should be
$users = User::with('books')->get();
Now the # of queries are only 2.
I want to print the book names with number of chapters like this->
BookName(# of chapters). So in my blade
#foreach($users as $user)
<tr>
<td>{{ $user->id }}</td>
<td>{{ $user->name }}</td>
<td>
#foreach($user->books as $book)
{{ $book->name }} ({{ $book->chapters->count() }}),
#endforeach
</td>
</tr>
#endforeach
so for 750 books with 1500 chapters the # of queries are about 752 and it increases if chapter number increases.
Is there any better Eloquent way to reduce it or should I go for raw SQL queries?

You don't need to load all chapters data and then manually count each collection. Use withCount() instead:
$users = User::with('books')->withCount('chapters')->get();
If you want to count the number of results from a relationship without actually loading them you may use the withCount method, which will place a {relation}_count column on your resulting models.

From the Eloquent Documentation:
Nested Eager Loading
To eager load nested relationships, you may use "dot" syntax. For example, let's eager load all of the book's authors and all of the author's personal contacts in one Eloquent statement:
$books = App\Book::with('author.contacts')->get();
In your case, you can retrieve the nested relationships you need with the following:
User::with('books.chapters')->get();

Related

I want to display the days and date also by the relationship between the coach and working times

Here a want to add Appointments available for the coach by using two tables...one for date_days the second for trainer and I make a 1-m relation between two tables.
Dateday Table...
class Dateday extends Model
{
public function trainer()
{
return $this->belongsTo(Trainer::class, 'trainer_id', 'id');
}
}
Trainer Table...
class Trainer extends Model
{
public function datedays()
{
return $this->hasMany(Dateday::class);
}
}
Trainer index ...
Show this way you give me an error, And I want To add a day and date and start_date/end_date to the index page for trainer Who can do this??
#foreach ($trainers as $trainer)
<tr>
<td>{{ $trainer->id }}</td>
<td>{{ $trainer->firstname }}</a></td>
<td>{{ $trainer->lastname }}</a></td>
<td>{{ $trainer->phone }}</a></td>
<-- Show this way you give me ero -->
<td>{{ $trainer->dateday->day}}</a></td>
<td>{{ $trainer->dateday->date}}</a></td>
</tr>
#endforeach
In your Trainer class, you have defined a function datedays, but in your template you are using $trainer->dateday without the s.
Regarding your edit
$trainer->datedays is a Collection of all the datedays associated with the trainer (because of the ->hasMany), so you cannot get the property of an individual item. If there should only be one date, the relation should be changed (->hasOne), or if there should be more dates possible, you should think about how you want to show that. If you (for example) only want to show the first date you could use {{ $trainer->datedays->first()->day }} or you could loop over all dates using #foreach ($trainer->datedays as $dateday) and use {{ $dateday->day }} in the loop.

Can't view collection of data in laravel

i returned a collection of two tables of data with join query as a collection and group by user id. i want to view it on a html table.
My data array looks like this. How can i show this all data into html table view using laravel blade.
if you are sending the collection to your blade template. You can use a foreach loop to iterate through array:
#foreach($items as $item)
<tr>
<td>{{ $item->fname}}</td>
<td>{{ $item->lname }}</td>
</tr>
#endforeach

Undefined property in Laravel 5

I've looked at other questions that have answered this same error and I understand I have a property that cannot be defined but not sure how to make it defined. First time using Many-to-Many relationship so I'm assuming this is the issue.
Project Table
id
company
stage
status
date_started
date_finished
timestamps
Employee Table
id
name
department
timestamps
Employee_Project Table
id
employee_id
project_id
In both models, I have belongsToMany and for the show function in the ProjectController, I have:
$projects = Project::find($id);
In the view, I want to show company name ($projects->company) and the employees on that project with their respective departments. These do not work:
$projects->employees->name
$projects->employees->department
How do I access these properties?
--Update--
Jeff's answer works but I set up a table like
<thead><th>PM</th><th>AM</th></thead>
<tbody><td>#if($employee->department == 'Project Manager'){{ $employee->name }}</td>
<td>#else($employee->department == 'Account Manager'){{ $employee->name }}</td></tbody>#endif
and this does not work to show the correct employees in their respective sections. Any ideas how to fix this?
Your problem is that $project->employees is a collection, not an individual instance of an employee. You will need to iterate over the collection in order to access each individual employee's name:
foreach($project->employees as $employee) {
echo $employee->name;
echo $employee->department;
}
Update
It looks like you may need to restructure as below, I think you probably want the entire if construct to be within the same table cell, though I could be wrong:
<tbody>
#foreach($project->employees as $employee)
<tr>
<td>
#if($employee->department == 'Project Manager')
{{ $employee->name }}
#elseif($employee->department == 'Account Manager')
{{ $employee->department }}
#else
<!-- What about non project/account managers? -->
#endif
</td>
</tr>
#endforeach
</tbody>
I could be totally wrong, and maybe you want account managers and project managers in different columns:
<tr>
<td>
#if($employee->department == 'Project Manager')
{{ $employee->name }}
#endif
</td>
<td>
#if($employee->department == 'Account Manager')
{{ $employee->name }}
#endif
</td>
</tr>
Be sure to take a look at the documentation on Blade if statements.
Update 2
If you want to order how the related entities are coming back, you can do something like this (assuming you want the employees ordered by their department:
$project->employees()->orderBy('department', 'DESC')->get();
Notice you're no longer accessing the property, you're calling the relation method employees() so that you can modify the query prior to it being executed.
Here's a link to the documentation on ordering queries, and there is also another StackOverflow question on it.
So I figured out how to show the correct person to show up under their respective departments in the table and I wanted to share.
Thanks to #jeff-lambert I added the foreach loop but I had added to the entire row like in his first update. This did not work because the employees were not showing up in their respective departments. For example, account managers were showing up under the project manager . To resolve this, I had to add a foreach loop to each .
<td>
#foreach($projects->employees as $employee)
#if($employee->department == 'Project Manager')
{{ $employee->name }}
#endif
#endforeach
</td>
Hope this helps explain it.

Laravel sorting eager loaded data

I'm using the following query to get all companies for a certain event. And they are ordered by name
$companies = Company::where('city_id', 3)->orderBy('name', 'asc')->get();
Now in my view i use eager loading to get all the persons that work for that company
#foreach($companies as $company)
#foreach($company->persons as $person)
<tr><td>{{ $person->firstname }}</td></tr>
#endforeach
#endforeach
I'm trying to sort the persons by name but it's not working. Any idea on how to do this?
Thanks
You can further query your relations in Laravel.
For example to get person by names in an ascending order.
#foreach($companies as $company)
#foreach($company->persons()->orderBy('firstname','asc')->get() as $person)
<tr><td>{{ $person->firstname }}</td></tr>
#endforeach
#endforeach

Getting pivot and reltated tables column values Laravel 3

Good evening peeps, I'm playing a little bit with Laravel, and I have this situation. I have 2 models and 3 tables to display vital signs from a pacient.
Table pacient:
id
names
birthdate
Table vital_sign:
id
description
unit
table pacient_vital_sign
id
pacient_id
vital_sign_id
first_value
second_value
date_time_taken
And the 2 models:
class Pacient extends Eloquent{
public static $timestamps = false;
public function vital_signs(){
return $this->has_many_and_belongs_to('VitalSign', 'pacient_vital_sign', 'pacient_id', 'vital_sign_id');
}
}
class VitalSign extends Eloquent{
public static $timestamps = false;
public static $table = 'vital_signs';
public function pacients(){
return $this->has_many_and_belongs_to('Pacient', 'pacient_vital_sign', 'vital_sign_id', 'pacient_id');
}
}
And for my view I'm sending the pacient whose vital signs I want to display:
$pacient = Pacient::where('active', '=', 1)->first();
So, based on the documentation I could do this to show the pivotal table data:
#foreach($pacient->vital_signs as $sv)
<tr>
<td>{{ $sv->pivot->date_time_taken }}</td>
<td>{{ $sv->description }}</td>
<td>{{ $sv->pivot->first_value }} {{ $sv->pivot->second_value }}</td>
</tr>
#endforeach
But this way, neither the date_time_taken value nor first_value or second_value is showed. Description is displayed.
Now, if I do something like this
#foreach($pacient->vital_signs()->pivot()->get() as $sv)
<tr>
<td>{{ $sv->date_time_taken }}</td>
<td>{{ $sv->description }}</td>
<td>{{ $sv->first_value }} {{ $sv->second_value }}</td>
</tr>
#endforeach
date_time_taken, first_value and secon_value are displayed but I dont know how to reverse the relationship in order to display the value for the related table column, in this case, description from vital_sign table
So I both cases, I'm missing something. Any ideas? What I'm missings?
Thanks
On the Docs:
By default only certain fields from the pivot table will be returned
(the two id fields, and the timestamps). If your pivot table contains
additional columns, you can fetch them too by using the with() method
So in my case, on both models:
return $this->has_many_and_belongs_to('VitalSign', 'pacient_vital_sign', 'pacient_id', 'vital_sign_id')->with('col1', 'col2', 'coln');
and this will work:
#foreach($pacient->vital_signs as $sv)
<tr>
<td>{{ $sv->pivot->date_time_taken }}</td>
<td>{{ $sv->description }}</td>
<td>{{ $sv->pivot->first_value }} {{ $sv->pivot->second_value }}</td>
</tr>
#endforeach
I found this to be useful, but a quick addendum for Laravel 4:
return $this->belongsToMany('Model','pivot_table','this_id','foreign_id')->withPivot('col1','col2');

Categories