> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Eloquent 入門

> 說明如何運用 Laravel 的 ORM「Eloquent」操作資料庫。

## Eloquent ORM 是什麼

Laravel 內建了名為 Eloquent 的物件關聯映射器（ORM）。
Eloquent 讓與資料庫的互動變得簡單，並實作了 **ActiveRecord 模式**。

使用 Eloquent 時，你會為每個資料表建立對應的「模型」類別。
透過模型即可完成資料的取得、新增、更新與刪除。

<Info>
  使用 Eloquent 前，請先在 `config/database.php` 設定資料庫連線。
  預設會採用 `.env` 檔案中的 `DB_*` 設定。
</Info>

## 建立模型

以 `make:model` Artisan 指令建立新模型。

```shell theme={null}
php artisan make:model Post
```

若想同時建立 migration，可加 `-m` 選項。

```shell theme={null}
php artisan make:model Post -m
```

模型會建立在 `app/Models` 目錄下。

```php theme={null}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // ...
}
```

## 模型與資料表的對應

Eloquent 會自動由類別名稱推測資料表名稱：
將類別名轉為 snake\_case 且複數形式即為資料表名稱。

| 模型名                    | 資料表名                      |
| ---------------------- | ------------------------- |
| `Post`                 | `posts`                   |
| `User`                 | `users`                   |
| `AirTrafficController` | `air_traffic_controllers` |

若資料表名稱不符命名慣例，可在模型中定義 `$table` 屬性明確指定。

```php theme={null}
class Post extends Model
{
    protected $table = 'blog_posts';
}
```

### 時間戳記

Eloquent 預設會自動管理 `created_at` 與 `updated_at` 欄位。
在 migration 中加入 `$table->timestamps()`，模型儲存 / 更新時就會自動設定值。

若要關閉自動時間戳記，可將 `$timestamps` 設為 `false`。

```php theme={null}
class Post extends Model
{
    public $timestamps = false;
}
```

## Mass Assignment 保護

以 Eloquent 一次匯入資料時，需要進行 mass assignment 保護設定。

### fillable

以 `$fillable` 屬性指定允許批次指派的欄位。

```php theme={null}
class Post extends Model
{
    protected $fillable = [
        'user_id',
        'title',
        'body',
        'published',
    ];
}
```

### guarded

反之，若要指定「不允許」批次指派的欄位，使用 `$guarded`。

```php theme={null}
class Post extends Model
{
    // 僅保護主鍵，其他允許
    protected $guarded = ['id'];
}
```

<Warning>
  將 `$guarded` 設為空陣列會允許所有欄位被批次指派。
  當直接使用使用者輸入時，可能會導致意外的欄位被覆寫；建議使用 `$fillable` 明確列出允許的欄位。
</Warning>

## Eloquent 查詢執行流程

以下示意 `User::where()->get()` 這類查詢的內部執行流程。

```mermaid theme={null}
flowchart LR
    A["User::where('active', true)"] --> B["建構查詢建構器"]
    B --> C["呼叫 ->get()"]
    C --> D["產生 SQL"]
    D --> E["資料庫執行"]
    E --> F["結果轉換為 Eloquent 模型"]
    F --> G["以 Collection 回傳"]
```

## 基本 CRUD 操作

### 讀取（Read）

取得所有紀錄：

```php theme={null}
use App\Models\Post;

$posts = Post::all();
```

依條件取得：

```php theme={null}
// 取得已發佈的文章
$publishedPosts = Post::where('published', true)->get();

// 取得第一筆
$post = Post::where('published', true)->first();

// 以 ID 取得單筆
$post = Post::find(1);

// 找不到時回傳 404
$post = Post::findOrFail(1);
```

### 建立（Create）

以 `create` 方法建立一筆紀錄（需先設定 `$fillable`）。

```php theme={null}
$post = Post::create([
    'title' => '第一篇文章',
    'body' => 'Laravel 是很棒的框架。',
    'published' => true,
]);
```

也可以建立實例後個別設值。

```php theme={null}
$post = new Post;
$post->title = '第一篇文章';
$post->body = 'Laravel 是很棒的框架。';
$post->save();
```

