今回は、作成したスケジュールに予約を管理する機能を追加する方法を紹介します。
予約機能作成
①予約データのモデル、マイグレーションファイル、コントローラー一式を作成する。
$sail artisan make:model Reserve --controller --resource --requests --migration※下記のように表示されていたら成功です。
INFO Model [app/Models/Reserve.php] created successfully.
INFO Migration [database/migrations/yyyy_mm_dd_xxxxxx_create_reserves_table.php] created successfully.
INFO Request [app/Http/Requests/StoreReserveRequest.php] created successfully.
INFO Request [app/Http/Requests/UpdateReserveRequest.php] created successfully.
INFO Controller [app/Http/Controllers/ReserveController.php] created successfully. ②コントローラーのパスを修正する。
app/Http/Controllers/ReserveController.php を app/Http/Controllers/Admin/に移動し、ファイル内のnamespaceを修正します。
※下記、L3~L4を参照
- namespace App\Http\Controllers;
+ namespace App\Http\Controllers\Admin;継承クラスのパス対応も追記します。
※下記、L5を参照
<?php
namespace App\Http\Controllers\Admin;
+ use App\Http\Controllers\Controller;
/*
以下省略
*/③スケジュールテーブルを作成する。
生成されたマイグレーションファイルを開き、スケジュールテーブルのカラムを作成します。
※下記、L18~L20を参照
public function up()
{
Schema::create('reserves', function (Blueprint $table) {
$table->id();
+ $table->foreignId('schedule_id')->constrained();
+ $table->foreignId('user_id')->constrained();
+ $table->timestamp('canceled_at')->nullable();
$table->timestamps();
// 同一スケジュールには1つしか予約できないようユニーク制約をつける
$table->unique(['schedule_id', 'user_id']);
});
}④マイグレーションを実施する。
$sail artisan migrate⑤ルーティングを追加する。
routes/admin.php に 予約管理機能用のルーティングを追加します。
※下記、L41を参照
Route::middleware('auth:admin')->group(function () {
// 中略
+ Route::resource('reserve', ReserveController::class);
});⑥Controllerの参照を追加する。
※下記、L12を参照
use App\Http\Controllers\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Auth\ConfirmablePasswordController;
use App\Http\Controllers\Auth\EmailVerificationNotificationController;
use App\Http\Controllers\Auth\EmailVerificationPromptController;
use App\Http\Controllers\Auth\NewPasswordController;
use App\Http\Controllers\Auth\PasswordController;
use App\Http\Controllers\Auth\PasswordResetLinkController;
use App\Http\Controllers\Auth\RegisteredUserController;
use App\Http\Controllers\Auth\VerifyEmailController;
+ use App\Http\Controllers\Admin\ReserveController⑦表示用の画面ファイルを作成する。
resources/views/admin/reserve に スケジュール一覧画面、新規登録画面、編集画面の3画面を作成します。
@extends('adminlte::page')
@section('title', '予約一覧')
@section('content_header')
<h1>予約一覧</h1>
@stop
@section('content')
{{-- 完了メッセージ --}}
@if (session('message'))
<div class="alert alert-info alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">
×
</button>
{{ session('message') }}
</div>
@endif
{{-- 検索 --}}
<div class="container" th:fragment="search">
<form action="{{ route('admin.reserve.index') }}" method="get">
<div class="form-group form-inline input-group-sm">
<label for="id" class="col-md-2 control-label">予約番号</label>
<input type="text" class="form-control col-md-5" id="id" name="id" placeholder="予約番号" value="{{ @$search_keywords['id'] }}">
</div>
<div class="form-group form-inline input-group-sm">
<label for="user_name" class="col-md-2 control-label">ユーザー名</label>
<input type="text" class="form-control col-md-5" id="user_name" name="user_name" placeholder="ユーザー名" value="{{ @$search_keywords['user_name'] }}">
</div>
<div class="form-group form-inline input-group-sm">
<label for="schedule_id" class="col-md-2 control-label">スケジュールID</label>
<input type="text" class="form-control col-md-5" id="schedule_id" name="schedule_id" placeholder="スケジュールID" value="{{ @$search_keywords['schedule_id'] }}">
</div>
<div class="text-center">
<button class="btn btn-sm btn-outline-secondary" type="submit">検索</button>
</div>
</form>
<hr>
</div>
{{-- 新規登録画面へ --}}
<a class="btn btn-primary mb-2" href="{{ route('admin.reserve.create') }}" role="button">新規登録</a>
<div class="card">
<div class="card-body">
<table class="table table-bordered">
<thead>
<tr>
<th>予約番号</th>
<th>ユーザー名</th>
<th>スケジュールID</th>
<th style="width: 70px"></th>
</tr>
</thead>
<tbody>
@foreach ($reserves as $reserve)
<tr>
<td>{{ $reserve->id }}</td>
<td>{{ $reserve->user->name }}</td>
<td>{{ $reserve->schedule_id }}</td>
<td>
<a class="btn btn-primary btn-sm mb-2" href="{{ route('admin.reserve.edit', $reserve->id) }}"
role="button">編集</a>
<form action="{{ route('admin.reserve.destroy', $reserve->id) }}" method="post">
@csrf
@method('DELETE')
{{-- 簡易的に確認メッセージを表示 --}}
<button type="submit" class="btn btn-danger btn-sm"
onclick="return confirm('削除してもよろしいですか?');">
削除
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@stop
@extends('adminlte::page')
@section('title', '予約登録')
@section('content_header')
<h1>予約登録</h1>
@stop
@section('content')
@if ($errors->any())
<div class="alert alert-warning alert-dismissible">
{{-- エラーの表示 --}}
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
{{-- 編集画面 --}}
<div class="card">
<form action="{{ route('admin.reserve.store') }}" method="post">
@csrf
<div class="card-body">
<div class="form-group">
<label for="name">スケジュールID</label>
<select class="form-control" id="schedule_id" name="schedule_id">
@foreach ($schedules as $schedule)
<option value="{{ $schedule->id }}" @selected(old('schedule_id') == $schedule->id)>
{{ $schedule->from_date }} ~ {{ $schedule->due_date }}
</option>
@endforeach
</select>
</div>
<div class="form-group">
<label for="name">ユーザーID</label>
<select class="form-control" id="user_id" name="user_id">
@foreach ($users as $user)
<option value="{{ $user->id }}" @selected(old('user_id') == $user->id)>
{{ $user->name }}
</option>
@endforeach
</select>
</div>
</div>
<div class="card-footer">
<div class="row">
<a class="btn btn-default" href="{{ route('admin.reserve.index') }}" role="button">戻る</a>
<div class="ml-auto">
<button type="submit" class="btn btn-primary">登録</button>
</div>
</div>
</div>
</form>
</div>
@stop
@extends('adminlte::page')
@section('title', 'スケジュール編集')
@section('content_header')
<h1>スケジュール編集</h1>
@stop
@section('content')
@if ($errors->any())
<div class="alert alert-warning alert-dismissible">
{{-- エラーの表示 --}}
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
{{-- 編集画面 --}}
<div class="card">
<form action="{{ route('admin.reserve.update', $reserve->id) }}" method="post">
@csrf @method('PUT')
<div class="card-body">
<div class="form-group">
<label for="name">スケジュールID</label>
<input type="text" class="form-control" id="schedule_id" name="schedule_id"
value="{{ old('schedule_id', $reserve->schedule_id) }}" placeholder="スケジュールID" />
</div>
<div class="form-group">
<label for="name">ユーザーID</label>
<input type="text" class="form-control" id="user_id" name="user_id"
value="{{ old('user_id', $reserve->user_id) }}" placeholder="ユーザーID" />
</div>
</div>
<div class="card-footer">
<div class="row">
<a class="btn btn-default" href="{{ route('admin.reserve.index') }}" role="button">戻る</a>
<div class="ml-auto">
<button type="submit" class="btn btn-primary">編集</button>
</div>
</div>
</div>
</form>
</div>
@stop
⑧コントローラーに表示処理を実装する。
※下記、参照し上書きする。
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\StorereserveRequest;
use App\Http\Requests\UpdatereserveRequest;
use App\Models\Reserve;
use App\Models\Schedule;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class ReserveController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
// 検索処理の準備
$query = Reserve::query();
$search_keywords = array();
// リクエストの検索キーに値がセットされていたらSQL抽出条件として追加する
if ($request->filled('id')) {
$query->where('id', '=', $request->input('id'));
$search_keywords['id'] = $request->input('id');
}
if ($request->filled('user_name')) {
// リレーション先のテーブルから検索する
$query->whereHas('user', function ($query) use ($request) {
$query->where('name', 'LIKE', "%{$request->input('user_name')}%");
});
$search_keywords['user_name'] = $request->input('user_name');
}
if ($request->filled('schedule_id')) {
$query->where('schedule_id', '=', $request->input('schedule_id'));
$search_keywords['schedule_id'] = $request->input('schedule_id');
}
// 設定した抽出条件+ソートをしてデータを取得する
$reserves = $query->orderBy('id', 'asc')->get();
// 動作確認用デバッグログ
Log::debug($query->toSql());
return view('admin.reserve.index', compact('reserves', 'search_keywords'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\View\View
*/
public function create()
{
$schedules = Schedule::orderBy('from_date')->get();
$users = User::orderBy('id')->get();
return view('admin.reserve.create', compact('schedules', 'users'));
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StorereserveRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StorereserveRequest $request)
{
$reserve = new Reserve();
$reserve->fill($request->all())->save();
return redirect()->route('admin.reserve.index')->with('message', 'add reserve!');
}
/**
* Display the specified resource.
*
* @param \App\Models\Reserve $reserve
* @return \Illuminate\View\View
*/
public function show(reserve $reserve)
{
return view('admin.reserve.show', compact('reserve'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Reserve $reserve
* @return \Illuminate\View\View
*/
public function edit(reserve $reserve)
{
return view('admin.reserve.edit', compact('reserve'));
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdatereserveRequest $request
* @param \App\Models\Reserve $reserve
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdatereserveRequest $request, reserve $reserve)
{
$reserve->update($request->all());
return redirect()->route('admin.reserve.index')->with('message', 'update reserve!');
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Reserve $reserve
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(reserve $reserve)
{
$reserve->delete();
return redirect()->route('admin.reserve.index')->with('message', 'delete reserve!');
}
}
⑨新規作成と編集時のリクエスト、バリデーションは以下のファイルで定義する。
※下記、L17~L18、L29~L31を参照
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StorereserveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
- return false;
+ return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
+ 'schedule_id' => ['required', 'exists:schedules,id' ],
+ 'user_id' => ['required', 'exists:users,id',
+ Rule::unique('reserves', 'user_id')->where('schedule_id', $this->input('schedule_id')) ],
];
}
}※下記、L16~L17、L28~L29を参照
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdatereserveRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
- return false;
+ return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
+ 'schedule_id' => ['required', 'exists:schedules,id' ],
+ 'user_id' => ['required', 'exists:users,id' ],
];
}
}動作確認
・管理画面にて/admin/reserve にアクセスし、一覧画面が表示されるかを確認する。
・新規登録ボタンからスケジュールの予約が登録できるかを確認する。
・編集ボタンでスケジュールの予約が更新ができるかを確認する。
