# hasOneThrough, hasManyThrough sử dụng truy vấn các mqh giữa các bảng thông qua một bảng trung gian

phương thức **`hasOneThrough`** và **`hasManyThrough`** được sử dụng để truy vấn các mối quan hệ giữa các bảng thông qua một bảng trung gian.

Trong Laravel, các phương thức **`hasOneThrough`** và **`hasManyThrough`** được sử dụng để truy vấn các mối quan hệ giữa các bảng thông qua một bảng trung gian. Tuy nhiên, việc sử dụng các phương thức này sẽ phụ thuộc vào cấu trúc mối quan hệ của các bảng và loại dữ liệu bạn cần truy vấn.

Trong bài toán bạn mô tả, chúng ta đang làm việc với ba bảng:

1. **`categories`** - Danh mục sản phẩm.
2. **`products`** - Sản phẩm thuộc về một danh mục.
3. **`orders`** - Đơn hàng chứa các sản phẩm.

Trong mối quan hệ này:

* **Mối quan hệ giữa `categories` và `products`** là một-một (1-n) - Một danh mục có thể có nhiều sản phẩm.
* **Mối quan hệ giữa `products` và `orders`** là nhiều-nhiều (n-n) - Một sản phẩm có thể thuộc nhiều đơn hàng, và một đơn hàng có thể chứa nhiều sản phẩm.

Dưới đây là cách bạn có thể sử dụng các phương thức **`hasOneThrough`** hoặc **`hasManyThrough`** để truy vấn dữ liệu:

#### 1. **Sử dụng `hasManyThrough`**

Phương thức **`hasManyThrough`** sẽ hữu ích trong trường hợp bạn muốn truy vấn tất cả các đơn hàng liên quan đến một danh mục sản phẩm cụ thể thông qua bảng `products`.

**Mô tả mối quan hệ:**

* Một **`category`** có thể có nhiều **`products`**.
* Mỗi **`product`** có thể được bán trong nhiều **`orders`**.

Phương thức **`hasManyThrough`** cho phép bạn lấy tất cả các đơn hàng (`orders`) của một danh mục (`category`).

**Ví dụ sử dụng `hasManyThrough`:**

Trong model **`Category`**, bạn có thể định nghĩa phương thức `orders` như sau:

```
class Category extends Model
{
    public function orders()
    {
        return $this->hasManyThrough(Order::class, Product::class);
    }
}
```

Trong phương thức này:

* **`hasManyThrough`** nhận hai tham số:
  * Tham số đầu tiên là model bạn muốn truy vấn, trong trường hợp này là `Order`.
  * Tham số thứ hai là model trung gian (ở đây là `Product`).

Khi bạn gọi `$category->orders`, Laravel sẽ truy vấn tất cả các đơn hàng mà sản phẩm thuộc danh mục này đã được bán.

**Truy vấn dữ liệu:**

```
$category = Category::find(1);
$orders = $category->orders;  // Lấy tất cả các đơn hàng của danh mục này
```

#### 2. **Sử dụng `hasOneThrough`**

Phương thức **`hasOneThrough`** sẽ hữu ích trong trường hợp bạn muốn truy vấn một bản ghi duy nhất từ bảng **`orders`** liên quan đến một danh mục thông qua sản phẩm. Tuy nhiên, mối quan hệ này chỉ hợp lý khi bạn muốn truy vấn một đơn hàng duy nhất.

**Mô tả mối quan hệ:**

* Một **`category`** có thể có nhiều **`products`**.
* Mỗi **`product`** có thể chỉ thuộc về một **`order`**.

**Ví dụ sử dụng `hasOneThrough`:**

Trong model **`Category`**, bạn có thể định nghĩa phương thức `order` như sau:

```
class Category extends Model
{
    public function order()
    {
        return $this->hasOneThrough(Order::class, Product::class);
    }
}
```

Trong phương thức này:

* **`hasOneThrough`** nhận hai tham số:
  * Tham số đầu tiên là model bạn muốn truy vấn, trong trường hợp này là `Order`.
  * Tham số thứ hai là model trung gian (ở đây là `Product`).

Khi bạn gọi `$category->order`, Laravel sẽ chỉ trả về đơn hàng đầu tiên liên quan đến danh mục này.

