---
description: https://viblo.asia/p/eloquent-relationships-in-laravel-53-chap-2-aRBvXnwJeWE
---

# Eloquent Relationships in Laravel 5.3 (Chap 2)

## Eloquent Relationships in Laravel 5.3 (Chap 2)

[PHP](https://viblo.asia/tags/php)[Laravel](https://viblo.asia/tags/laravel)[eloquent](https://viblo.asia/tags/eloquent)[relationships](https://viblo.asia/tags/relationships)

#### Index <a href="#index-0" id="index-0"></a>

1. [Eloquent Relationships in Laravel 5.3 (Chap 1)](https://viblo.asia/chu.quynh.trang/posts/rQOvPNlyeYj)

#### Mở đầu <a href="#mo-dau-1" id="mo-dau-1"></a>

> Khi có nguyên liệu đầy đủ và một quyển công thức dạy nấu ăn thì chắc chắn bạn sẽ làm được món ăn ngon một cách dễ dàng. Eloquent Relationships trong Laravel cũng thế, khi các bảng đã được nối với nhau, và có hướng dẫn cách truy vấn đến từng bảng thì việc lấy dữ liệu trong database trở nên đơn giản hơn bao giờ hết. Ở bài trước mình đã cung cấp cho các bạn định nghĩa về `relationships`, thế nên hôm nay bằng những kinh nghiệm của bản thân (mặc dù chưa có kinh nghiệm nhiều =))), mình xin đưa ra công thức để `querying relations`.

#### Querying Relations <a href="#querying-relations-2" id="querying-relations-2"></a>

Khi tất cả các kiểu của quan hệ Eloquent được định nghĩa qua các hàm, bạn có thể gọi những hàm này để lấy những instance của quan hệ mà không cần thực thi các truy vấn quan hệ. Tất cả các kiểu quan hệ của Eloquent cũng có tại `query builders`thế nên bạn hoàn toàn có thể gán thêm các ràng buộc trên các truy vấn.

Tiếp tục với ví dụ quan hệ giữa 1 Area - n Stations:

```
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Area extends Model
{
    /**
     * Get all of the stations for the area.
     */
    public function stations()
    {
        return $this->hasMany('App\Station');
    }
}
```

Bạn có thể truy vấn quan hệ `stations` và thêm các quan hệ như sau:

```
$area = App\Area::find(1);

$area->stations()->where('departure_time', '<', 10)->get();
```

Đoạn code trên dùng để lấy tất cả các nhà ga có giờ khởi hành trước 10h của vùng có id là 1.

`Note`: Hãy nhớ rằng bạn có thể sử dụng bất kì phương thức `query builders` trên các mối quan hệ để việc truy vấn trở nên đơn giản hơn.

#### Relationship Methods Vs. Dynamic Properties <a href="#relationship-methods-vs-dynamic-properties-3" id="relationship-methods-vs-dynamic-properties-3"></a>

Ở bài trước mình có nhắc rất nhiều đến `Eloquent dynamic properties` như là `$area->stations`, nhưng nếu bạn tìm trong model `Area` bạn sẽ chẳng thể tìm thấy một properties nào tên là `stations` (haha).

Nghe có vẻ thần kì, kì thực là Laravel sử dụng các Magic Method của PHP để làm cho việc gọi `stations` trở nên hợp lệ và nó sẽ trả về kết quả của quan hệ `stations` (tương đương với `$area->stations->get()`). `Eloquent dynamic properties` chính là `lazy loading` nghĩa là chúng chỉ load dữ liệu quan hệ của chúng khi bạn thực sự gọi.

Bởi vì điều này, lập trình viên thường sử dụng `Eager loading` để pre-load các quan hệ mà họ biết sẽ được xử lý sau khi load model. Eager loading cung cấp một sự giảm bớt đáng kể trong các truy vấn SQL mà sẽ phải thực thi khi load các quan hệ của model.

#### Querying Relationship Existence <a href="#querying-relationship-existence-4" id="querying-relationship-existence-4"></a>

Khi truy cập các bản ghi một model, bạn có thể muốn giới hạn kết quả trả về dựa trên sự tồn tại của một quan hệ. Ví dụ, bạn muốn lấy tất cả các `area` mà có ít nhất một `house`. Để làm việc này, bạn phải truyền tên của quan hệ vào phương thức `has`:

```
// Retrieve all areas that have at least one house...
$areas = App\Area::has('houses')->get();
```

Hoặc là bạn cũng có thể chỉ định thêm toán tử và số lượng vào truy vấn:

```
// Retrieve all areas that have one-hundered or more houses...
$posts = Post::has('houses', '>=', 100)->get();
```

Bạn cũng có thể lấy ra tất cả các `areas` có tối thiểu 1 `houses` và 1 `stations`:

```
// Retrieve all houses that have at least one house and station...
$posts = Post::has('houses.stations')->get();
```

Nếu bạn vấn muốn có những truy vấn mạnh hơn, hãy sử dụng phương thức `whereHas` và `orWhereHas` để đặt các điều kiện `where` bên trong truy vấn `has`. Những phương thức này cho phép bạn thêm những ràng buộc tùy chọn, như là kiểm tra nội dung của 1 `house`:

```
// Retrieve all areas with at least one houses containing house_number like %128A%
$areas = ::whereHas('houses', function ($query) {
    $query->where('house_number', 'like', '%128A%');
})->get();
```

#### Querying Relationship Absence <a href="#querying-relationship-absence-5" id="querying-relationship-absence-5"></a>

