🐘

Laravel でレコード作成する場合にどの方法使うのか問題

2024/09/25に公開

状況

レコード作成する場合にいくつかやり方があるので,どの方法を使うのか迷う.

基本のレコード作成処理は以下のとおりとなる.

  • モデルでの $fillable(または $guarded)プロパティの設定.

  • コントローラなどでの create() メソッドの使用.

現時点の判断

  1. モデルには $fillable (または $guarded)プロパティを設定する.

  2. $request->user() など使える場合は使う.そうでない場合は Model::create() を使用する.

例 1:user_id のない posts テーブルのレコード作成

カラム名 備考
id unsignedBigInteger 主キー
post string 投稿内容
created_at timestamp 作成日時
updated_at timestamp 更新日時

この場合,アプリケーション側から設定できる値を指定(モデルの $fillable)してコントローラなどで create() メソッドを使用する.

// app/Models/Post.php

$fillable = [
  'post',
];
// app/Http/Controllers/PostController.php

public function store(Request $request)
{
  $request->validate([
    'post' => 'required|max:255',
  ]);

  Post::create($request->only('post'));

  return redirect()->route('posts.index');
}

他テーブルとのリレーションがない場合は,このようにシンプルに記述できる.

例 2:user_id のある posts テーブルのレコード作成

カラム名 備考
id unsignedBigInteger 主キー
user_id unsignedBigInteger 外部キー
post string 投稿内容
created_at timestamp 作成日時
updated_at timestamp 更新日時

user_id などの外部キー(他テーブルの id)が含まれる場合には,不正な値で作成されないことが重要である.

create() メソッドを使用してデータを作成することもできるが,その場合モデルの $fillable プロパティに user_id など外部キーを追加する必要がある.

しかし,これではリクエストで送信されたデータで外部キーが設定できてしまうため,不正な値で作成される可能性がある.そのため,リクエストからユーザの情報を取得してリレーションを活用してデータを作成する,という流れになっている.

// app/Models/Post.php

$fillable = [
  'post',
];
// app/Models/User.php

public function posts()
{
  return $this->hasMany(Post::class);
}
// app/Http/Controllers/PostController.php

public function store(Request $request)
{
  $request->validate([
    'post' => 'required|max:255',
  ]);

  $request->user()->posts()->create($request->only('post'));

  return redirect()->route('posts.index');
}
  • $request->user() の部分は現在認証されているユーザーのインスタンス(ユーザ情報)を取得する.

  • posts() は User のモデルから,1 対多の Post モデルを呼び出している.このときの Post モデルにはすでに user_id が設定された状態となる.

  • create Post モデルにデータを作成するためのメソッドである.

リクエストからユーザの情報を取得し,ユーザ id が指定済みの posts テーブルを用意してデータを作成する,と考えるとイメージしやすい.

例 3:user_id のある posts テーブルのレコード作成(リレーションを使わない場合 1)

リレーションを使わない場合は,以下のように記述する.create() メソッドを使用してデータを作成するが,fillable プロパティに外部キーを追加する必要がある.

// app/Models/Post.php

$fillable = [
  'user_id',
  'post',
];
// app/Models/User.php

public function posts()
{
  return $this->hasMany(Post::class);
}
// app/Http/Controllers/PostController.php

public function store(Request $request)
{
  $request->validate([
    'post' => 'required|max:255',
  ]);

  $newRecord = $request->merge(['user_id' => $request->user()->id])->all();
  Post::create($newRecord);

  return redirect()->route('posts.index');
}

例 4:user_id のある posts テーブルのレコード作成(リレーションを使わない場合 2)

以下のようにも記述できる.カラム数が多くなると冗長になるが,fillable プロパティに外部キーを追加する必要がない.

// app/Models/Post.php

$fillable = [
  'post',
];
// app/Http/Controllers/PostController.php

public function store(Request $request)
{
  $request->validate([
    'post' => 'required|max:255',
  ]);

  $post = new Post();
  $post->post = $request->post;
  $post->user_id = $request->user()->id;
  $post->save();

  return redirect()->route('posts.index');
}

まとめ

データ保護の観点から,$fillable を使用した方法を使用したい.まずはモデルでリレーションを設定した上で,$request->user() などを使用してリレーションを活用する方法を考えると良い.他のパターンでも Model::create() を使用して作成するのが安全では.

以上だ( `・ω・)b

GitHubで編集を提案

Discussion