**Truy vấn dữ liệu:**

```
$category = Category::find(1);
$order = $category->order;  // Lấy đơn hàng đầu tiên của danh mục này
```

#### Bài toán thực tế:

Giả sử bạn muốn báo cáo số lượng đơn hàng từ các danh mục khác nhau. Nếu bạn muốn truy vấn tất cả các đơn hàng từ các danh mục, bạn sẽ sử dụng **`hasManyThrough`**. Đây là một mối quan hệ điển hình khi bạn muốn truy xuất nhiều bản ghi từ bảng `orders` thông qua bảng trung gian `products`.

Nếu bạn muốn lấy một đơn hàng đầu tiên liên quan đến danh mục, bạn sẽ sử dụng **`hasOneThrough`**.

#### Tóm tắt:

* **`hasManyThrough`** là phương thức phù hợp nhất trong trường hợp này nếu bạn muốn truy vấn tất cả các đơn hàng từ một danh mục.
* **`hasOneThrough`** là phương thức thích hợp khi bạn chỉ cần truy vấn một đơn hàng duy nhất từ một danh mục.

Hy vọng điều này giúp bạn hiểu rõ hơn cách sử dụng các phương thức quan hệ trong Laravel cho bài toán của bạn!

{% embed url="<https://chatgpt.com/c/69ce32d1-b5a8-839e-87bf-3cdcf35280f0>" %}

Dưới đây là cách bạn có thể sử dụng các phương thức **`hasOneThrough`** và **`hasManyThrough`** trong Laravel để giải quyết bài toán với ba bảng có mối quan hệ một-nhiều giữa các bảng, như bạn yêu cầu:

#### 1. Cấu trúc bảng và mối quan hệ

* **Bảng `categories`** lưu trữ các danh mục sản phẩm.
* **Bảng `products`** lưu trữ các sản phẩm, mỗi sản phẩm thuộc một danh mục (`categories`).
* **Bảng `orders`** lưu trữ các đơn hàng, mỗi đơn hàng có thể chứa nhiều sản phẩm (`products`).

#### Mối quan hệ:

* **`categories` → `products`**: Mối quan hệ một-nhiều (1-n) - Một danh mục có thể có nhiều sản phẩm.
* **`products` → `orders`**: Mối quan hệ nhiều-nhiều (n-n) - Một sản phẩm có thể thuộc nhiều đơn hàng và một đơn hàng có thể chứa nhiều sản phẩm.

#### 2. Sử dụng `hasManyThrough`

Phương thức **`hasManyThrough`** sẽ hữu ích khi bạn muốn truy vấn tất cả các đơn hàng (`orders`) của một danh mục (`category`) thông qua các sản phẩm (`products`).

**Ví dụ sử dụng `hasManyThrough`:**

Trong model **`Category`**, bạn sẽ định nghĩa phương thức `orders` sử dụng **`hasManyThrough`** như sau:

```
class Category extends Model
{
    public function orders()
    {
        return $this->hasManyThrough(Order::class, Product::class);
    }
}
```

Trong đó:

* `Order::class` là model bạn muốn truy vấn (đơn hàng).
* `Product::class` là model trung gian (sản phẩm).

#### Truy vấn dữ liệu:

Giả sử bạn muốn lấy tất cả các đơn hàng liên quan đến một danh mục, bạn có thể thực hiện như sau:

```
$category = Category::find(1); // Lấy danh mục với ID 1
$orders = $category->orders;  // Lấy tất cả các đơn hàng liên quan đến danh mục này
```

**Giải thích:**

* **`hasManyThrough`** kết nối ba bảng: `categories`, `products`, và `orders`. Laravel sẽ tự động tạo các join để lấy tất cả các đơn hàng mà sản phẩm thuộc về danh mục này.

#### 3. Sử dụng `hasOneThrough`

Phương thức **`hasOneThrough`** sẽ hữu ích khi bạn muốn truy vấn một bản ghi duy nhất từ bảng **`orders`** liên quan đến một danh mục thông qua sản phẩm. Đây là phương thức khi bạn chỉ cần lấy một đơn hàng đầu tiên cho mỗi danh mục.

**Ví dụ sử dụng `hasOneThrough`:**

