> ## 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
```

マイグレーションと同時に作成する場合は `-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はクラス名からテーブル名を自動的に推測します。
クラス名をスネークケースの複数形に変換したものがテーブル名になります。

| モデル名                   | テーブル名                     |
| ---------------------- | ------------------------- |
| `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` カラムを自動管理します。
マイグレーションで `$table->timestamps()` を追加しておくと、モデルの保存・更新時に自動的に値がセットされます。

タイムスタンプの自動管理を無効にするには `$timestamps` を `false` に設定します。

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

## マスアサインメント保護

Eloquentでまとめてデータを保存する際は、マスアサインメント保護の設定が必要です。

### 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}
// publishedがtrueの投稿を取得
$publishedPosts = Post::where('published', true)->get();

// 最初の1件を取得
$post = Post::where('published', true)->first();

// IDで1件を取得
$post = Post::find(1);

// 見つからない場合は404レスポンスを返す
$post = Post::findOrFail(1);
```

### レコードの作成（Create）

`create` メソッドでレコードを1つ挿入します（`$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で1件取得（見つからなければ `null`） |
| `Post::findOrFail($id)`                      | IDで1件取得（見つからなければ404）     |
| `Post::where('column', 'value')->get()`      | 条件に一致するレコードを取得           |
| `Post::where('column', 'value')->first()`    | 条件に一致する最初の1件を取得          |
| `Post::where('column', 'value')->count()`    | 条件に一致する件数を取得             |
| `Post::orderBy('created_at', 'desc')->get()` | 並び順を指定して取得               |
| `Post::latest()->get()`                      | `created_at` の降順で取得      |
| `Post::limit(10)->get()`                     | 件数を絞って取得                 |

## 実践例：Postモデルを使ったデータ操作

マイグレーションで作成した `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="マイグレーション" icon="table" href="/jp/migrations">
  Eloquentが利用するテーブルをマイグレーションで作成する方法を振り返ります。
</Card>
