# Laravel Has One Through Eloquent Relationship Tutorial

Đây là một mối quan hệ liên kết các bảng với nhau thông qua một bảng trung gian Ví dụ có 3 bảng:

```php
suppliers
    id - integer
users
    id - integer
    supplier_id - integer
history
    id - integer
    user_id - integer
```

<figure><img src="https://1957079826-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MCBeDUD-PK_RF-YgJRe%2Fuploads%2FUzMcMofT0JNxOQjyvJEj%2FScreenshot_1.png?alt=media&#x26;token=113ba3fa-2fa3-4c5a-8a0f-450b6fb50adb" alt=""><figcaption></figcaption></figure>

Mặc dù bảng `history` không chứ `supplier_id` nhưng chúng ta vẫn có thể truy cập đến `user's history` bới mối quan hệ `hasOneThrough` như sau

```php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model
{
    public function userHistory()
    {
        return $this->hasOneThrough('App\History', 'App\User');
    }
}

```

Với tham số thứ nhất được truyền vào là tên của model mà chúng ta muốn truy cập, tham số thứ 2 là model trung gian. Chúng ta cũng có thể custom các key liên quan đến mối quan hệ này lần lượt là các tham số sau vào hàm định nghĩa quan hệ.

```php
class Supplier extends Model
{
    public function userHistory()
    {
        return $this->hasOneThrough(
            'App\History',
            'App\User',
            'supplier_id', // Khóa ngoại của bảng trung gian user
            'user_id', // Khóa ngoại của bảng chúng ta muốn truy cập đến
            'id', // Khóa mà chúng ta muốn liên kết ở bảng supplier
            'id' // Khóa mà chúng ta muốn liên kết ở bảng user
        );
    }
}
Chú ý: Nếu quan hệ là Many to Many thì sử dụng 
public function userHistory()
  {
    return $this->hasManyThrough(
      History::class,
      User::class,
      'supplier_id',  // Khóa ngoại của bảng trung gian user
      'user_id' // Khóa ngoại của bảng chúng ta muốn truy cập đến
    );
  }
Xem ảnh ở dưới
```

<figure><img src="https://1957079826-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MCBeDUD-PK_RF-YgJRe%2Fuploads%2FNgYx6507coMw5rCpNZcZ%2Fimage.png?alt=media&#x26;token=3bc28f48-f9fd-477d-b405-84f640a4ff64" alt=""><figcaption></figcaption></figure>

hai bảng `user` và `history` chúng ta định nghia như bình thường

```php
// User.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    public function supplier()
    {
         return $this->belongsTo(Supplier::class);
    }
}
```

```php
// Supplier.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Supplier extends Model
{
    public function user()
    {
         return $this->hasOne(User::class);
    }
}

```

## Từ thiết kế bảng sau

### Bài toán 1:

```
suppliers: id, name 
users: id,supplier_id 
histories: id,user_id,description
```

#### Bài toán:

**Mô tả:**\
Giả sử bạn đang quản lý các nhà cung cấp và người dùng trong một công ty phân phối sản phẩm. Mỗi người dùng có thể thực hiện nhiều hoạt động, và mỗi hoạt động này được ghi lại trong bảng `histories`. Bạn cần thực hiện các yêu cầu sau:

1. **Tìm ra nhà cung cấp có số lượng người dùng nhiều nhất.**
2. **Tính tổng số hoạt động (lịch sử) của người dùng thuộc nhà cung cấp đó.**

#### Cấu trúc dữ liệu:

1. **Bảng `suppliers`:**
   * Lưu trữ thông tin về các nhà cung cấp.
   * Các trường: `id`, `name`.
2. **Bảng `users`:**
   * Lưu trữ thông tin về người dùng.
   * Các trường: `id`, `supplier_id` (chỉ ra nhà cung cấp mà người dùng thuộc về).
3. **Bảng `histories`:**
   * Lưu trữ các hoạt động của người dùng.
   * Các trường: `id`, `user_id`, `description`.

#### Câu hỏi cần trả lời:

1. Tìm nhà cung cấp có số lượng người dùng nhiều nhất.
2. Tính tổng số hoạt động mà các người dùng của nhà cung cấp đó đã thực hiện.

***

#### Giải pháp SQL:

```
SELECT 
    s.name AS supplier_name,
    COUNT(u.id) AS total_users,
    COUNT(h.id) AS total_histories
FROM 
    suppliers s
JOIN 
    users u ON s.id = u.supplier_id
LEFT JOIN 
    histories h ON u.id = h.user_id
GROUP BY 
    s.id
ORDER BY 
    total_users DESC
LIMIT 1;
```

#### Giải thích:

1. **JOIN** giữa bảng `suppliers` và `users` để kết nối nhà cung cấp với người dùng của họ.
2. **LEFT JOIN** với bảng `histories` để kết nối người dùng với lịch sử hoạt động của họ.
3. **COUNT(u.id)** đếm số lượng người dùng của mỗi nhà cung cấp.
4. **COUNT(h.id)** đếm số lượng hoạt động của người dùng thuộc mỗi nhà cung cấp.
5. **GROUP BY s.id** nhóm kết quả theo nhà cung cấp.
6. **ORDER BY total\_users DESC LIMIT 1** sắp xếp theo số lượng người dùng giảm dần và chỉ lấy nhà cung cấp có số lượng người dùng lớn nhất.

#### Kết quả:

* **supplier\_name:** Tên nhà cung cấp có số lượng người dùng lớn nhất.
* **total\_users:** Tổng số người dùng của nhà cung cấp đó.
* **total\_histories:** Tổng số lịch sử hoạt động của người dùng thuộc nhà cung cấp đó.

### Bài toán 2:

```
suppliers: id, name 
users: id,country_id,supplier_id
histories: id,user_id,description 
```

Dưới đây là một bài toán cụ thể phù hợp với các bảng `histories`, `suppliers`, và `users` mà bạn đã cung cấp:

#### Bài toán:

**Mô tả:**\
Giả sử bạn đang phát triển một hệ thống quản lý lịch sử hoạt động của các nhà cung cấp và người dùng trong một công ty phân phối sản phẩm. Bạn cần xác định các nhà cung cấp có số lượng người dùng lớn nhất, và tính tổng số lịch sử hoạt động của họ. Đồng thời, bạn cũng cần biết quốc gia của những người dùng này.

**Yêu cầu:**

1. Liệt kê tên các nhà cung cấp có số lượng người dùng lớn nhất.
2. Đối với mỗi nhà cung cấp, tính tổng số lịch sử hoạt động (từ bảng `histories`) của người dùng liên kết với nhà cung cấp đó.
3. Lấy thông tin quốc gia của các người dùng liên kết với nhà cung cấp này.

#### Cấu trúc dữ liệu:

1. **Bảng `histories`:**
   * Lưu trữ các hoạt động lịch sử của người dùng.
   * Mỗi dòng bao gồm `id`, `user_id`, `description`.
2. **Bảng `suppliers`:**
   * Lưu trữ thông tin nhà cung cấp.
   * Mỗi dòng bao gồm `id`, `name` (tên nhà cung cấp).
3. **Bảng `users`:**
   * Lưu trữ thông tin người dùng.
   * Mỗi dòng bao gồm `id`, `country_id` (ID của quốc gia người dùng), `supplier_id` (ID của nhà cung cấp mà người dùng thuộc về).

#### Câu hỏi cần trả lời:

1. Xác định nhà cung cấp nào có số lượng người dùng lớn nhất?
2. Tính tổng số lịch sử hoạt động của người dùng thuộc nhà cung cấp đó?
3. Đưa ra quốc gia của các người dùng đó.

***

#### Giải pháp SQL:

```
SELECT 
    s.name AS supplier_name,
    COUNT(u.id) AS total_users,
    SUM(CASE WHEN h.user_id IS NOT NULL THEN 1 ELSE 0 END) AS total_histories,
    GROUP_CONCAT(DISTINCT c.name) AS countries
FROM 
    suppliers s
JOIN 
    users u ON s.id = u.supplier_id
LEFT JOIN 
    histories h ON u.id = h.user_id
JOIN 
    countries c ON u.country_id = c.id
GROUP BY 
    s.id
ORDER BY 
    total_users DESC
LIMIT 1;
```

#### Giải thích:

1. **JOIN** giữa bảng `suppliers` và `users` để lấy thông tin người dùng theo nhà cung cấp.
2. **LEFT JOIN** với bảng `histories` để tính tổng số lịch sử của người dùng.
3. **GROUP\_CONCAT** để gộp tất cả các quốc gia mà người dùng thuộc vào một chuỗi (nếu có nhiều quốc gia).
4. **COUNT(u.id)** đếm tổng số người dùng cho mỗi nhà cung cấp.
5. **SUM(CASE WHEN h.user\_id IS NOT NULL THEN 1 ELSE 0 END)** tính tổng số lịch sử hoạt động của người dùng.
6. **ORDER BY total\_users DESC LIMIT 1** chọn nhà cung cấp có số lượng người dùng lớn nhất.

***

Kết quả sẽ là thông tin về nhà cung cấp có số lượng người dùng lớn nhất, tổng số lịch sử hoạt động của người dùng thuộc về nhà cung cấp này, và các quốc gia của những người dùng đó.