Trong model **`Category`**, bạn sẽ định nghĩa phương thức `order` sử dụng **`hasOneThrough`** như sau:

```
class Category extends Model
{
    public function order()
    {
        return $this->hasOneThrough(Order::class, Product::class);
    }
}
```

Trong đó:

* `Order::class` là model bạn muốn truy vấn (đơn hàng).
* `Product::class` là model trung gian (sản phẩm).

#### Truy vấn dữ liệu:

Giả sử bạn muốn lấy đơn hàng đầu tiên liên quan đến một danh mục, bạn có thể thực hiện như sau:

```
$category = Category::find(1); // Lấy danh mục với ID 1
$order = $category->order;  // Lấy đơn hàng đầu tiên liên quan đến danh mục này
```

**Giải thích:**

* **`hasOneThrough`** kết nối ba bảng: `categories`, `products`, và `orders`, nhưng chỉ trả về một đơn hàng đầu tiên cho mỗi danh mục.

#### 4. Tóm tắt:

* **`hasManyThrough`**: Sử dụng khi bạn muốn lấy tất cả các đơn hàng từ một danh mục qua các sản phẩm. Đây là phương thức hữu ích nhất trong trường hợp này vì mỗi danh mục có thể có nhiều sản phẩm và mỗi sản phẩm có thể thuộc nhiều đơn hàng.
* **`hasOneThrough`**: Sử dụng khi bạn chỉ muốn lấy một đơn hàng duy nhất từ danh mục qua sản phẩm (ví dụ: chỉ lấy đơn hàng đầu tiên).

Hy vọng hướng dẫn này giúp bạn hiểu cách sử dụng **`hasManyThrough`** và **`hasOneThrough`** trong Laravel để giải quyết bài toán thực tế với ba bảng có mối quan hệ một-nhiều và nhiều-nhiều!

Thiết kế bảng: Bảng categories lưu trữ các danh mục sản phẩm. Bảng products lưu trữ các sản phẩm, mỗi sản phẩm thuộc một danh mục (categories). Bảng orders lưu trữ các đơn hàng, mỗi đơn hàng có thể chứa nhiều sản phẩm (products).

Để thiết kế ba bảng **`categories`**, **`products`**, và **`orders`** với mối quan hệ một-nhiều giữa **`categories`** và **`products`**, và mối quan hệ nhiều-nhiều giữa **`orders`** và **`products`**, bạn có thể thực hiện như sau:

#### 1. **Bảng `categories`**

Bảng này lưu trữ thông tin về các danh mục sản phẩm. Mỗi danh mục có thể có nhiều sản phẩm.

```
CREATE TABLE categories (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
```

**Dữ liệu ví dụ:**

| id | name       |
| -- | ---------- |
| 1  | Điện thoại |
| 2  | Máy tính   |
| 3  | Sách       |

#### 2. **Bảng `products`**

Bảng này lưu trữ thông tin về các sản phẩm. Mỗi sản phẩm thuộc một danh mục trong bảng `categories`, vì vậy chúng ta sẽ có một khóa ngoại **`category_id`** trong bảng `products` để liên kết với bảng `categories`.

```
CREATE TABLE products (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    price DECIMAL(10, 2),
    category_id INT,
    FOREIGN KEY (category_id) REFERENCES categories(id)
);
```

**Dữ liệu ví dụ:**

| id | name             | price | category\_id |
| -- | ---------------- | ----- | ------------ |
| 1  | iPhone 13        | 1000  | 1            |
| 2  | Galaxy S21       | 950   | 1            |
| 3  | Dell XPS 13      | 1200  | 2            |
| 4  | Tìm lại tuổi trẻ | 20    | 3            |

#### 3. **Bảng `orders`**

Bảng này lưu trữ thông tin về các đơn hàng. Mỗi đơn hàng có thể chứa nhiều sản phẩm, do đó, bạn cần một bảng trung gian (junction table) để quản lý mối quan hệ nhiều-nhiều giữa **`orders`** và **`products`**.

```
CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,
    order_date DATE,
    total_price DECIMAL(10, 2)
);
```

**Dữ liệu ví dụ:**

| id | order\_date | total\_price |
| -- | ----------- | ------------ |
| 1  | 2026-04-01  | 2150.00      |
| 2  | 2026-04-02  | 1200.00      |

