L

一覧・登録・編集・削除を実装する

設計したメモ投稿アプリを、リソースコントローラ・ビュー・認証・認可を組み合わせて実際に作り上げます。

広告枠(記事上)— 本番では AdSense 広告が表示されます

前の単元の設計をもとに、メモ投稿アプリを実装します。これまでの章の総まとめです。一気に見えますが、1つ1つはすべて既に学んだことです。落ち着いて進めましょう。

1. 下準備(モデル・コントローラなどを作る)

必要なファイルをまとめて作ります。

php artisan make:model Post -mcr

-mcr は「migration(マイグレーション)・controller(コントローラ)・resource(7メソッド入り)」をまとめて作るオプションです。

マイグレーション(第4章)に、前の単元で設計した列を書いて実行します。

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained();
    $table->string('title');
    $table->text('body');
    $table->timestamps();
});
php artisan migrate

Post モデルに $fillable を設定します(第5章)。

class Post extends Model
{
    protected $fillable = ['title', 'body'];
}

2. ルート(ログイン必須にする)

routes/web.php に、ログイン必須のリソースルートを書きます(第7章)。

use App\Http\Controllers\PostController;

Route::resource('posts', PostController::class)->middleware('auth');

3. コントローラ

app/Http/Controllers/PostController.php を実装します。各メソッドの中身は、これまで学んだ内容そのものです。認可で Gate を使うので、ファイル先頭に use Illuminate\Support\Facades\Gate; を追加しておきます(第7章を参照)。

public function index()
{
    // 自分のメモだけを新しい順で取得
    $posts = Post::where('user_id', auth()->id())->latest()->get();
    return view('posts.index', ['posts' => $posts]);
}

public function create()
{
    return view('posts.create');
}

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

    // 投稿者=ログイン中の人として保存
    $request->user()->posts()->create($validated);

    return redirect('/posts');
}

public function edit(Post $post)
{
    Gate::authorize('update', $post); // 本人だけ(第7章)
    return view('posts.edit', ['post' => $post]);
}

public function update(Request $request, Post $post)
{
    Gate::authorize('update', $post);

    $validated = $request->validate([
        'title' => ['required', 'max:255'],
        'body'  => ['required'],
    ]);

    $post->update($validated);
    return redirect('/posts');
}

public function destroy(Post $post)
{
    Gate::authorize('delete', $post);
    $post->delete();
    return redirect('/posts');
}

$request->user()->posts()->create(...) は、リレーション(第5章)を使った書き方で、user_id が自動でログイン中の人になります。

store で使うリレーションのため、User モデルに posts() を、Post モデルに user() を書いておきます(第5章)。また編集・削除の本人チェックのため、ポリシー(第7章)も用意します。

php artisan make:policy PostPolicy --model=Post
// PostPolicy.php
public function update(User $user, Post $post): bool
{
    return $user->id === $post->user_id;
}

public function delete(User $user, Post $post): bool
{
    return $user->id === $post->user_id;
}

4. 画面(Blade)

一覧 resources/views/posts/index.blade.php

<a href="/posts/create">新規メモ</a>

@forelse ($posts as $post)
    <div>
        <h2>{{ $post->title }}</h2>
        <p>{{ $post->body }}</p>
        <a href="/posts/{{ $post->id }}/edit">編集</a>
        <form action="/posts/{{ $post->id }}" method="POST">
            @csrf
            @method('DELETE')
            <button type="submit">削除</button>
        </form>
    </div>
@empty
    <p>まだメモがありません。</p>
@endforelse

新規作成 resources/views/posts/create.blade.php

<form action="/posts" method="POST">
    @csrf
    <input type="text" name="title" value="{{ old('title') }}">
    @error('title') <p style="color:red">{{ $message }}</p> @enderror

    <textarea name="body">{{ old('body') }}</textarea>
    @error('body') <p style="color:red">{{ $message }}</p> @enderror

    <button type="submit">保存</button>
</form>

@method('DELETE') とは

HTMLのフォームは GET と POST しか送れません。削除(DELETE)や更新(PUT)をしたいときは、@method('DELETE') を書いて Laravel に「本当はDELETEです」と伝えます。これも @csrf とセットで覚えておきましょう。

編集画面(edit.blade.php)は、create とほぼ同じフォームに @method('PUT') を足し、value に既存の値({{ old('title', $post->title) }})を入れれば作れます。

5. 動作確認

php artisan serve で起動し、ログインしてから /posts を開きます。新規作成・編集・削除ができ、他人のメモには編集・削除ボタンが効かない(ポリシーで弾かれる)ことを確認できれば完成です。

まとめ

  • make:model Post -mcr で必要なファイルを一括生成。
  • ルートは Route::resource(...)->middleware('auth') でCRUD+ログイン必須。
  • コントローラの各メソッドは、これまで学んだ取得・保存・更新・削除・認可の組み合わせ。
  • フォームの削除・更新は @method('DELETE') / @method('PUT') を使う。
  • これで第8章=アプリ作りの一周が完成です。最終章では、作ったアプリをインターネットに公開します。
広告枠(記事下)— 本番では AdSense 広告が表示されます