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 | /posts | index | posts.index | 
| GET | /posts/create | create | posts.create | 
| POST | /posts | store | posts.store | 
| GET | /posts/{post} | show | posts.show | 
| GET | /posts/{post} | edit | posts.edit | 
| PUT/PATCH | /posts/{post}/edit | update | posts.update | 
| DELETE | /posts/{post} | destroy | posts.destroy | 
動作環境情報
"macOS Ventura" 13.1 "Docker Desktop" 4.15.0 "Laravel Sail" "Laravel Framework" 9.48.0

  
  
  
  

コメント