みなさん、こんにちは。
見切り発車で開発記録を載せていますが、part20超えそうな気がしてきます
今までの開発記録はこちらへ
胡蝶蘭を捨てるくらいならワイが欲しいので、サービス開発する編
公式ドキュメントの言う通り、パッケージをインストールされたら、Inertia.jsが導入されて???になった編
マルチログインを作ってみた編
管理者機能を作ります!
現在の開発状況
今回の予定
- ユーザー新規登録
- ユーザー編集
- ユーザー削除
- 退会ユーザーの処理
ルート設定
必要なのはトップ画面のindex,ユーザー作成のcreate,作成処理のstore,ユーザー編集のedit,編集処理のupdate,ユーザー削除のdeleteが必要ということだけど
全部をルートやコントローラーに書くのは面倒くさい!!
そんな悩みを解決するのが
リソースコントローラー!
詳しくはこちらへ
リソースコントローラー
これがあれば、C(作成)R(読み取り)U(更新)D(削除)のルートの設定やコントローラーの関数の利用をかってにやってくれるのですごく便利です!
使い方は通常のコントローラー作成コマンドに--resource とつけるだけです。
というわけで、ぽちっとな
php artisan make:controller AdminController --resource
そうなると
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AdminController extends Controller
{
public function index()
{
//
}
public function create()
{
//
}
public function store(Request $request)
{
//
}
public function show($id)
{
//
}
public function edit($id)
{
//
}
public function update(Request $request, $id)
{
//
}
public function destroy($id)
{
//
}
}
やったぜ。
ルート設定もとても簡単で
Route::resource('users', AdminController::class)
->middleware('auth:admin')->except(['show']);
これだけ
showメゾットは今回は使わないため、取り除いています。
コントローラー作成
では、さっき作ってもらったコントローラーに処理を書いていきます
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Admin;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules\Password;
class AdminController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
$users = User::select('id', 'name', 'email', 'created_at')->paginate(5);
return view('admin.users.index', compact('users'));
}
public function create()
{
return view('admin.users.create');
}
public function store(Request $request)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', 'string', Password::defaults()],
]);
User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
return redirect()->route('admin.users.index')
}
public function show($id)
{
}
public function edit($id)
{
$user = User::findOrFail($id);
return view('admin.users.edit', compact('user'));
}
public function update(Request $request, $id)
{
$request->validate([
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'confirmed', 'string', Password::defaults()],
]);
$user = User::findOrFail($id);
$user->name = $request->name;
$user->email = $request->email;
$user->password = Hash::make($request->password);
$user->save();
return redirect()->route('admin.users.index')
}
public function destroy($id)
{
User::findOrFail($id)->delete();
return redirect()->route('admin.users.index')
}
}
最初に、 ** $this->middleware('auth') で認証している状態で使うことを設定する
データベースから変数をviewファイルに送りたいときはcompact関数**を使えばいいよ!
viewの設定
3つのファイルを一からデザインなんてやってられるか!!
tailblocks「力が欲しいか?」
tailblocksにはtailwindで利用できるデザインのひな型があるのだ!
indexのトップにはこのひな形を改造して使います
view codeをぽちっとすると
感動
あとはindex.blade.phpのファイルを作成し張り付けるだけ!
ということでコントローラーから得た情報を表示するために、さくっと改造します
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
ユーザー管理
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="md:p-6 bg-white border-b border-gray-200">
<div class="container md:px-5 mx-auto">
<div class="flex justify-between w-full mb-4">
<h1 class="text-4xl font-medium title-font text-gray-900 ">ユーザー管理</h1>
<div class="p-2 w-1/3">
<button onclick="location.href='{{ route('admin.users.create') }}'"
class=" bg-indigo-500 border-0 sm:px-8 focus:outline-none hover:bg-indigo-600 rounded text-lg py-2 text-white">新規登録する</button>
</div>
</div>
<div class="lg:w-3/4 w-full mx-auto overflow-auto">
<table class="table-auto text-left whitespace-no-wrap border solid">
<thead>
<tr>
<th
class="md:px-4 py-3 w-1/6 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100">
ユーザー名</th>
<th
class=" md:px-4 py-3 w-1/3 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100 rounded-tl rounded-bl">
メールアドレス</th>
<th
class="md:px-4 py-3 w-1/6 title-font tracking-wider font-medium text-gray-900 text-sm bg-gray-100">
前回のログイン</th>
<th
class="w-10 title-font w-1/3 tracking-wider font-medium text-gray-900 text-sm bg-gray-100 rounded-tr rounded-br">
</th>
</tr>
</thead>
<tbody>
@foreach ($users as $user)
<tr class="border-2">
<td class="md:px-4 py-3 break-all">{{ $user->name }}</td>
<td class="md:px-4 py-3 break-all">{{ $user->email }}</td>
<td class="md:px-4 py-3">
@if ($user->created_at->diffInDays(now()) >= 30)
{{ $user->created_at->subDays(30)->toDateString() }}
@else
{{ $user->created_at->diffForHumans() }}
@endif
</td>
<td class=" text-center md:px-4 py-3 md:flex md:flex-row">
<button type="button"
onclick="location.href='{{ route('admin.users.edit', ['user' => $user->id]) }}'"
class="mx-auto text-white bg-indigo-500 border-0 py-2 px-4 focus:outline-none hover:bg-gray-600 rounded text-sm mb-2 md:mb-0">編集</button>
<form id="delete_{{ $user->id }}" method="post"
action="{{ route('admin.users.destroy', ['user' => $user->id]) }}">
@csrf
@method('delete')
<a href="#" data-id="{{ $user->id }}" onclick="deletePost(this)"
class="inline-block mx-auto text-white bg-red-500 border-0 py-2 px-4 focus:outline-none hover:bg-red-600 rounded text-sm">
削除</a>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
{{ $users->links() }}
</div>
</section>
</div>
</div>
</div>
</div>
<script src="{{ mix('/js/app.js') }}"></script>
<script>
'use strict';
function deletePost(e) {
if (confirm('本当に削除してもよろしいですか?')) {
document.getElementById('delete_' + e.dataset.id).submit();
}
}
</script>
</x-app-layout>
プレビュー
@foreachを利用して、データベースからとった情報を吐き出しています。
日時管理について
よく、ソーシャルゲームだと最新のログイン日とかありますよね
phpにはcarbonというライブラリがあり、これを利用して日付の表現ができます。
carbonのインストール
composer require nesbot/carbon
詳しくはこちらへ
PHPで日付時刻処理を書くならCarbonを使うべき
今回はログインから何日前かを表示したいので
diffForHumans()
を使用します。
ただし、一つ問題点が・・・
前回のログイン日が30日前までならどの日かイメージしやすいのですが、例えば334日前ならイメージできるのか?
30日以上の日付の計算ができない男
というわけで、30日以上なら、前回のログイン日を指定するように分岐させます
@if ($user->created_at->diffInDays(now()) >= 30)
{{ $user->created_at->subDays(30)->toDateString() }} //〇年〇月〇日と表示
@else
{{ $user->created_at->diffForHumans() }} //〇〇前と表示
@endif
create,editのview
ひな形はtailblocksから拝借
フォームの内容
<form action="{{ route('admin.users.update', ['user' => $user->id]) }}" method="post">
@method('PUT')
@csrf
<div class="flex flex-col items-center m-2">
<div class="p-2 mx-auto">
<div class="relative">
<label for="name" class="leading-7 text-sm text-gray-600">ユーザー名</label>
<input type="text" id="name" name="name"
class="w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-indigo-500 focus:bg-white focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
value="{{ $user->name }}" required>
</div>
</div>
<div class="p-2 mx-auto">
<div class="relative">
<label for="email"
class="leading-7 text-sm text-gray-600">メールアドレス</label>
<input type="email" id="email" name="email"
class="w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-indigo-500 focus:bg-white focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
value="{{ $user->email }}" required>
</div>
</div>
<div class="p-2 mx-auto">
<div class="relative">
<label for="password"
class="leading-7 text-sm text-gray-600">パスワード</label>
<input type="password" id="password" name="password"
class="w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-indigo-500 focus:bg-white focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
required>
</div>
</div>
<div class="p-2 mx-auto">
<div class="relative">
<label for="password_confirmation"
class="leading-7 text-sm text-gray-600">パスワード確認</label>
<input type="password" id="password_confirmation"
name="password_confirmation"
class="w-full bg-gray-100 bg-opacity-50 rounded border border-gray-300 focus:border-indigo-500 focus:bg-white focus:ring-2 focus:ring-indigo-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
required>
</div>
</div>
</div>
<div class="flex flex-col md:flex-row justify-around mt-4">
<button type="button"
onclick="location.href='{{ route('admin.users.index') }}'"
class="mx-auto text-white bg-gray-500 border-0 py-2 px-12 focus:outline-none hover:bg-gray-600 rounded text-lg mx-4 mb-4 md:mb-0">戻る</button>
<button type="submit"
class=" mx-auto text-white bg-indigo-500 border-0 py-2 px-12 focus:outline-none hover:bg-indigo-600 rounded text-lg mx-4">更新</button>
</div>
</form>
特に重要なのが**method('PUT')の部分
便利なリソースコントローラーなんだけれど、
フォームはPUT/PATCH,DELETEメゾットをサポートしていない弱点がある。
そのために、フォームの下にmethod('PUT')**などを追記する必要がある。
退会ユーザー管理機能
登録もする人もいれば、退会する人もいる。登録した人は管理するのは当然だが、退会した人を放っておいていいわけではない。
退会が、単なるユーザーの誤爆だったりすることもあるし、退会したユーザーがトラブルを起こしていたということもある。
もしかすると、警察などから情報提供を依頼されることもあるので、退会者の情報を保持することに越したことはないのである。
準備
softdeleteを使う
$table->softDeletes(); //追加
この状態でマイグレーションするとテーブルにdeleted_atが追加される。
このカラムはユーザーが退会してもユーザーをテーブルから削除せず、退会した時間をderete_atにスタンプする
ユーザー側から見ると削除されているように見えるが、データベースには残っていることである(論理削除)
一方で、ユーザーをテーブルから完全に抹消する方法もある(物理削除)
物理削除を使う機会はないが、コードを書く
まずはモデルファイル
use Illuminate\Database\Eloquent\SoftDeletes; //追加
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, SoftDeletes; //追加
ルート
Route::prefix('expired-users')
->middleware('auth:admin')->group(function () {
Route::get('index', [AdminController::class, 'expiredUserIndex'])->name('expired-users.index');
Route::post('destroy/{user}', [AdminController::class, 'expiredUserDestroy'])->name('expired-users.destroy');
});
コントローラーはAdminControllerを使いまわし
public function expiredUserIndex()
{
$expireUsers = User::onlyTrashed()->get();
return view('admin.expired-users', compact('expireUsers'));
}
public function expiredUserDestroy($id)
{
User::onlyTrashed()->findOrFail($id)->forceDelete();
return redirect()->route('admin.expired-users.index')
}
ポイントは2つだけ
onlyTrashed()で退会したユーザーの情報を入手し、
forceDelete()で物理削除をするということ
viewはユーザー一覧のindex.phpを使いまわして、少しいじればOK
ここまでを終えて
物理削除のコマンドは正直いるか迷った
管理者による誤爆や、隠蔽もできそうだし
どの管理者が退会ユーザーを処理したのか、実装する必要があるかもしれない
これで一旦、管理者機能は実装できたので、次回はユーザーの機能を作っていきたい。