Ngược lại với `has` là `doesntHave` có nghĩa là bạn sẽ lấy những kết quả trả về dựa trên sự không tồn tại của 1 quan hệ. Ví dụ như bạn muốn liệt kê những `areas` không có `station` nào thì bạn sẽ thực hiện đơn giản như sau:

```
// Retrieve all houses that doesnot have station...
$areas = App\Area::doesntHave('stations')->get();
```

Tương tự nếu bạn muốn truy vấn sâu hơn thì sử dụng phương thức `whereDoesntHave`:

```
// Retrieve all areas doesnot have house containing house_number like %128A%
$areas = ::whereDoesntHave('houses', function ($query) {
    $query->where('house_number', 'like', '%128A%');
})->get();
```

#### Counting Related Models <a href="#counting-related-models-6" id="counting-related-models-6"></a>

Nếu bạn muốn đếm số kết quả từ 1 quan hệ mà không muốn load chúng bạn có thể sử dụng phương thức `withCount`, nó sẽ đặt một cột {relation}\_count vào model kết quả. Ví dụ:

```
$areas = App\Area::withCount('houses')->get();

foreach ($areas as $area) {
    echo $area->houses_count;
}
```

Bạn có thể lấy `counts` cho nhiều quan hệ cũng giống như thêm ràng buộc vào truy vấn:

```
$areas = Ap\Area::withCount(['houses', 'stations' => function ($query) {
    $query->where('name', 'like', '%A%');
}])->get();

//Retrieve the first area
echo $areas[0]->houses_count;
echo $areas[0]->stations_count;
```

#### Eager Loading <a href="#eager-loading-7" id="eager-loading-7"></a>

Khi bạn truy cập `Eloquent relationships` như là các thuộc tính, các dữ liệu này là `lazy loaded`. Điều này có nghĩa là dữ liệu không thực sự load cho đến khi bạn truy cập lần đầu tiên tới thuộc tính. Tuy nhiên, Eloquent có thể `eager load` các quan hệ tại thời điểm bạn truy vấn vào model cha. `Eager loading` làm giảm bớt vấn đề truy vấn N + 1. Để giải thích vấn đề truy vấn N + 1, ta xét model `House` có quan hệ với model `Area`:

```
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class House extends Model
{
    /**
     * Get the area that contain the house.
     */
    public function area()
    {
        return $this->belongsTo('App\Area');
    }
}
```

Và bạn cần lấy toàn bộ `houses` và các `area` của chúng:

```
$houses = App\House::all();

foreach ($houses as $house) {
    echo $house->area->name;
}
```

Vòng lặp sẽ thực hiện 1 truy vấn để lấy tất cả các `houses` trong bảng, sau đó là mỗi truy vấn cho 1 `house` để lấy `area`. Vì vậy nếu chúng ta có 100 `houses`, vòng lặp sẽ thực hiện 101 truy vấn: 1 để lấy `house`, 100 để lấy `area` của mỗi house.

Như các bạn đã biết việc thực hiện nhiều truy vấn làm cho hiệu suất của trang web giảm đáng kể. Nhưng rất may, chúng ta có thể sử dụng `eager loading` để giảm thiểu tính toán chỉ với 2 truy vấn. Khi thực hiện truy vấn, bạn chỉ định quan hệ này sẽ được `eager load` bằng cách dùng phương thức `with`:

```
$houses = App\House::with('area')->get();

foreach ($houses as $house) {
    echo $house->area->name;
}
```

Với tính toán này chỉ có 2 truy vấn sẽ được thực thi đó là:

```
select * from houses

select * from areas where id in (1, 2, 3, 4, 5, ...)
```

#### Eager Loading Multiple Relationships <a href="#eager-loading-multiple-relationships-8" id="eager-loading-multiple-relationships-8"></a>

Có những lúc bạn sẽ cần `eager load` vài mối quan hệ khác nhau trong 1 tính toán. Để làm việc này, chỉ cần truyền thêm các tham số vào phương thức `with`:

```
$houses = App\House::with('area', 'user')->get();
```

#### Nested Eager Loading <a href="#nested-eager-loading-9" id="nested-eager-loading-9"></a>

Để lồng các `eager load`, bạn có thể sử dụng dấu `"."`. Ví dụ `eager load` tất cả `area` và tất cả `stations` trong một cú pháp Eloquent:

```
$houses = App\House::with('area.stations')->get();
```

#### Constraining Eager Loads <a href="#constraining-eager-loads-10" id="constraining-eager-loads-10"></a>

Đôi khi bạn có thể muốn `eager load` một quan hệ, nhưng cũng muốn chỉ định những ràng buộc thêm vào trong truy vấn của `eager load` như sau:

```
//Retrieve all houses have area contain the word 'Ha'
 $houses = App\House::with(['areas' => function ($query) {
    $query->where('name', 'like', '%Ha%');

}])->get();
```

#### Lazy Eager Loading <a href="#lazy-eager-loading-11" id="lazy-eager-loading-11"></a>

Có lúc bạn muốn `eager load` một quan hệ sau khi model cha đã được lấy ra. Điều này có thể hữu ích nếu bạn muốn quyết định sẽ load model liên quan nào:

```
$houses = App\House::all();

if ($someCondition) {
    $houses->load('area', 'user');
}
```

Nếu bạn muốn thêm các ràng buộc vào truy vấn của `eager load`, bạn có thể truyền một `Closure` vào phương thức `load`:

```
$houses->load(['area' => function ($query) {
    $area->orderBy('name', 'asc');
}]);
```

Bài này mình đã giới thiệu cho các bạn cách `Querying Relations`, bài sau mình sẽ tiếp tục nói về việc `Inserting & Updating Related Models`. Cảm ơn các bạn đã đón đọc! (lay)
