【Laravel/Eloquent】モデル連携でよく使う機能まとめ



Laravel の Eloquent ORM は、データベース操作をシンプルかつ直感的に行える強力なツールです。  
この記事では、以下の内容を詳しく解説します。

- 基本操作（レコードの取得・作成・更新・削除）
- モデル間のリレーション（1対多、多対多、1対1）  
  → 各リレーションのサンプルテーブルも提示
- アクセサ（Accessor）とミューテータ（Mutator）
- クエリスコープ（Query Scopes）
- モデルイベント（Model Events）
- その他の便利機能（ソフトデリート、ペジネーションなど）

---

## 1. 基本操作

### 1-1. レコードの取得

- **全件取得**
  ```php
  $users = App\Models\User::all();
  ```

- **条件付き取得**
  ```php
  $activeUsers = App\Models\User::where('active', 1)->get();
  ```

- **単一レコードの取得**
  ```php
  $user = App\Models\User::find(1); // 主キーで取得
  // または
  $user = App\Models\User::where('email', 'example@example.com')->first();
  ```

### 1-2. レコードの作成・更新・削除

- **新規作成**
  ```php
  $user = new App\Models\User();
  $user->name  = 'John Doe';
  $user->email = 'john@example.com';
  $user->save();
  ```

- **create メソッドによる一括作成**  
  ※モデル内で `$fillable` または `$guarded` の設定が必要です。
  ```php
  $user = App\Models\User::create([
      'name'     => 'Jane Doe',
      'email'    => 'jane@example.com',
      'password' => bcrypt('secret')
  ]);
  ```

- **更新**
  ```php
  $user = App\Models\User::find(1);
  $user->update(['name' => 'John Smith']);
  ```

- **削除**
  ```php
  $user = App\Models\User::find(1);
  $user->delete();
  ```

---

## 2. モデル間のリレーション

Eloquent では、モデル同士のリレーションを直感的に定義できます。  
以下に、代表的なリレーション（1対多、多対多、1対1）のサンプルシナリオとテーブル例、モデル定義、使用例を示します。

### 2-1. 1対多 (One To Many)

#### サンプルシナリオ
- **ユーザー (users)**  
  → 各ユーザーが複数の投稿 (posts) を持つ

#### テーブルサンプル

**`users` テーブル**

| カラム名   | データ型      | 説明               |
|------------|---------------|--------------------|
| id         | INT (PK)      | ユーザーの主キー   |
| name       | VARCHAR(255)  | ユーザー名         |
| email      | VARCHAR(255)  | メールアドレス     |
| created_at | TIMESTAMP     | 作成日時           |
| updated_at | TIMESTAMP     | 更新日時           |

**`posts` テーブル**

| カラム名   | データ型      | 説明                                             |
|------------|---------------|--------------------------------------------------|
| id         | INT (PK)      | 投稿の主キー                                     |
| user_id    | INT (FK)      | 投稿者のユーザーID（users.id への外部キー）       |
| title      | VARCHAR(255)  | 投稿タイトル                                     |
| content    | TEXT          | 投稿内容                                         |
| created_at | TIMESTAMP     | 作成日時                                         |
| updated_at | TIMESTAMP     | 更新日時                                         |

#### モデル定義

**User モデル (`app/Models/User.php`)**
```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    // ユーザーは複数の投稿を持つ
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}
```

**Post モデル (`app/Models/Post.php`)**
```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // 投稿は1人のユーザーに属する
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
```

#### 使用例
```php
$user = App\Models\User::find(1);
$posts = $user->posts; // ユーザーID 1 の全投稿を取得
```

---

### 2-2. 多対多 (Many To Many)

#### サンプルシナリオ
- **投稿 (posts)**  
  → 複数のタグ (tags) を持つ  
- **タグ (tags)**  
  → 複数の投稿に付与できる

#### テーブルサンプル

**`posts` テーブル**

| カラム名   | データ型      | 説明          |
|------------|---------------|---------------|
| id         | INT (PK)      | 投稿の主キー  |
| title      | VARCHAR(255)  | 投稿タイトル  |
| content    | TEXT          | 投稿内容      |
| created_at | TIMESTAMP     | 作成日時      |
| updated_at | TIMESTAMP     | 更新日時      |

**`tags` テーブル**

| カラム名   | データ型      | 説明         |
|------------|---------------|--------------|
| id         | INT (PK)      | タグの主キー |
| name       | VARCHAR(255)  | タグ名       |
| created_at | TIMESTAMP     | 作成日時     |
| updated_at | TIMESTAMP     | 更新日時     |

**`post_tag` 中間テーブル**

| カラム名 | データ型 | 説明                                        |
|----------|----------|---------------------------------------------|
| post_id  | INT (FK) | 投稿ID（posts.id への外部キー）              |
| tag_id   | INT (FK) | タグID（tags.id への外部キー）               |

#### モデル定義

**Post モデル (`app/Models/Post.php`)**
```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // 投稿は複数のタグを持つ
    public function tags()
    {
        return $this->belongsToMany(Tag::class);
    }
}
```

**Tag モデル (`app/Models/Tag.php`)**
```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model
{
    // タグは複数の投稿に属する
    public function posts()
    {
        return $this->belongsToMany(Post::class);
    }
}
```

#### 使用例
```php
$post = App\Models\Post::find(1);
$tags = $post->tags; // 投稿に付いたタグを取得

$tag = App\Models\Tag::find(1);
$posts = $tag->posts; // タグが付いた全投稿を取得
```

---

### 2-3. 1対1 (One To One)

#### サンプルシナリオ
- **ユーザー (users)**  
  → 各ユーザーに1つのプロフィール (profiles) がある

#### テーブルサンプル

**`users` テーブル**