#### 4. **Bảng `order_items`** (Junction Table)

Bảng này lưu trữ thông tin về các sản phẩm trong mỗi đơn hàng. Mỗi dòng trong bảng này đại diện cho một sản phẩm trong một đơn hàng, và bảng này sẽ có khóa ngoại **`order_id`** và **`product_id`**.

```
CREATE TABLE order_items (
    order_id INT,
    product_id INT,
    quantity INT,
    price DECIMAL(10, 2),
    PRIMARY KEY (order_id, product_id),
    FOREIGN KEY (order_id) REFERENCES orders(id),
    FOREIGN KEY (product_id) REFERENCES products(id)
);
```

**Dữ liệu ví dụ:**

| order\_id | product\_id | quantity | price |
| --------- | ----------- | -------- | ----- |
| 1         | 1           | 1        | 1000  |
| 1         | 2           | 1        | 950   |
| 2         | 3           | 1        | 1200  |

#### Mối quan hệ giữa các bảng:

* **`categories` → `products`**: Một danh mục có thể có nhiều sản phẩm. Đây là mối quan hệ **một-nhiều (1-n)**.
* **`products` → `orders`**: Một sản phẩm có thể xuất hiện trong nhiều đơn hàng và một đơn hàng có thể chứa nhiều sản phẩm. Đây là mối quan hệ **nhiều-nhiều (n-n)**, được quản lý thông qua bảng trung gian **`order_items`**.

#### 5. **Mối quan hệ giữa các bảng trong Laravel**

Trong Laravel, bạn có thể định nghĩa các mối quan hệ này trong các model tương ứng. Ví dụ:

**Model `Category`:**

```
class Category extends Model
{
    public function products()
    {
        return $this->hasMany(Product::class);
    }
}
```

**Model `Product`:**

```
class Product extends Model
{
    public function category()
    {
        return $this->belongsTo(Category::class);
    }

    public function orders()
    {
        return $this->belongsToMany(Order::class, 'order_items')
                    ->withPivot('quantity', 'price');
    }
}
```

**Model `Order`:**

```
class Order extends Model
{
    public function products()
    {
        return $this->belongsToMany(Product::class, 'order_items')
                    ->withPivot('quantity', 'price');
    }
}
```

**Model `OrderItem` (bảng trung gian):**

```
class OrderItem extends Model
{
    protected $table = 'order_items';

    public function order()
    {
        return $this->belongsTo(Order::class);
    }

    public function product()
    {
        return $this->belongsTo(Product::class);
    }
}
```

#### 6. **Truy vấn thực tế**

Ví dụ: Nếu bạn muốn lấy tất cả các đơn hàng của một danh mục (ví dụ, **`Điện thoại`**) và các sản phẩm trong những đơn hàng đó, bạn có thể thực hiện như sau:

```
$category = Category::find(1); // Tìm danh mục Điện thoại
$orders = $category->products()->with('orders')->get();
```

**Giải thích:**

* **`$category->products()->with('orders')->get();`**: Lấy tất cả sản phẩm thuộc danh mục này và thông tin về các đơn hàng chứa các sản phẩm đó.

***

Đây là cách thiết kế bảng và mối quan hệ giữa các bảng trong Laravel để giải quyết bài toán quản lý danh mục, sản phẩm và đơn hàng với mối quan hệ một-nhiều và nhiều-nhiều.

Không sử đụng được hasManyThrough phải không?

Đúng, trong trường hợp bạn mô tả với các bảng **`categories`**, **`products`**, và **`orders`**, bạn **không thể sử dụng `hasManyThrough`** vì mối quan hệ giữa **`orders`** và **`categories`** không phải là một mối quan hệ trực tiếp mà phải đi qua bảng trung gian **`products`**.

#### Mối quan hệ giữa các bảng:

* **`categories` → `products`**: Mối quan hệ **một-nhiều** (1-n) - Một danh mục có thể có nhiều sản phẩm.
* **`products` → `orders`**: Mối quan hệ **nhiều-nhiều** (n-n) - Một sản phẩm có thể thuộc nhiều đơn hàng, và một đơn hàng có thể chứa nhiều sản phẩm.

#### Tại sao không sử dụng `hasManyThrough`:

Phương thức **`hasManyThrough`** trong Laravel yêu cầu có một mối quan hệ **một-nhiều trực tiếp** từ bảng ban đầu đến bảng đích thông qua một bảng trung gian.

Trong trường hợp của bạn:

* **`categories`** và **`orders`** không có mối quan hệ một-nhiều trực tiếp, mà phải đi qua bảng **`products`** (vì mỗi đơn hàng chứa sản phẩm và mỗi sản phẩm thuộc một danh mục). Đây là một mối quan hệ nhiều-nhiều thông qua bảng **`order_items`** (bảng trung gian).

Do đó, **`hasManyThrough`** không thể được sử dụng trực tiếp trong trường hợp này.

#### Giải pháp phù hợp:

* **Sử dụng `hasMany` và `belongsToMany`**:\
  Thay vì sử dụng `hasManyThrough`, bạn nên sử dụng các phương thức **`hasMany`** (cho mối quan hệ một-nhiều) và **`belongsToMany`** (cho mối quan hệ nhiều-nhiều). Cụ thể:
  * **`Category`** có thể có nhiều **`Product`** thông qua **`hasMany`**.
  * **`Product`** có thể có nhiều **`Order`** thông qua bảng trung gian **`order_items`** với **`belongsToMany`**.

**Cách sử dụng đúng các phương thức:**

**1. Model `Category`**

```
class Category extends Model
{
    public function products()
    {
        return $this->hasMany(Product::class); // Một danh mục có nhiều sản phẩm
    }
}
```

**2. Model `Product`**

```
class Product extends Model
{
    public function category()
    {
        return $this->belongsTo(Category::class); // Mỗi sản phẩm thuộc về một danh mục
    }

    public function orders()
    {
        return $this->belongsToMany(Order::class, 'order_items') // Mối quan hệ nhiều-nhiều với bảng trung gian 'order_items'
                    ->withPivot('quantity', 'price');
    }
}
```

**3. Model `Order`**

```
class Order extends Model
{
    public function products()
    {
        return $this->belongsToMany(Product::class, 'order_items') // Mối quan hệ nhiều-nhiều với bảng trung gian 'order_items'
                    ->withPivot('quantity', 'price');
    }
}
```

**4. Model `OrderItem` (Bảng trung gian)**

```
class OrderItem extends Model
{
    protected $table = 'order_items';

    public function order()
    {
        return $this->belongsTo(Order::class); // Mỗi sản phẩm thuộc về một đơn hàng
    }

    public function product()
    {
        return $this->belongsTo(Product::class); // Mỗi đơn hàng có thể có nhiều sản phẩm
    }
}
```

#### Truy vấn thực tế:

Giả sử bạn muốn lấy tất cả các đơn hàng của một danh mục, bạn có thể làm như sau:

```
$category = Category::find(1); // Tìm danh mục với ID 1
$orders = $category->products()->with('orders')->get(); // Lấy tất cả các đơn hàng của danh mục này thông qua các sản phẩm
```

#### Tóm lại:

* **`hasManyThrough`** không thể được sử dụng trong trường hợp này vì không có mối quan hệ trực tiếp từ **`categories`** đến **`orders`**.
* Thay vào đó, bạn sử dụng **`hasMany`** (cho mối quan hệ một-nhiều từ `Category` đến `Product`) và **`belongsToMany`** (cho mối quan hệ nhiều-nhiều giữa `Product` và `Order` thông qua bảng trung gian `order_items`).

Một ví dụ cho sử dụng hasManyThrough

Dưới đây là một ví dụ cụ thể về cách sử dụng **`hasManyThrough`** trong Laravel, với một tình huống mối quan hệ rõ ràng giữa ba bảng.

#### Ví dụ về **`hasManyThrough`** trong Laravel

Giả sử bạn có ba bảng sau:

1. **Bảng `countries`** - Lưu trữ thông tin các quốc gia.
2. **Bảng `users`** - Lưu trữ thông tin người dùng, mỗi người dùng thuộc về một quốc gia.
3. **Bảng `posts`** - Lưu trữ các bài viết của người dùng. Mỗi người dùng có thể có nhiều bài viết.

Mối quan hệ:

