Advanced use of linked tables
- In this section, we show some advanced use cases on how to get data from a chain of linked tables
- In our site we only have three tables (orderlines, orders and users) linked to each other. However, the logic we describe here will be the same if you have more linked tables (four, five, ten, ...), as long as you correctly define the relations (
belongsTo()
andhasMany()
) inside the Eloquent models.
Example setup
- We want to define 2 queries
- The first query starts from the most left table (orderlines) and should retrieve all the information from the orders table (the order to which this orderline belongs) and from the users table (the user to which this order belongs)
- The second query starts from the most right table (users) and should retrieve all the information from the orders table (the orders placed by this user) and from the orderlines table (the orderlines of these orders)
- First create some extra routes in routes/web.php
Route::middleware(['auth', 'admin'])->prefix('admin')->group(function () {
...
Route::get('orders', 'Admin\OrderController@index');
// yes, you can nest prefixes and groups if you want :-)
Route::prefix('demo')->group(function (){
Route::get('orderlines', 'Admin\OrderController@orderlines');
Route::get('users', 'Admin\OrderController@users');
});
});
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
- Add two methods to Admin/OrderController.php
// Orderlines with order and user
public function orderlines()
{
$orderlines = Orderline::get();
return $orderlines;
}
// User with orders and orderlines
public function users()
{
$users = User::get();
return $users;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
- Open the pages in the browser. At this point we only see the starting tables.
Retrieve all the information from the chain
- To get all the information from a chain of linked tables you only have to concatenate the name of the relations wit a dot
- From the
Orderline
model concatenate all the names of the functions that containbelongsTo()
with a dotorder
describes the relation from theOrderline
model to theOrder
modeluser
describes the relation from theOrder
model to theUser
model
- From the
User
model: concatenate all the names of the functions that containhasMany()
with a dotorders
describes the relation from theUser
model to theOrder
modelorderlines
describes the relation from theOrder
model to theOrderline
model
- From the
// Orderlines with order and user
public function orderlines()
{
$orderlines = Orderline::with('order.user')
->get();
return $orderlines;
}
// User with orders and orderlines
public function users()
{
$users = User::with('orders.orderlines')
->get();
return $users;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Filter fields on parent/child tables
- To exclude (filter) some fields from the linked tables, you have to split the relations into an array
(just like we did in our example in Admin: order history) - First, replace replace the single notation inside
with()
with an array which contains all the related tables- Orderlines :
with('order.user')
becomeswith(['order', 'order.user'])
- Users :
with('orders.orderlines')
becomeswith(['orders','orders.orderlines'])
- Orderlines :
// Orderlines with order and user
public function orderlines()
{
$orderlines = Orderline::with(['order', 'order.user'])
->get();
return $orderlines;
}
// User with orders and orderlines
public function users()
{
$users = User::with(['orders','orders.orderlines'])
->get();
return $users;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- When you reload the pages, nothing has changed yet, but now we can apply some filters on each table
- As stated earlier, do not forget to include all the primary and relevant foreign keys!
// Orderlines with order and user
public function orderlines()
{
$orderlines = Orderline::with(['order:id,user_id,total_price', 'order.user:id,name,email'])
->get();
return $orderlines;
}
// User with orders and orderlines
public function users()
{
$users = User::with(['orders:id,user_id,total_price', 'orders.orderlines:id,order_id,artist,title'])
->get();
return $users;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Add additional query conditions
- Until now we can get linked tables and filter some of their fields, but we can't add additional queries on the linked tables.
- To do this, we have to take a totally differed approach.
- Suppose we want to sort the orderlines (the records a user ordered) by
artist
and not byid
(the default order)
- We rewrite our code using nested relationships inside a closure function:
with(['relation' => function ($query) {...}])
- Filtering also must be done inside this closure
// User with orders and orderlines
public function users()
{
$users = User::with(['orders' => function ($query) {
// select the required fields from the orders table
$query->select(['id', 'user_id', 'total_price'])
// go to the next table (orderlines)
->with(['orderlines' => function ($query) {
// select the required fields from the orderlines table and order by artist
$query->select(['id', 'order_id', 'artist', 'title'])
->orderBy('artist');
}]);
}])
->get();
return $users;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16