| カラム名   | データ型      | 説明              |
|------------|---------------|-------------------|
| id         | INT (PK)      | ユーザーの主キー  |
| name       | VARCHAR(255)  | ユーザー名        |
| email      | VARCHAR(255)  | メールアドレス    |
| created_at | TIMESTAMP     | 作成日時          |
| updated_at | TIMESTAMP     | 更新日時          |

**`profiles` テーブル**

| カラム名   | データ型      | 説明                                           |
|------------|---------------|------------------------------------------------|
| id         | INT (PK)      | プロフィールの主キー                           |
| user_id    | INT (FK)      | ユーザーID（users.id への外部キー）             |
| bio        | TEXT          | 自己紹介文                                     |
| twitter    | VARCHAR(255)  | Twitter アカウント                             |
| created_at | TIMESTAMP     | 作成日時                                       |
| updated_at | TIMESTAMP     | 更新日時                                       |

#### モデル定義

**User モデル (`app/Models/User.php`)**
```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    // ユーザーは1つのプロフィールを持つ
    public function profile()
    {
        return $this->hasOne(Profile::class);
    }
}
```

**Profile モデル (`app/Models/Profile.php`)**
```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Profile extends Model
{
    // プロフィールは1人のユーザーに属する
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
```

#### 使用例
```php
$user = App\Models\User::find(1);
$profile = $user->profile; // ユーザーのプロフィールを取得

$profile = App\Models\Profile::find(1);
$user = $profile->user; // プロフィールからユーザー情報を取得
```

---

## 3. アクセサ (Accessors) とミューテータ (Mutators)

Eloquent では、属性値の取得時および保存時に自動的な変換を行うことが可能です。

### アクセサ（取得時の変換）
```php
// app/Models/User.php
public function getNameAttribute($value)
{
    // DB の値 "john" を "JOHN" に変換して返す
    return strtoupper($value);
}
```

使用例:
```php
$user = App\Models\User::find(1);
echo $user->name; // 常に大文字で表示
```

### ミューテータ（保存時の変換）
```php
// app/Models/User.php
public function setEmailAttribute($value)
{
    // 保存前にメールアドレスを小文字に変換
    $this->attributes['email'] = strtolower($value);
}
```

使用例:
```php
$user = new App\Models\User();
$user->email = 'John@Example.Com'; // 保存時に "john@example.com" に変換される
$user->save();
```

---

## 4. クエリスコープ (Query Scopes)

共通のクエリ条件をスコープとして定義することで、コードの再利用性が向上します。

### ローカルスコープ
```php
// app/Models/User.php
public function scopeActive($query)
{
    return $query->where('active', 1);
}
```

使用例:
```php
$activeUsers = App\Models\User::active()->get();
```

### グローバルスコープ
```php
// app/Models/User.php
protected static function booted()
{
    static::addGlobalScope('active', function ($builder) {
        $builder->where('active', 1);
    });
}
```

---

## 5. モデルイベント

Eloquent では、モデルのライフサイクルに合わせたイベントを利用して自動処理を実行できます。

```php
// app/Models/User.php
protected static function booted()
{
    static::created(function ($user) {
        \Log::info('新規ユーザーが登録されました: ' . $user->email);
    });
}
```

---

## 6. その他の便利な機能

### 6-1. ソフトデリート (Soft Deletes)
論理削除を行う場合、`SoftDeletes` トレイトを利用します。
```php
use Illuminate\Database\Eloquent\SoftDeletes;

class Post extends Model
{
    use SoftDeletes;
    protected $dates = ['deleted_at'];
}
```

### 6-2. ペジネーション (Pagination)
大量のレコードを扱う際は、簡単にページネーションが利用できます。
```php
$users = App\Models\User::paginate(15);
```
Blade テンプレートでの表示例:
```blade
{{ $users->links() }}
```

---

## まとめ

Eloquent の各機能を組み合わせることで、以下のように柔軟かつ効率的なデータベース操作が実現できます。

- **基本操作:**  
  レコードの取得、作成、更新、削除

- **リレーション:**  
  - **1対多:** ユーザーと投稿の関係を `hasMany`/`belongsTo` で定義  
    （例：`users` と `posts` テーブル、外部キー `user_id`）
  - **多対多:** 投稿とタグの関係を `belongsToMany` で定義  
    （例：中間テーブル `post_tag` で管理）
  - **1対1:** ユーザーとプロフィールの関係を `hasOne`/`belongsTo` で定義  
    （例：`users` と `profiles` テーブル、外部キー `user_id`）

- **アクセサ/ミューテータ:**  
  取得時・保存時の自動変換処理

- **クエリスコープ:**  
  共通クエリ条件の再利用

- **モデルイベント:**  
  ライフサイクルに応じた自動処理（例：新規登録時のログ出力など）

- **その他の機能:**  
  ソフトデリート、ペジネーションなど

これらの機能を活用することで、コードの可読性と再利用性が向上し、堅牢なアプリケーションの開発が可能になります。ぜひ各プロジェクトで試してみてください！

---

#### 参考リンク

- [Laravel 公式ドキュメント - Eloquent ORM](https://laravel.com/docs/11.x/eloquent)
- [Laravel 公式ドキュメント - Eloquent Relationships](https://laravel.com/docs/11.x/eloquent-relationships)
- [Laravel 公式ドキュメント - Eloquent Mutators](https://laravel.com/docs/11.x/eloquent-mutators)
- [Laravel 公式ドキュメント - Eloquent Collections](https://laravel.com/docs/11.x/eloquent-collections)
- [Laravel 公式ドキュメント - Pagination](https://laravel.com/docs/11.x/pagination)

---

以上、リレーション機能のテーブルサンプルを含む Eloquent の各機能まとめ【完全版】でした。  
質問やご意見があれば、ぜひコメントしてください！