こんにちは、かつコーチです。
LaravelにおけるModel(モデル)は、アプリケーションのデータとビジネスロジックを扱う非常に重要な部分です。
モデルはデータベースとのやり取りを行い、データの取得、保存、更新、削除
といった操作を簡単に行えるように設計されています。
Laravelのモデルは、ORM(Object-Relational Mapping)であるEloquentを利用して、
データベースのテーブルとオブジェクトを関連付ける役割を果たします。
ここでは、Laravelのモデルをさらに深く掘り下げて、主要な機能や概念について詳しく解説します。
モデルの基礎
Laravelでは、モデルは通常データベースの1つのテーブルに対応します。
例えば、Post
モデルはposts
テーブルと関連付けられています。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
// デフォルトではテーブル名はモデル名の複数形になるが、カスタマイズも可能
protected $table = 'posts';
// Eloquentはデフォルトでタイムスタンプを管理する(created_at, updated_at)
public $timestamps = true;
// 複数代入可能な属性を指定 (Mass Assignment)
protected $fillable = ['title', 'content'];
}
- テーブル名の指定: デフォルトでは、モデル名のスネークケース複数形がテーブル名として使用されますが、
protected $table
を使ってカスタマイズできます。 - タイムスタンプの管理: Laravelは自動的に
created_at
とupdated_at
を更新します。この挙動はpublic $timestamps = false;
で無効化できます。 - Mass Assignment:
$fillable
プロパティを使って、配列から一度に複数のフィールドを挿入できる属性を指定します。
Eloquent ORMの基本機能
Eloquentは、Laravelのモデルを通じてデータベースとのやり取りを
オブジェクト形式で行うことを可能にするORMです。
SQLを書かずに、PHPのオブジェクトを使ってデータ操作ができます。
データの取得
Eloquentモデルは、複数の便利なメソッドを提供しており、データベースからレコードを簡単に取得できます。
- 全てのレコードを取得:
$posts = Post::all();
- 特定のIDのレコードを取得:
$post = Post::find(1);
- 条件付きでレコードを取得:
$posts = Post::where('status', 'published')->get();
- 最初のレコードを取得:
$post = Post::where('status', 'published')->first();
データの挿入と更新
Eloquentモデルを使うと、新しいデータの挿入や既存データの更新も簡単です。
- 新しいレコードを挿入:
$post = new Post();
$post->title = 'New Post';
$post->content = 'This is the content of the post.';
$post->save();
または、create
メソッドを使用して一度に挿入できます($fillable
プロパティを設定しておく必要があります)。
Post::create([
'title' => 'New Post',
'content' => 'This is the content of the post.'
]);
- 既存レコードを更新:
$post = Post::find(1);
$post->title = 'Updated Title';
$post->save();
データの削除
Eloquentモデルは、簡単にレコードを削除するメソッドも提供しています。
- 単一レコードの削除:
$post = Post::find(1);
$post->delete();
- 条件に基づいて複数レコードを削除:
Post::where('status', 'draft')->delete();
リレーション(モデル間の関係)
データベースのテーブル間には、しばしばリレーション(関係性)が存在します。
LaravelのEloquentは、これらのリレーションを直感的に扱うことができるメソッドを提供しています。
1対1のリレーション(One-to-One)
1対1のリレーションは、1つのモデルが1つの別のモデルに関連付けられている場合に使用します。
class User extends Model
{
public function profile()
{
return $this->hasOne(Profile::class);
}
}
- 利用方法:
$user = User::find(1);
$profile = $user->profile;
1対多のリレーション(One-to-Many)
1対多のリレーションは、1つのモデルが複数の別のモデルに関連付けられている場合に使用します。
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
}
- 利用方法:
$post = Post::find(1);
$comments = $post->comments;
多対多のリレーション(Many-to-Many)
多対多のリレーションは、複数のモデル同士が相互に関連している場合に使用します。
このリレーションは、中間テーブルを使用して管理されます。
class Post extends Model
{
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
- 利用方法:
$post = Post::find(1);
$tags = $post->tags;
多形リレーション(Polymorphic Relationships)
多形リレーションは、異なるモデルが同じリレーションを共有する場合に使用されます。
たとえば、Post
やVideo
モデルの両方がComment
を持つ場合に有効です。
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
- 利用方法:
$post = Post::find(1);
$comments = $post->comments; // Postと関連するコメントを取得
スコープ(Scopes)
スコープは、クエリに対して共通の制約を追加するためのメソッドです。
たとえば、「公開済みの投稿のみを取得する」ようなクエリを再利用したい場合に便利です。
ローカルスコープ
ローカルスコープは、特定のクエリに対して適用できるメソッドです。
class Post extends Model
{
public function scopePublished($query)
{
return $query->where('status', 'published');
}
}
- 利用方法:
$publishedPosts = Post::published()->get();
グローバルスコープ
グローバルスコープは、モデルのすべてのクエリに対して自動的に適用されるスコープです。
use Illuminate\Database\Eloquent\Builder;
class Post extends Model
{
protected static function booted()
{
static::addGlobalScope('published', function (Builder $builder) {
$builder->where('status', 'published');
});
}
}
イベントとリスナー
Eloquentモデルには、様々なイベントが発生します。
たとえば、モデルの保存前、削除後などのタイミングでイベントが発生し、
それに応じて処理を実行できます。
- イベントの種類:
creating
: レコードが保存される前に発生created
: レコードが保存された後に発生updating
: レコードが更新される前に発生updated
: レコードが更新された後に発生deleting
: レコードが削除される前に発生deleted
: レコードが削除された後に発生
class Post extends Model
{
protected static function booted()
{
static::created(function ($post) {
// 新しい投稿が作成された後に実行される処理
Log::info('Post created: ' . $post->title);
});
}
}
アクセサとミューテータ
モデルの属性に対して値を取得する際や設定する際に、
特定のロジックを適用したい場合にはアクセサやミューテータを使用します。
アクセサ(Accessor)
アクセサは、モデルの属性を取得する際にカスタムロジックを適用します。
class Post extends Model
{
public function getTitleAttribute($value)
{
return ucfirst($value); // titleの最初の文字を大文字に変換
}
}
ミューテータ(Mutator)
ミューテータは、モデルの属性に値を設定する際にカスタムロジックを適用します。
ミューテータを使うと、データベースに保存する前に特定のロジックを適用して、値を加工できます。
例えば、投稿タイトルをデータベースに保存する前に自動的に小文字に変換したい場合、
以下のように設定します。
class Post extends Model
{
// titleフィールドに値を設定する際にミューテータを使用
public function setTitleAttribute($value)
{
$this->attributes['title'] = strtolower($value); // 小文字に変換
}
}
- アクセサはデータベースから取得する時にデータを加工し、ミューテータはデータを保存する時に加工します。
- アクセサとミューテータを使用することで、データの加工やフォーマットが簡単になり、ビューやロジック内での操作が減少し、コードの保守性が向上します。
キャスト(Casts)
**キャスト(Casts)**を使うと、Eloquentモデルがデータベースのカラムのデータ型を自動的に変換します。
例えば、json
カラムを配列として扱う場合や、日時フィールドをCarbon
インスタンスとして扱うことができます。
class Post extends Model
{
// キャストを設定するプロパティ
protected $casts = [
'is_published' => 'boolean', // ブール値にキャスト
'published_at' => 'datetime', // DateTimeにキャスト
'metadata' => 'array', // JSONデータを配列にキャスト
];
}
- キャストの主な用途:
boolean
:true
/false
のブール値として扱う。datetime
:Carbon
インスタンスとして日時を操作できる。array
: JSONフィールドを自動的に配列に変換。
これにより、例えば、$post->is_published
を使用すると、0
や1
の値がブール値として返されるため、
コードが簡潔で直感的になります。
ソフトデリート(Soft Deletes)
ソフトデリートは、データを実際には削除せずに「削除された」とマークするための機能です。
これにより、データベースから物理的に削除することなく、レコードを非表示にすることが可能です。
ソフトデリートの設定
ソフトデリートを有効にするためには、モデルでSoftDeletes
トレイトを使用します。
また、テーブルにdeleted_at
カラムを追加する必要があります。
use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model
{
use SoftDeletes;
// ソフトデリート用のカラム
protected $dates = ['deleted_at'];
}
ソフトデリートの操作
- レコードのソフトデリート:
$post = Post::find(1);
$post->delete(); // 実際には削除されず、deleted_atにタイムスタンプが記録される
- ソフトデリートされたレコードの取得:
// ソフトデリートされたレコードも含めて取得する
$posts = Post::withTrashed()->get();
- ソフトデリートされたレコードのみを取得:
// ソフトデリートされたレコードのみ取得する
$deletedPosts = Post::onlyTrashed()->get();
- ソフトデリートされたレコードの復元:
$post = Post::withTrashed()->find(1);
$post->restore(); // deleted_atがnullになり、レコードが復活
リレーションシップにおけるソフトデリート
Eloquentはリレーションシップにおけるソフトデリートもサポートしています。
例えば、1対多のリレーションにおいて、ソフトデリートされた子レコードを無視することができます。
class Post extends Model
{
use SoftDeletes;
public function comments()
{
return $this->hasMany(Comment::class)->withTrashed();
}
}
モデルイベント
Laravelのモデルは、データベース操作に対してフックを提供するイベントを発生させます。
これにより、例えば、モデルが保存される前や削除される後に
カスタムロジックを実行することが可能です。
主なイベント
creating
: レコードが保存される前に発生。created
: レコードが保存された後に発生。updating
: レコードが更新される前に発生。updated
: レコードが更新された後に発生。deleting
: レコードが削除される前に発生。deleted
: レコードが削除された後に発生。restoring
: ソフトデリートされたレコードが復元される前に発生。restored
: ソフトデリートされたレコードが復元された後に発生。
イベントリスナーの設定例
class Post extends Model
{
protected static function booted()
{
static::created(function ($post) {
// 投稿が作成された後の処理
Log::info('New post created: ' . $post->title);
});
}
}
これにより、Eloquentモデルに対して何らかの処理が実行された際に、
自動的に関連する処理を呼び出すことができます。
モデルのベストプラクティス
- 単一責任の原則: モデルは1つのテーブルに関連するデータ操作のみに焦点を当てるべきです。ビジネスロジックや特定の操作は、モデルの外部、例えばリポジトリパターンやサービスクラスで管理するのがベストプラクティスです。
- リレーションの使用: 可能な限りEloquentのリレーションを活用し、SQLクエリを効率的に実行できるようにします。リレーションの遅延読み込みやN+1問題には特に注意が必要です。
- バリデーションの分離: モデル自体にバリデーションロジックを直接書くことは避け、フォームリクエストクラスなどを使用して、バリデーションとデータ処理を分離することが推奨されます。
まとめ
Laravelのモデルは、データベースとのやり取りをシンプルかつ強力に行うための重要な要素です。
Eloquent ORMを活用することで、SQLを直接書かずにデータベース操作を簡単に行うことができ、
また、リレーションシップやイベント、スコープ、キャストなどの豊富な機能を利用して、
モデル層でのロジックを効率化できます。
さらに、アクセサやミューテータを使うことで、
データの取得や保存時に柔軟なロジックを適用することが可能です。
また、ソフトデリートやイベントを使うことで、
データのライフサイクルをよりきめ細かく管理できる点も特徴です。
モデルをうまく活用し、適切な場所にロジックを分離することで、
Laravelアプリケーションはよりシンプルで保守しやすい構造になります。