Yes it's possible, and it's a good idea if view need them ;)
Laravel 5: How do I pass all related items to the view?
I have the default User model along with models such as Article, Task, and Module etc. What I would like to do, is within my ProfilesController is to send all the related models through to the view?
- Is this possible?
- Is this a good idea?
@bestmomo you sure you're not British with that wit? :P
I've set up the relationships in the models. Does this make the related models available automatically when I pass the User to the view?
@mstnorris @bestmomo Technically yes, but they'll be loaded lazily, which means you could end up with a TON of queries on that page. To prevent that, you want to use Eager Loading, which will only use 1 extra query for relationship.
For a query:
$user = User::with('article', 'task', 'module')->where('id', $userId)->first();
For an existing instance:
$user->load('article', 'task', 'module');
Now you'll be able to access those relationships without worrying about extra queries.
@thepsion5 is faster than me, I must have a British side yes ^^
@mstnorris when you pass the User to the view, you'll be able to access the relationships via User->relationship.
It's tidier imo to pass relationship as a separate variable within your controller. It also means that anybody looking or working with the view knows what is there, rather than having to dive into the model to identify any relationships.
Using eager loading will cut down on surplus queries, too (running a single WHERE user_id IN for all Users rather than one WHERE user_id = for each User).
@deringer do you mean what @thepsion5 said with regards to something like?
Auth::user()->with('article', 'module', 'task');
I have just installed Clockwork and I'm logging what queries are being called.
There isn't a difference in the number of queries being executed when I change:
Auth::user();
to
Auth::user()->with('article', 'module', 'note', 'reminder', 'task');
Where would it make a difference?
@mstnorris maybe add a ->get() at the end.
Yes @mstnorris! And as @bestmomo stated, the query won't actually run without the ->get() call as with() returns an instance of the query builder, not a collection :)
@bestmomo thank you, but I am now getting an error saying.
Undefined property: Illuminate\Database\Eloquent\Collection::$modules
@mstnorris check your syntax, not module ?
@bestmomo I have the following models Article, Task, and Module.
In my navigation.blade.php file
@if ( Auth::check() )
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-cube"></i> <span class="hidden-xs">My Modules <span class="caret"></span></span></a>
<ul class="dropdown-menu" role="menu">
@foreach ( $user->modules as $module )
<li><a href="#">{{ $module->module_code }} - {{ str_limit($module->name, 30) }}</a></li>
@endforeach
<li class="divider"></li>
<li><a href="#">View Last Years Modules</a></li>
</ul>
</li>
@endif
User.php
public function articles()
{
return $this->hasMany('App\Article');
}
public function modules()
{
return $this->belongsToMany('App\Module');
}
public function tasks()
{
return $this->hasMany('App\Task');
}
Do you need any other information?
And you use that ?
Auth::user()->with('articles', 'modules', 'tasks')->get();
@bestmomo yes.
@mstnorris Try :
Auth::user()->with('articles', 'modules', 'tasks')->first();
@bestmomo that works... However, and this is a big however. In my ProfilesController I have the following method:
public function dashboard()
{
if ( Auth::check() )
{
$user = Auth::user()->with('articles', 'modules', 'notes', 'reminders', 'tasks')->first();
return view('users.dashboard', compact('user'));
}
abbort(403);
}
This executes 9 sql queries
But when I remove the ->with(...); part, it only executes 4 queries.
public function dashboard()
{
if ( Auth::check() )
{
$user = Auth::user();
return view('users.dashboard', compact('user'));
}
abbort(403);
}
Why is that?
@mstnorris Eloquent needs queries for eager loading.
@bestmomo, I thought that eager loading would reduce the number of queries.
Also, how to I reference the models in my navigation?
Do I still need to use
@foreach ( Auth::user()->modules as $module )
...
because if I just use
@foreach ( $user->modules as $module )
...
I get Undefined variable: user in my navigation.blade.php file.
Eager loading will reduce the number of queries in loops in your view.
But you send $user in your view and you dont get it ???
@bestmomo no I don't.
I have a layouts directory which houses a master.blade.php file which includes my partials/navigation.blade.php file. As such I have another directory partials which is on the same document level as the layouts directory which has the navigation.blade.php file within.
Could it be that my dashboard.blade.php that extends layouts.master and doesn't have access to the User model?
could you dd($user);
@xroot and @bestmomo here is my ProfilesController
public function dashboard()
{
if ( Auth::check() )
{
$user = Auth::user()->with('articles', 'modules', 'notes', 'reminders', 'tasks')->first();
dd($user);
return view('users.dashboard', compact('user'));
}
abbort(403);
}
Auth::user()-> ... -> first(); always returns the first user in the database, not the actual authenticated user.
Also, the format for the output from dd($user); is:
User {#294 ▼
#table: "users"
#fillable: array:3 [▼
0 => "name"
1 => "email"
2 => "password"
]
#hidden: array:2 [▼
0 => "password"
1 => "remember_token"
]
#connection: null
#primaryKey: "id"
#perPage: 15
+incrementing: true
+timestamps: true
#attributes: array:7 [▼
"id" => 1
"name" => "Faker User"
"email" => "fake@example.com"
"password" => "$2y$10$fk0Ricz3lc7FBQOGW8CWEu7.Tl7QJW6Jfd5aOiaaWrDBiA/vjXIha"
"remember_token" => "SjrYKNs18XoidM4NwfVmRr1KROWnG79o38mZl5W7qwdbvXBkRCtqfBXpPs0E"
"created_at" => "2015-02-10 21:08:28"
"updated_at" => "2015-02-11 11:09:51"
]
#original: array:7 [▼
"id" => 1
"name" => "Faker User"
"email" => "fake@example.com"
"password" => "$2y$10$fk0Ricz3lc7FBQOGW8CWEu7.Tl7QJW6Jfd5aOiaaWrDBiA/vjXIha"
"remember_token" => "SjrYKNs18XoidM4NwfVmRr1KROWnG79o38mZl5W7qwdbvXBkRCtqfBXpPs0E"
"created_at" => "2015-02-10 21:08:28"
"updated_at" => "2015-02-11 11:09:51"
]
#relations: array:5 [▼
"articles" => Collection {#304 ▶}
"modules" => Collection {#318 ▶}
"notes" => Collection {#322 ▶}
"reminders" => Collection {#319 ▶}
"tasks" => Collection {#339 ▶}
]
#visible: []
#appends: []
#guarded: array:1 [▼
0 => "*"
]
#dates: []
#casts: []
#touches: []
#observables: []
#with: []
#morphClass: null
+exists: true
}
This is the actual answer:
ProfilesController
public function dashboard()
{
if ( Auth::check() )
{
$user = Auth::user();
$user->load('articles', 'modules', 'notes', 'reminders', 'tasks')->get();
return view('users.dashboard', compact('user'));
}
abbort(403);
}
And then for example in my navigation.blade.php I can use the following (a snippet):
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><i class="fa fa-inbox"></i> <span class="hidden-xs">Reminders</span> <span class="label label-danger">{{ $user->reminders()->count() }}</span></a>
<ul class="dropdown-menu" role="menu">
@foreach ( $user->reminders as $reminder )
<li><a href="#">{{ $reminder->text }}</a></li>
@endforeach
<li class="divider"></li>
<li><a href="#"><i class="fa fa-sign-out"></i> Clear All</a></li>
</ul>
</li>
This will work :
$user = \App\User::with('articles', 'modules', 'notes', 'reminders', 'tasks')->find(Auth::id());
@bestmomo I still don't understand that when I remove the with( ... ) part I execute fewer queries. I thought by eager loading them, that would run fewer.
@mstnorris You have fewer queries in controller without the with but you'll have many ones in your loops in views.
@bestmomo how should my navigation view look like with my drop down lists?
How do I reference the Tasks, Articles etc?
You should make a new post for each issue with clear context.
This is a nonsense :
$user = Auth::user()->with('articles', 'modules', 'notes', 'reminders', 'tasks')->first();
Auth::user() returns the logged in user, not a query builder. Good way of doing this would be either :
$user = Auth::user();
$user->load('articles', 'modules', 'notes', 'reminders', 'tasks');
Or use the $with attribute of the user model :
class User extends Model{
protected $with = ['articles', 'modules', 'notes', 'reminders', 'tasks'];
}
But remember with the second method all these relationship will be loaded every time a user is retrieved.
About eager loading, it will issue one query per relationship. If you have more queries than expected you must have made a mistake somewhere.
Looks like @mstnorris didn't close their * and the markdown parser has made everything after it italics... @JeffreyWay lol
Please or to participate in this conversation.