### 更新（Update）

取得模型，修改值後呼叫 `save` 即可更新。

```php theme={null}
$post = Post::find(1);
$post->title = '更新後的標題';
$post->save();
```

使用 `update` 方法可一次更新多個欄位。

```php theme={null}
Post::find(1)->update([
    'title' => '更新後的標題',
    'published' => true,
]);
```

也可以一次更新符合條件的多筆紀錄。

```php theme={null}
Post::where('published', false)->update(['published' => true]);
```

### 刪除（Delete）

使用 `delete` 方法刪除紀錄。

```php theme={null}
$post = Post::find(1);
$post->delete();
```

也可以指定 ID 直接刪除。

```php theme={null}
Post::destroy(1);

// 一次刪除多個 ID
Post::destroy([1, 2, 3]);
```

## 基本查詢方法

| 方法                                           | 說明                      |
| -------------------------------------------- | ----------------------- |
| `Post::all()`                                | 取得所有紀錄                  |
| `Post::find($id)`                            | 以 ID 取得單筆（找不到回傳 `null`） |
| `Post::findOrFail($id)`                      | 以 ID 取得單筆（找不到觸發 404）    |
| `Post::where('column', 'value')->get()`      | 取得符合條件的紀錄               |
| `Post::where('column', 'value')->first()`    | 取得符合條件的第一筆              |
| `Post::where('column', 'value')->count()`    | 取得符合條件的筆數               |
| `Post::orderBy('created_at', 'desc')->get()` | 指定順序取得                  |
| `Post::latest()->get()`                      | 依 `created_at` 降序取得     |
| `Post::limit(10)->get()`                     | 限制筆數                    |

## 實戰範例：以 Post 模型進行資料操作

以下是操作透過 migration 建立的 `posts` 資料表的控制器範例：

```php theme={null}
<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PostController extends Controller
{
    // 顯示文章列表
    public function index(): View
    {
        $posts = Post::where('published', true)
            ->latest()
            ->get();

        return view('posts.index', ['posts' => $posts]);
    }

    // 建立文章
    public function store(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'title' => ['required', 'string', 'max:255'],
            'body' => ['required', 'string'],
            'published' => ['boolean'],
        ]);

        Post::create([
            ...$validated,
            'user_id' => $request->user()->id,
        ]);

        return redirect('/posts');
    }

    // 更新文章
    public function update(Request $request, Post $post): RedirectResponse
    {
        $validated = $request->validate([
            'title' => ['required', 'string', 'max:255'],
            'body' => ['required', 'string'],
        ]);

        $post->update($validated);

        return redirect('/posts');
    }

    // 刪除文章
    public function destroy(Post $post): RedirectResponse
    {
        $post->delete();

        return redirect('/posts');
    }
}
```

<Tip>
  在控制器方法的參數上寫 `Post $post`，Laravel 會自動由路由參數取得模型（路由模型繫結）。
  你就不必自己呼叫 `Post::findOrFail($id)`。
</Tip>

## 模型事件的生命週期

呼叫 `save()` 時會觸發的事件流程如下。新建立或更新會觸發不同事件，但 `saving`、`saved` 兩者皆會觸發。

```mermaid theme={null}
flowchart TD
    A["建立新實例"] --> B{"save()"}
    B --> I["saving 事件"]
    I -->|"新建"| C["creating 事件"]
    C --> D["執行 INSERT"]
    D --> E["created 事件"]
    I -->|"更新"| F["updating 事件"]
    F --> G["執行 UPDATE"]
    G --> H["updated 事件"]
    E --> J["saved 事件"]
    H --> J
```

## 下一步

<Card title="Migrations" icon="table" href="/zh-TW/migrations">
  複習如何用 migration 建立 Eloquent 所使用的資料表。
</Card>


## Related topics

- [Eloquent 關聯入門](/zh-TW/eloquent-relationships.md)
- [Eloquent 集合](/zh-TW/eloquent-collections.md)
- [資料庫 Seeding](/zh-TW/seeding.md)
- [資料庫遷移（Migrations）](/zh-TW/migrations.md)
- [MongoDB](/zh-TW/mongodb.md)
