Laravel9 | 簡単なCRUDアプリを実装する方法

Laravel Sail でプロジェクト作成から簡単なCRUD機能を持つアプリを実装する方法のメモ。

プロジェクト作成

curl -s "https://laravel.build/post-app?with=mysql" | bash

コンテナ起動

cd post-app
sail up -d

モデル作成

モデル作成と同時にマイグレーションファイルとリソースコントローラファイルを作成する

sail artisan make:model Post -mr
INFO  Model [app/Models/Post.php] created successfully.  
INFO  Migration [database/migrations/2023_01_23_165723_create_posts_table.php] created successfully.  
INFO  Controller [app/Http/Controllers/PostController.php] created successfully.  

フォームリクエスト作成

sail artisan make:request PostRequest
INFO  Request [app/Http/Requests/PostRequest.php] created successfully.

マイグレーションファイル編集

upメソッドを編集してカラム追加

# /database/migrations/2023_01_22_004803_create_posts_table.php

$table->string('title');
$table->text('description');

編集後のupメソッド

# /database/migrations/2023_01_22_004803_create_posts_table.php

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('description');
        $table->timestamps();
    });
}

マイグレーション実行

sail artisan migrate

Postモデル編集

fillable設定

protected $fillable = ['title', 'description'];

編集後のPostクラス

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

PostRequest編集

authorizeメソッド修正

false を true に変更する

# /app/Http/Requests/PostRequest.php

public function authorize()
{
    return true;
}

バリデーションルール設定

# /app/Http/Requests/PostRequest.php

public function rules()
{
    return [
        'title'       => ['required', 'max:80'],
        'description' => ['required']
    ];
}

リソースコントローラ編集

index

# /app/Http/Controllers/PostController.php

public function index()
{
    $posts = Post::latest()->paginate(10);
    return view('posts.index', compact('posts'));
}

create

# /app/Http/Controllers/PostController.php

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

store

# /app/Http/Controllers/PostController.php

public function store(PostRequest $request)
{
    Post::create($request->validated());
    return redirect()->route('posts.index')->with('message', '投稿の作成が完了しました。');
}

show

# /app/Http/Controllers/PostController.php

public function show(Post $post)
{
    return view('posts.show', compact('post'));
}

edit

# /app/Http/Controllers/PostController.php

public function edit(Post $post)
{
    return view('posts.edit', compact('post'));
}

update

# /app/Http/Controllers/PostController.php

public function update(PostRequest $request, Post $post)
{
    $post->update([
        'title' => $request->title,
        'description' => $request->description
    ]);

    return redirect()->route('posts.index')->with('message', '投稿の更新が完了しました。');
}

destroy

# /app/Http/Controllers/PostController.php

public function destroy(Post $post)
{
    $post->delete();
    return redirect()->route('posts.index')->with('message', '投稿の削除が完了しました。');
}

Bladeテンプレート

以下、5つのテンプレートファイルを用意します。

# /resources/views/components/app.blade.php
# /resources/views/posts/index.blade.php
# /resources/views/posts/create.php
# /resources/views/posts/edit.php
# /resources/views/posts/show.php

app.blade.php

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Laravel CRUDサンプル</title>
</head>
<body>
  {{ $slot }}
</body>
</html>

index.blade.php

<x-app>

  @if (session()->has('message'))
  <div>{{ session('message') }}</div>
  @endif

  <div>
    <button onclick="location.href='{{ route('posts.create') }}'" >新規作成</button>
  </div>

  <table>
    <thead>
      <tr>
        <th>ID</th>
        <th>タイトル</th>
        <th>本文</th>
        <th>作成日時</th>
        <th>更新日時</th>
        <th colspan="2">編集・削除</th>
      </tr>
    </thead>

    <tbody>

      @foreach ($posts as $post)
      <tr>
        <td>{{ $post->id }}</td>
        <td><a href="{{ route('posts.show', $post->id) }}">{{ $post->title }}</a></td>
        <td>{!! nl2br( htmlspecialchars($post->description) ) !!}</td>
        <td>{{ $post->created_at }}</td>
        <td>{{ $post->updated_at }}</td>
        <td>
          <button onclick="location.href='{{ route('posts.edit', $post->id) }}'" >編集</button>
        </td>
        <td>
          <form action="{{ route('posts.destroy',$post->id) }}" method="POST" onsubmit="return confirm('削除してもよろしいですか?');">
            <input type="hidden" name="_method" value="DELETE">
            <input type="hidden" name="_token" value="{{ csrf_token() }}">
            <button type="submit">削除</button>
          </form>
        </td>
      </tr>
      @endforeach

    </tbody>
  </table>

