今回は、予約システムの主データとなるスケジュールの管理機能の作成方法を紹介します。
スケジュール機能作成
①スケジュールのモデル、マイグレーションファイル、コントローラー一式を作成する。
$sail artisan make:model Schedule --controller --resource --requests --migration※下記のように表示がされていれば成功
INFO Model [app/Models/Schedule.php] created successfully.
INFO Migration [database/migrations/yyyy_mm_dd_xxxxxx_create_schedules_table.php] created successfully.
INFO Request [app/Http/Requests/StoreScheduleRequest.php] created successfully.
INFO Request [app/Http/Requests/UpdateScheduleRequest.php] created successfully.
INFO Controller [app/Http/Controllers/ScheduleController.php] created successfully. ②コントローラーのパスを修正する。
app/Http/Controllers/ScheduleController.php を app/Http/Controllers/Admin/に移動し、ファイル内のnamespaceを修正します。また、継承クラスのパス解決のために、use use App\Http\Controllers\Controller; の追加も行います。
※下記、L3~L4を参照
- namespace App\Http\Controllers\Auth;
+ namespace App\Http\Controllers\Admin\Auth;③スケジュールテーブルを作成する。
生成されたマイグレーションファイルを開き、スケジュールテーブルのカラムを作成します。
※下記、L16~L20を参照
public function up()
{
Schema::create('schedules', function (Blueprint $table) {
$table->id();
+ $table->string('title', 255);
+ $table->datetime('from_date');
+ $table->datetime('due_date');
+ $table->integer('reservation_limit')->nullable();
+ $table->string('staff_name')->nullable();
$table->timestamps();
});
}④マイグレーションを実施する。
sail artisan migrate⑤ルーティングを追加する。
routes/admin.php に スケジュール管理機能用のルーティングを追加します。
※下記、L44を参照
Route::middleware('auth:admin')->group(function () {
// 中略
Route::get('/dashboard', function () {
return view('dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');
+ Route::resource('schedule', ScheduleController::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\ScheduleController 管理者ユーザーの認証がされている場合にアクセス可とし、/admin/schedule にアクセスした場合にScheduleContollerの各処理を呼び出すように設定します。Route::resourceは自動でテーブルに帯する各処理のルーティングを生成します。
⑦表示用の画面ファイルを作成する。
resources/views/admin/schedule 配下に スケジュール一覧画面、新規登録画面、編集画面の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.schedule.index') }}" method="get">
<div class="form-group form-inline input-group-sm">
<label for="title" class="col-md-2 control-label">タイトル</label>
<input type="text" class="form-control col-md-5" id="title" name="title" placeholder="タイトル" value="{{ @$search_keywords['title'] }}">
</div>
<div class="form-group form-inline input-group-sm">
<label for="from_date" class="col-md-2 control-label">実施日</label>
<input type="date" class="form-control col-md-3" id="from_date" name="from_date" placeholder="From" value="{{ @$search_keywords['from_date'] }}">
<label class="col-md-1 control-label">~</label>
<input type="date" class="form-control col-md-3" id="due_date" name="due_date" placeholder="To" value="{{ @$search_keywords['due_date'] }}">
<div class="col-md-3"></div>
</div>
<div class="form-group form-inline input-group-sm">
<label for="reservation_limit_from" class="col-md-2 control-label">予約可能数</label>
<input type="number" class="form-control col-md-2" id="reservation_limit_from" name="reservation_limit_from" placeholder="下限" value="{{ @$search_keywords['reservation_limit_from'] }}">
<label class="col-md-1 control-label">~</label>
<input type="number" class="form-control col-md-2" id="reservation_limit_to" name="reservation_limit_to" placeholder="上限" value="{{ @$search_keywords['reservation_limit_to'] }}">
</div>
<div class="form-group form-inline input-group-sm">
<label for="staff_name" class="col-md-2 control-label">担当者</label>
<input type="text" class="form-control col-md-2" id="staff_name" name="staff_name" placeholder="スタッフ名" value="{{ @$search_keywords['staff_name'] }}">
</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.schedule.create') }}" role="button">新規登録</a>
<div class="card">
<div class="card-body">
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>タイトル</th>
<th>開始日時</th>
<th>終了日時</th>
<th>予約可能数</th>
<th>スタッフ</th>
<th style="width: 70px"></th>
</tr>
</thead>
<tbody>
@foreach ($schedules as $schedule)
<tr>
<td>{{ $schedule->id }}</td>
<td>{{ $schedule->title }}</td>
<td>{{ $schedule->from_date }}</td>
<td>{{ $schedule->due_date }}</td>
<td>{{ $schedule->reservation_limit }}</td>
<td>{{ $schedule->staff_name }}</td>
<td>
<a class="btn btn-primary btn-sm mb-2" href="{{ route('admin.schedule.edit', $schedule->id) }}"
role="button">編集</a>
<form action="{{ route('admin.schedule.destroy', $schedule->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.schedule.store') }}" method="post">
@csrf
<div class="card-body">
<div class="form-group">
<label for="name">タイトル</label>
<input type="text" class="form-control" id="title" name="title"
value="{{ old('title') }}" placeholder="スケジュールタイトル" />
</div>
<div class="form-group">
<label for="name">開始日時</label>
<input type="datetime-local" class="form-control" id="from_date" name="from_date"
value="{{ old('from_date') }}" placeholder="2023/03/10 9:00" />
</div>
<div class="form-group">
<label for="name">終了日時</label>
<input type="datetime-local" class="form-control" id="due_date" name="due_date"
value="{{ old('due_date') }}" placeholder="2023/03/10 10:00" />
</div>
<div class="form-group">
<label for="name">予約可能数</label>
<input type="number" class="form-control" id="reservation_limit" name="reservation_limit"
value="{{ old('reservation_limit') }}" placeholder="予約可能数(1~)" />
</div>
<div class="form-group">
<label for="price">担当者名</label>
<input type="text" class="form-control" id="staff_name" name="staff_name"
value="{{ old('staff_name') }}" placeholder="スタッフ名" />
</div>
</div>
<div class="card-footer">
<div class="row">
<a class="btn btn-default" href="{{ route('admin.schedule.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.schedule.update', $schedule->id) }}" method="post">
@csrf @method('PUT')
<div class="card-body">
<div class="form-group">
<label for="name">タイトル</label>
<input type="text" class="form-control" id="title" name="title"
value="{{ old('title', $schedule->title) }}" placeholder="スケジュールタイトル" />
</div>
<div class="form-group">
<label for="name">開始日時</label>
<input type="datetime-local" class="form-control" id="from_date" name="from_date"
value="{{ old('from_date', $schedule->from_date) }}" placeholder="2023/03/10 9:00" />
</div>
<div class="form-group">
<label for="name">終了日時</label>
<input type="datetime-local" class="form-control" id="due_date" name="due_date"
value="{{ old('due_date', $schedule->due_date) }}" placeholder="2023/03/10 10:00" />
</div>
<div class="form-group">
<label for="name">予約可能数</label>
<input type="number" class="form-control" id="reservation_limit" name="reservation_limit"
value="{{ old('reservation_limit', $schedule->reservation_limit) }}" placeholder="予約可能数(1~)" />
</div>
<div class="form-group">
<label for="price">担当者名</label>
<input type="text" class="form-control" id="staff_name" name="staff_name"
value="{{ old('staff_name', $schedule->staff_name) }}" placeholder="スタッフ名" />
</div>
</div>
<div class="card-footer">
<div class="row">
<a class="btn btn-default" href="{{ route('admin.schedule.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\StoreScheduleRequest;
use App\Http\Requests\UpdateScheduleRequest;
use App\Models\Schedule;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class ScheduleController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
// 検索処理の準備
$query = Schedule::query();
$search_keywords = array();
// リクエストの検索キーに値がセットされていたらSQL抽出条件として追加する
if ($request->filled('title')) {
$query->where('title', 'LIKE', "%{$request->input('title')}%");
$search_keywords['title'] = $request->input('title');
}
if ($request->filled('from_date')) {
$query->where('from_date', '>=', $request->input('from_date'));
$search_keywords['from_date'] = $request->input('from_date');
}
if ($request->filled('due_date')) {
$query->where('due_date', '<=', $request->input('due_date'));
$search_keywords['due_date'] = $request->input('due_date');
}
if ($request->filled('reservation_limit_from')) {
$query->where('reservation_limit', '>=', $request->input('reservation_limit_from'));
$search_keywords['reservation_limit_from'] = $request->input('reservation_limit_from');
}
if ($request->filled('reservation_limit_to')) {
$query->where('reservation_limit', '<=', $request->input('reservation_limit_to'));
$search_keywords['reservation_limit_to'] = $request->input('reservation_limit_to');
}
if ($request->filled('staff_name')) {
$query->where('staff_name', 'LIKE', "%{$request->input('staff_name')}%");
$search_keywords['staff_name'] = $request->input('staff_name');
}
// 設定した抽出条件+ソートをしてデータを取得する
$schedules = $query->orderBy('from_date', 'asc')->get();
// 動作確認用デバッグログ
Log::debug($query->toSql());
return view('admin.schedule.index', compact('schedules', 'search_keywords'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\View\View
*/
public function create()
{
return view('admin.schedule.create');
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreScheduleRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreScheduleRequest $request)
{
$schedule = new Schedule();
$schedule->fill($request->all())->save();
return redirect()->route('admin.schedule.index')->with('message', 'add schedule!');
}
/**
* Display the specified resource.
*
* @param \App\Models\Schedule $schedule
* @return \Illuminate\View\View
*/
public function show(Schedule $schedule)
{
return view('admin.schedule.show', compact('schedule'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Schedule $schedule
* @return \Illuminate\View\View
*/
public function edit(Schedule $schedule)
{
return view('admin.schedule.edit', compact('schedule'));
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateScheduleRequest $request
* @param \App\Models\Schedule $schedule
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateScheduleRequest $request, Schedule $schedule)
{
$schedule->update($request->all());
return redirect()->route('admin.schedule.index')->with('message', 'update schedule!');
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Schedule $schedule
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Schedule $schedule)
{
$schedule->delete();
return redirect()->route('admin.schedule.index')->with('message', 'delete schedule!');
}
}⑨新規作成と編集時のリクエスト、バリデーションは以下のファイルで定義する。
※下記、L27~L31を参照
public function rules()
{
return [
+ 'title' => ['required', 'max:255'],
+ 'from_date' => ['required', 'date', 'after:2000-01-01' ],
+ 'due_date' => ['required', 'date', 'after:from_date' ],
+ 'reservation_limit' => ['numeric', 'max:9999'],
+ 'staff_name' => ['string', 'max:255'],
];
}※下記、L27~L31を参照
public function rules()
{
return [
+ 'title' => ['required', 'max:255'],
+ 'from_date' => ['required', 'date', 'after:2000-01-01' ],
+ 'due_date' => ['required', 'date', 'after:from_date' ],
+ 'reservation_limit' => ['numeric', 'max:9999'],
+ 'staff_name' => ['string', 'max:255'],
];
}動作確認
・管理画面にて/admin/schedule にアクセスし、一覧画面が表示されるか確認する。
・新規登録ボタンからスケジュールが登録できるか確認する。
・編集ボタンでスケジュールの更新ができるか確認する。