* **`countries` → `users`**: Mối quan hệ **một-nhiều** (1-n) - Một quốc gia có thể có nhiều người dùng.
* **`users` → `posts`**: Mối quan hệ **một-nhiều** (1-n) - Một người dùng có thể có nhiều bài viết.

#### Cấu trúc bảng:

**1. Bảng `countries`**

```
CREATE TABLE countries (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
```

**2. Bảng `users`**

```
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    country_id INT,
    FOREIGN KEY (country_id) REFERENCES countries(id)
);
```

**3. Bảng `posts`**

```
CREATE TABLE posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    user_id INT,
    FOREIGN KEY (user_id) REFERENCES users(id)
);
```

#### Mối quan hệ trong Laravel:

* **`Country` → `User`**: Một quốc gia có thể có nhiều người dùng.
* **`User` → `Post`**: Một người dùng có thể có nhiều bài viết.

### Sử dụng **`hasManyThrough`**:

Bạn có thể sử dụng **`hasManyThrough`** khi bạn muốn truy vấn các bài viết (**`posts`**) từ một quốc gia (**`country`**), thông qua các người dùng (**`users`**). Phương thức **`hasManyThrough`** giúp bạn truy vấn dữ liệu từ bảng **`posts`** thông qua bảng trung gian **`users`** mà không cần phải viết thêm nhiều câu lệnh **`JOIN`** thủ công.

#### Cấu trúc các model:

**1. Model `Country`**

```
class Country extends Model
{
    public function posts()
    {
        // Sử dụng hasManyThrough để lấy tất cả các bài viết từ quốc gia này
        return $this->hasManyThrough(Post::class, User::class);
    }
}
```

**2. Model `User`**

```
class User extends Model
{
    public function country()
    {
        return $this->belongsTo(Country::class); // Một người dùng thuộc về một quốc gia
    }

    public function posts()
    {
        return $this->hasMany(Post::class); // Một người dùng có nhiều bài viết
    }
}
```

**3. Model `Post`**

```
class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class); // Mỗi bài viết thuộc về một người dùng
    }
}
```

#### Truy vấn sử dụng **`hasManyThrough`**:

Giả sử bạn muốn lấy tất cả các bài viết của người dùng từ một quốc gia cụ thể, ví dụ **`Vietnam`**.

```
$country = Country::where('name', 'Vietnam')->first(); // Tìm quốc gia Vietnam
$posts = $country->posts;  // Lấy tất cả bài viết của người dùng thuộc quốc gia Vietnam
```

#### Giải thích:

* **`$country->posts`**: Laravel sẽ tự động sử dụng **`hasManyThrough`** để truy vấn tất cả các bài viết từ **`posts`**, thông qua **`users`** (bảng trung gian), liên kết với quốc gia **Vietnam**.
* Đây là một cách dễ dàng để truy vấn bài viết từ một quốc gia mà không cần phải viết các câu lệnh **`JOIN`** thủ công.

#### Kết quả:

Giả sử quốc gia **Vietnam** có ba người dùng, mỗi người dùng có một số bài viết, thì **`$posts`** sẽ trả về tất cả các bài viết của người dùng từ **Vietnam**.

#### Tóm lại:

* **`hasManyThrough`** được sử dụng khi bạn muốn truy vấn các bản ghi từ bảng **đích** (ví dụ, **`posts`**) thông qua một bảng **trung gian** (ví dụ, **`users`**) mà có mối quan hệ một-nhiều giữa bảng nguồn và bảng trung gian.
* Trong ví dụ trên, **`hasManyThrough`** giúp bạn lấy tất cả các bài viết của người dùng thuộc một quốc gia cụ thể mà không cần phải sử dụng nhiều câu lệnh **`JOIN`**.

Hy vọng ví dụ này giúp bạn hiểu rõ cách sử dụng **`hasManyThrough`** trong Laravel!

Lấy một ví dụ cho sử dụng hasOneThrough

### Sử dụng **`hasOneThrough`** trong Laravel

Phương thức **`hasOneThrough`** trong Laravel được sử dụng khi bạn muốn truy vấn một bản ghi duy nhất từ bảng **đích** thông qua một bảng **trung gian** mà giữa bảng ban đầu và bảng đích có mối quan hệ **một-nhiều**.

#### Ví dụ về **`hasOneThrough`**:

Giả sử bạn có ba bảng sau:

1. **Bảng `countries`** - Lưu trữ thông tin về các quốc gia.
2. **Bảng `users`** - Lưu trữ thông tin người dùng, mỗi người dùng thuộc về một quốc gia.
3. **Bảng `posts`** - Lưu trữ các bài viết của người dùng, mỗi người dùng có thể có nhiều bài viết.

Mối quan hệ:

* **`countries` → `users`**: Mối quan hệ **một-nhiều** (1-n) - Một quốc gia có thể có nhiều người dùng.
* **`users` → `posts`**: Mối quan hệ **một-nhiều** (1-n) - Một người dùng có thể có nhiều bài viết.

#### Cấu trúc bảng:

**1. Bảng `countries`**

```
CREATE TABLE countries (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
```

**2. Bảng `users`**

```
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    country_id INT,
    FOREIGN KEY (country_id) REFERENCES countries(id)
);
```

**3. Bảng `posts`**

```
CREATE TABLE posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    user_id INT,
    FOREIGN KEY (user_id) REFERENCES users(id)
);
```

#### Mối quan hệ trong Laravel:

* **`countries` → `users`**: Một quốc gia có thể có nhiều người dùng.
* **`users` → `posts`**: Một người dùng có thể có nhiều bài viết.

#### Cách sử dụng **`hasOneThrough`**:

Giả sử bạn muốn lấy bài viết **đầu tiên** của một quốc gia (ví dụ, **Vietnam**) mà không cần phải viết các câu lệnh **`JOIN`** phức tạp.

#### Cấu trúc các model:

**1. Model `Country`:**

```
class Country extends Model
{
    public function firstPost()
    {
        // Sử dụng hasOneThrough để lấy bài viết đầu tiên của quốc gia này
        return $this->hasOneThrough(Post::class, User::class);
    }
}
```

**2. Model `User`:**

```
class User extends Model
{
    public function country()
    {
        return $this->belongsTo(Country::class); // Một người dùng thuộc về một quốc gia
    }

    public function posts()
    {
        return $this->hasMany(Post::class); // Một người dùng có nhiều bài viết
    }
}
```

**3. Model `Post`:**

```
class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class); // Mỗi bài viết thuộc về một người dùng
    }
}
```

#### Truy vấn sử dụng **`hasOneThrough`**:

Giả sử bạn muốn lấy bài viết **đầu tiên** của quốc gia **Vietnam** (bài viết đầu tiên của người dùng thuộc quốc gia này).

```
$country = Country::where('name', 'Vietnam')->first(); // Tìm quốc gia Vietnam
$post = $country->firstPost;  // Lấy bài viết đầu tiên của quốc gia Vietnam
```

#### Giải thích:

* **`$country->firstPost`**: Laravel sẽ tự động sử dụng **`hasOneThrough`** để truy vấn bài viết **đầu tiên** từ **`posts`** thông qua **`users`**, mà không cần phải viết nhiều câu lệnh **`JOIN`**.

#### Tại sao sử dụng **`hasOneThrough`**?

Phương thức **`hasOneThrough`** được sử dụng trong trường hợp này vì bạn muốn lấy một bản ghi duy nhất (bài viết đầu tiên) từ bảng **`posts`** thông qua bảng **`users`**, mà không cần phải truy vấn nhiều bảng và sử dụng nhiều **`JOIN`** thủ công.

#### Kết quả:

Giả sử quốc gia **Vietnam** có nhiều người dùng và mỗi người dùng có một số bài viết, phương thức **`hasOneThrough`** sẽ lấy bài viết đầu tiên của người dùng thuộc quốc gia **Vietnam** mà không cần phải thực hiện các truy vấn phức tạp.

#### Tóm lại:

* **`hasOneThrough`** trong Laravel rất hữu ích khi bạn muốn lấy một bản ghi duy nhất từ bảng **đích** thông qua bảng **trung gian**, khi mối quan hệ là một-nhiều giữa bảng ban đầu và bảng trung gian.
* Trong ví dụ này, chúng ta sử dụng **`hasOneThrough`** để lấy bài viết đầu tiên của quốc gia **Vietnam** thông qua người dùng mà không cần phải viết câu lệnh **`JOIN`** phức tạp.