</x-app>

create.blade.php

<x-app>

  <form method="POST" action="{{ route('posts.index') }}">
    @csrf

    <div>
      <div>
        <label>タイトル</label>
      </div>
      <div>
        <input type="text" name="title" value="{{old('title')}}">
      </div>
      @error('title')
      <span>{{ $message }}</span>
      @enderror
    </div>

    <div>
      <div>
        <label>本文</label>
      </div>
      <div>
        <textarea name="description" rows="4"> {{old('description')}}</textarea>
      </div>
      @error('description')
      <span>{{ $message }}</span>
      @enderror
    </div>

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

  </form>

</x-app>

edit.blade.php

<x-app>

  <form method="POST" action="{{ route('posts.update',$post->id) }}">
    @csrf
    @method('PUT')

    <div>
      <div>
        <label>タイトル</label>
      </div>
      <div>
        <input type="text" name="title" value="{{old('title',$post->title)}}">
      </div>
      @error('title')
      <span>{{ $message }}</span>
      @enderror
    </div>

    <div>
      <div>
        <label>本文</label>
      </div>
      <div>
        <textarea name="description" rows="4"> {{old('description',$post->description)}}</textarea>
      </div>
      @error('description')
      <span>{{ $message }}</span>
      @enderror
    </div>

    <button type="submit">更新</button>

  </form>

</x-app>

show.blade.php

<x-app>

  <div>
    <label for="title">タイトル</label>
    <p>{{old('title',$post->title)}}</p>
  </div>

  <div>
    <label for="description">本文</label>
    <p>{{old('description',$post->description)}}</p>
  </div>

  <button onclick="location.href='{{ route('posts.index') }}'" >戻る</button>

</x-app>

ルーティング設定

use宣言 追加

use App\Http\Controllers\PostController;

ルート設定

まとめて設定した場合

Route::resource('posts', PostController::class);

バラして設定した場合

Route::get('/posts', [PostController::class, 'index'])->name('posts.index');
Route::post('/posts', [PostController::class, 'store'])->name('posts.store');
Route::get('/posts/create', [PostController::class, 'create'])->name('posts.create');
Route::get('/posts/{post}', [PostController::class, 'show'])->name('posts.show');
Route::get('/posts/{post}/edit', [PostController::class, 'ineditdex'])->name('posts.edit');
Route::put('/posts/{post}', [PostController::class, 'update'])->name('posts.update');
Route::delete('/posts/{post}', [PostController::class, 'destroy'])->name('posts.destroy');

ルーティング設定状況確認

sail artisan route:list
GET|HEAD        posts ...............................posts.index › PostController@index
POST            posts ...............................posts.store › PostController@store
GET|HEAD        posts/create ......................posts.create › PostController@create
GET|HEAD        posts/{post} ..........................posts.show › PostController@show
PUT|PATCH       posts/{post} ......................posts.update › PostController@update
DELETE          posts/{post} ....................posts.destroy › PostController@destroy
GET|HEAD        posts/{post}/edit .....................posts.edit › PostController@edit

リソースコントローラにより処理されるアクション一覧

HTTPメソッドURIアクションルート名
GET/postsindexposts.index
GET/posts/createcreateposts.create
POST/postsstoreposts.store
GET/posts/{post}showposts.show
GET/posts/{post}editposts.edit
PUT/PATCH/posts/{post}/editupdateposts.update
DELETE/posts/{post}destroyposts.destroy

動作環境情報

"macOS Ventura" 13.1
"Docker Desktop" 4.15.0
"Laravel Sail"
"Laravel Framework" 9.48.0

関連記事

コメント

タイトルとURLをコピーしました