Laravel10.x のインストールからBreezeを使ったマルチ認証まで
最新のLaravelをインストールして、最低限やること、入れるコンポーネントとかを備忘録しようと思う。バージョンごとに変わったりするので、記録は大事。
基本は、laravelドキュメントを参考にしています。
環境はWindows11, Xampp, PHP8.1.6, MariaDB10.4.24, Visual Studio Code(vscode)
インストール
まずLaravelプロジェクトを作成します。最新のLaravelをDLする前提です。
Xamppのコントロールパネルで、Shellボタンを押してShellを立ち上げ、次のコードを入力します。
project-name には、自身のプロジェクト名を入力します。
composer create-project laravel/laravel project-name
インストールが終わったらvscodeで該当プロジェクトを開き、新規ターミナルを立ち上げます。
Laravelの初期設定
サイトURL・メール・データベース等の設定と、サーバー時間の修正・日本語で使うための設定が必要です。
ENVファイルの修正
まずENVファイルの修正から行います。
APP_NAMEにはサイト名を。日本語や、スペースのある文字を入れるときは""ダブルクォーテーションで囲んで入力します。
APP_ENVはlocalでローカル開発用、productionで本番用となります。
APP_URLはサイトのトップURL。
DB_と書かれたところには、使用するDBの設定を入力します。
MAIL_と書かれたところには、使用するメーラー情報を入力します。
app.phpの修正
次にconfigフォルダにあるapp.phpの設定です。サーバーの時間と言語を設定します。
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',
'faker_locale' => 'ja_JP',
言語ファイル
上記の設定が終わったら、言語ファイルの作成とストレージLinkの設定をします。
このコマンドでアプリケーションにlangディレクトリを作成します。
デフォルトはenで作成されますが、コピーしてjaにリネームします。
php artisan lang:publish
langファイル直下にja.jsonファイルも置くこと。
下記のjson.jsonをja.jsonにリネームして使用します。ありがたや。
https://github.com/Laravel-Lang/lang/tree/main/locales/ja
jaファイル
ファイルストレージ
laravelは、デフォルトでファイルをstorage/app/publicに保存します。そのフォルダにWebからアクセスできるようにするため、ストレージのシンボリックリンクを作成します。
これで、publicフォルダの中にstorageフォルダへのショートカットが作成されます。
php artisan storage:link
最後に初期データベースのマイグレーションをして、サーバーが立ち上がるか確認します。
php artisan migrate
php artisan serve
Breezeのインストール
laravel8.xから、認証機能にBreezeとJetstreamというスターターキットが導入されました。
Jetsreamにはteamサポートがあり、それを使用してSNSのような招待制やWordPressのユーザーグループのようなユーザー分けが可能です。
一方、Breezeは最小限の認証機能だけで、teamサポートのような機能は自分で作成する必要があります。ただ、Breezeのほうが、Jetstreamよりはるかにカスタマイズしやすく感じたので、こちらを使用してマルチ認証を作成したいと思います。
今回はUser+Adminの形で増やします。
もっと簡単な実装方法もあるのですが、Roleなどをふやせるような実装方法のほうが、あとあと便利なのでBreezeでいきます。
ログイン、ユーザー登録、パスワードリセット、メール確認、パスワード確認が標準でついています。
Breeze本体のインストール
ターミナルに下記を入力します。
composer require laravel/breeze --dev
終ったら次にこれを入力します。
php artisan breeze:install
エンターを押すと、色々質問されます。自分のお好みで選択してください。
私はBlade, dark mode support, PHPunitの選択をしました。
終わると次を入力します。
新しいデータベーステーブルのマイグレーションを行います。
php artisan migrate
npm install
npm run dev
これでViteが起動します。
別のターミナルを立ち上げてサーバーを起動してください。
php artisan serve
アドレスバーにhttp://localhost:8000 と入力するとLaravelのwelcome画面が表示されます。
右上にLoginとRegisterの表示が出ていれば成功です。
これでBreezeのインストールは終わりです。あとは修正を行います。
Adminの作成
現在Breezeは、userの登録・削除・編集ができるようになっています。このUserのシステムを利用して、Adminも使えるようにしたいと思います。
Model作成
Adminモデルの作成時に、-mコマンドをつけることで、同名のデータベーステーブルも作成できます。
php artisan make:model Admin -m
これでModelsフォルダにAdmin.phpができたので、User.phpの中身を丸ごとコピーし書き換えます。
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class Admin extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'login_name',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
Adminテーブル作成
databaseフォルダの中にmigrationsフォルダがあります。
その中にcreate_admins_tableと書かれたphpファイルがあるので、それを編集します。
create_users_tableの中身をコピーして適時、書き換えます。
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('admins', function (Blueprint $table) {
$table->id();
$table->string('name')->nullable();
$table->string('email')->nullable();
$table->string('login_name');
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('admins');
}
};
パスワード変更用テーブル作成
パスワードの変更もできるようにターミナルでテーブルを作成します。
php artisan make:migration create_admin_password_reset_tokens_table
これもUser用のpassword_reset_tokensテーブルの中身をコピーして貼り付けます。
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('admin_password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('admin_password_reset_tokens');
}
};
Migration
全て終わったら、ターミナルでマイグレーションします。
php artisan migrate
ルートの設定
現在、userのルートは設定されていますが、admin用のルートは無いので、独自に設定します。
Route作成
routes/web.phpをコピーしてadmin.phpにリネームします。
auth.phpの中身をコピーして、admin.phpに貼り付け、適時修正します。
use文のPathは、Authの後にAdminをいれ、Routeのmiddlewareにはauth:adminsを記入します。
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Auth\Admin\AuthenticatedSessionController;
use App\Http\Controllers\Auth\Admin\ConfirmablePasswordController;
use App\Http\Controllers\Auth\Admin\EmailVerificationNotificationController;
use App\Http\Controllers\Auth\Admin\EmailVerificationPromptController;
use App\Http\Controllers\Auth\Admin\NewPasswordController;
use App\Http\Controllers\Auth\Admin\PasswordController;
use App\Http\Controllers\Auth\Admin\PasswordResetLinkController;
use App\Http\Controllers\Auth\Admin\RegisteredUserController;
use App\Http\Controllers\Auth\Admin\VerifyEmailController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('/', function () {
return view('backend.welcome');
});
Route::middleware('guest')->group(function () {
Route::get('register', [RegisteredUserController::class, 'create'])
->name('register');
Route::post('register', [RegisteredUserController::class, 'store']);
Route::get('login', [AuthenticatedSessionController::class, 'create'])
->name('login');
Route::post('login', [AuthenticatedSessionController::class, 'store']);
Route::get('forgot-password', [PasswordResetLinkController::class, 'create'])
->name('password.request');
Route::post('forgot-password', [PasswordResetLinkController::class, 'store'])
->name('password.email');
Route::get('reset-password/{token}', [NewPasswordController::class, 'create'])
->name('password.reset');
Route::post('reset-password', [NewPasswordController::class, 'store'])
->name('password.store');
});
Route::middleware('auth:admins')->group(function () {
Route::get('/dashboard', function(){
return view('admin.dashboard');
})->middleware('verified')->name('dashboard');
Route::get('verify-email', EmailVerificationPromptController::class)
->name('verification.notice');
Route::get('verify-email/{id}/{hash}', VerifyEmailController::class)
->middleware(['signed', 'throttle:6,1'])
->name('verification.verify');
Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store'])
->middleware('throttle:6,1')
->name('verification.send');
Route::get('confirm-password', [ConfirmablePasswordController::class, 'show'])
->name('password.confirm');
Route::post('confirm-password', [ConfirmablePasswordController::class, 'store']);
Route::put('password', [PasswordController::class, 'update'])->name('password.update');
Route::post('logout', [AuthenticatedSessionController::class, 'destroy'])
->name('logout');
});
ルートプロバイダ
appフォルダのProviderフォルダにRouteServiceProvider.phpがあります。
Adminにも対応できるように変更します。
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to your application's "home" route.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/dashboard';
public const ADMIN_HOME = '/admin/dashboard';//追加
/**
* Define your route model bindings, pattern filters, and other route configuration.
*/
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
//ここから追加
Route::prefix('admin')->as('admin.')
->middleware('web')
->namespace($this->namespace)
->group(base_path('routes/admin.php'));
//ここまで
Route::middleware('web')
->group(base_path('routes/web.php'));
});
}
}
Auth認証のガードの設定
Laravelの認証では、AuthファザードのGuardを参照して認証ユーザーを区別します。
middlewareではguard設定で指定されたものだけを認証します。
auth設定
configフォルダのauth.phpの中を参照していますので、そこに指定します。
return [
'defaults' => [
'guard' => 'users',
'passwords' => 'users',
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
'admins' => [
'provider' => 'admins',
'table' => 'admin_password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
],
'password_timeout' => 10800,
];
Middleware
appフォルダのHttpフォルダの中にMiddlewareがあります。
ここのファイルで指定された挙動を行うことになっています。
まず認証時の Authenticateファイルを修正します。
routeにadmin.の文字列があったらadmin.loginに、それ以外はloginに飛ばす処理になります。
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;//追加
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*/
protected function redirectTo(Request $request): ?string
{
//return $request->expectsJson() ? null : route('login');
if ($request->expectsJson()) {
return null;
} else {
if (Route::is('admin.*')) {
return route('admin.login');
} else {
return route('login');
}
}
}
}
次に認証後のAuthenticatedでどこにリダイレクトするかの処理になります。
RedirectIfAuthenticatedファイルを編集します。
guardがadminsでrouteにadmin.の文字列があれば、先ほど設定したADMIN_HOMEにリダイレクトする設定にします。
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next, string ...$guards): Response
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
if (($guard === 'admins') && $request->routeIs('admin.*')) {
return redirect(RouteServiceProvider::ADMIN_HOME);
}
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}
Requestにguard追加
appフォルダのHttpフォルダの中、Requestフォルダにログインの際の条件を記載したLoginRequestファイルがあります。
ここのauthenticate条件にGuardも追加します。これで、Adminはadmins、UserはusersのGuardが無いとログインできなくなります。
//前略
/**
* Attempt to authenticate the request's credentials.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function authenticate(): void
{
$this->ensureIsNotRateLimited();
//追加
if ($this->routeIs('admin.*')) {
$guard = 'admins';
} else {
$guard = 'users';
}
//guardを追加
if (! Auth::guard($guard)->attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => trans('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
【Admin用にカスタマイズしたいとき】
Adminは、ログイン名とパスワードでログインしたいというときは、LoginRequest.phpをコピーして、AdminLoginRequest.phpにリネームして、emailの部分をlogin_name等に書き換えます。
ログイン時に、Auth\Admin\AuthenticatedSessionController.phpを使用するので、その中のLoginRequestをAdminLoginRequestに書き換えればOKです。
あとは、Adminログイン用のViewファイルのフォームの内容を適時書き換えします。
Admin専用AuthController
現在、AuthはUserモデル専用のcontrollerになっています。Adminモデルにも同じようにcontrollerを作成します。
appフォルダのHttpフォルダにControllersがあり、その中にAuthフォルダがあります。
Authフォルダの中にAdminフォルダを作成して、Authの全ファイルをAdminフォルダにコピーしてください。
そしてすべてのファイルのnamespace・use文・viewファイルを適時書き換えます。
viewファイルは、admin.を追加でつけます。(resources/views/authの中にadminを作成予定)
guardはadmins。
下記は一例です。
namespace App\Http\Controllers\Auth\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginRequest;
use App\Providers\RouteServiceProvider;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
class AuthenticatedSessionController extends Controller
{
/**
* Display the login view.
*/
public function create(): View
{
return view('auth.admin.login');
}
/**
* Handle an incoming authentication request.
*/
public function store(LoginRequest $request): RedirectResponse
{
$request->authenticate();
$request->session()->regenerate();
return redirect()->intended(RouteServiceProvider::ADMIN_HOME);
}
/**
* Destroy an authenticated session.
*/
public function destroy(Request $request): RedirectResponse
{
Auth::guard('admins')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/admin/login');
}
}
RegisteredUserControllerの場合。use App\Models\User; になってるところはAdminにします。
namespace App\Http\Controllers\Auth\Admin;
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\AdminRole;
use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules;
use Illuminate\View\View;
use Symfony\Contracts\Service\Attribute\Required;
class RegisteredUserController extends Controller
{
/**
* Display the registration view.
*/
public function create(): View
{
$admin_roles = AdminRole::orderBy('role_name', 'asc')->get();
return view('auth.admin.register', compact('admin_roles'));
}
/**
* Handle an incoming registration request.
*
* @throws \Illuminate\Validation\ValidationException
*/
public function store(Request $request): RedirectResponse
{
//UserをAdminにして、項目を増やす
$request->validate([
'name' => ['string', 'max:190'],
'login_name' => ['required', 'string', 'max:190'],
'email' => ['required', 'string', 'lowercase', 'email', 'max:190', 'unique:'.Admin::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()],
]);
//UserをAdminにして、項目を増やす
$user = Admin::create([
'name' => $request->name,
'login_name' => $request->login_name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
event(new Registered($user));
Auth::login($user);
return redirect(RouteServiceProvider::ADMIN_HOME);
}
}
ダッシュボード表示等に使用するAdminControllerをターミナルで作成します。
php artisan make:controller Backend/AdminController
AdminControllerでindexファンクションを記載します。
namespace App\Http\Controllers\Backend;
use App\Http\Controllers\Controller;
use App\Models\Admin;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AdminController extends Controller
{
public function index() {
$admin = Admin::find(Auth::guard('admins')->id());
return view('backend.dashboard', compact('admin'));
}
}
このままroutes\admin.phpのdashboardのルートも修正します。
use App\Http\Controllers\Backend\AdminController;//追加
Route::middleware('auth:admins')->group(function () {
Route::controller(AdminController::class)->group(function () {
Route::get('/dashboard', 'index')->middleware('verified')->name('dashboard');
});
});
Admin専用のViewファイル作成
resourcesフォルダのviewsの中に、WEB表示用のBladeファイルが入っています。
AdminのViewのための共通レイアウトファイルを作成
viewsのlayoutsフォルダの中には、コンテンツを表示するための共通ファイルが入っています。
ログイン時にはapp.blade、未ログインにはguest.blade、そしてヘッダー用のメニューが記載されているnavigation.bladeです。
admin専用に、appとnavigationをコピーして、adminとadmin-navigationにリネームします。
@include('layouts.admin-navigation') //ここだけ直す
admin-navigationは全てのrouteにadmin.を追加します。profileのrouteは使わないので消してOK。
<nav x-data="{ open: false }" class="bg-white dark:bg-gray-800 border-b border-gray-100 dark:border-gray-700">
<!-- Primary Navigation Menu -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex">
<!-- Logo -->
<div class="shrink-0 flex items-center">
<a href="{{ route('admin.dashboard') }}">
<x-application-logo class="block h-9 w-auto fill-current text-gray-800 dark:text-gray-200" />
</a>
</div>
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
<x-nav-link :href="route('admin.dashboard')" :active="request()->routeIs('admin.dashboard')">
{{ __('Dashboard') }}
</x-nav-link>
</div>
</div>
<!-- Settings Dropdown -->
<div class="hidden sm:flex sm:items-center sm:ms-6">
<x-dropdown align="right" width="48">
<x-slot name="trigger">
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 focus:outline-none transition ease-in-out duration-150">
<div>{{ Auth::user()->name }}</div>
<div class="ms-1">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</div>
</button>
</x-slot>
<x-slot name="content">
<x-dropdown-link :href="">
{{ __('Profile') }}
</x-dropdown-link>
<!-- Authentication -->
<form method="POST" action="{{ route('admin.logout') }}">
@csrf
<x-dropdown-link :href="route('admin.logout')"
onclick="event.preventDefault();
this.closest('form').submit();">
{{ __('Log Out') }}
</x-dropdown-link>
</form>
</x-slot>
</x-dropdown>
</div>
<!-- Hamburger -->
<div class="-me-2 flex items-center sm:hidden">
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-900 focus:outline-none focus:bg-gray-100 dark:focus:bg-gray-900 focus:text-gray-500 dark:focus:text-gray-400 transition duration-150 ease-in-out">
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>
<!-- Responsive Navigation Menu -->
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
<div class="pt-2 pb-3 space-y-1">
<x-responsive-nav-link :href="route('admin.dashboard')" :active="request()->routeIs('admin.dashboard')">
{{ __('Dashboard') }}
</x-responsive-nav-link>
</div>
<!-- Responsive Settings Options -->
<div class="pt-4 pb-1 border-t border-gray-200 dark:border-gray-600">
<div class="px-4">
<div class="font-medium text-base text-gray-800 dark:text-gray-200">{{ Auth::user()->name }}</div>
<div class="font-medium text-sm text-gray-500">{{ Auth::user()->email }}</div>
</div>
<div class="mt-3 space-y-1">
<x-responsive-nav-link :href="">
{{ __('Profile') }}
</x-responsive-nav-link>
<!-- Authentication -->
<form method="POST" action="{{ route('admin.logout') }}">
@csrf
<x-responsive-nav-link :href="route('admin.logout')"
onclick="event.preventDefault();
this.closest('form').submit();">
{{ __('Log Out') }}
</x-responsive-nav-link>
</form>
</div>
</div>
</div>
</nav>
次に、appフォルダの中にViewフォルダがあります。その中のComponentsにAppLayout.phpとGuestLayout.phpがあるので、AppLayout.phpをコピーしてAdminLayout.phpにリネームし,
中を以下のように修正します。
namespace App\View\Components;
use Illuminate\View\Component;
use Illuminate\View\View;
class AdminLayout extends Component
{
/**
* Get the view / contents that represents the component.
*/
public function render(): View
{
return view('layouts.admin');
}
}
Auth認証のAdmin専用ファイルを作成
Auth認証で使用するファイルはauthフォルダの中にあります。
このauthフォルダの中にadminフォルダを作成し、authの中のファイルをすべてコピーします。
auth\admin\の中のファイルを全てAdmin用に修正します。
routeは、先頭にadmin.を付けます。登録に必要な項目も追加を忘れずにします。
以下は登録時のファイルの一例です。
<x-guest-layout>
<form method="POST" action="{{ route('admin.register') }}">
@csrf
<!-- Name -->
<div class="mt-4">
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
<x-input-error :messages="$errors->get('name')" class="mt-2" />
</div>
<!-- Email Address -->
<div class="mt-4">
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<!-- Login Name -->
<div class="mt-4">
<x-input-label for="login_name" :value="__('Login Name')" />
<x-text-input id="login_name" class="block mt-1 w-full" type="text" name="login_name" :value="old('login_name')" required autofocus
autocomplete="login_name" />
<x-input-error :messages="$errors->get('login_name')" class="mt-2" />
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<!-- Confirm Password -->
<div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
name="password_confirmation" required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
</div>
<div class="flex items-center justify-end mt-4">
<a class="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800" href="{{ route('login') }}">
{{ __('Already registered?') }}
</a>
<x-primary-button class="ms-4">
{{ __('Register') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
ログインが成功した時のダッシュボードView作成
先ほど、admin用のログイン時レイアウトファイルを作成しました。
これをダッシュボード表示用のviewに適用します。
viewsフォルダの中に、backendフォルダを作成し、viewsにあるdashboard.blade.phpとwelcome.blade.phpをbackendの中にコピーします。
dashboard.bladeはレイアウトファイル名をx-adminに変更します。
<x-admin-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Dashboard') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900 dark:text-gray-100">
{{ __("You're logged in!") }}
</div>
</div>
</div>
</div>
</x-admin-layout>
welcome.bladeはリンク先を変更します。
@if (Route::has('admin.login'))
<div class="sm:fixed sm:top-0 sm:right-0 p-6 text-right z-10">
@auth
<a href="{{ url('/admin/dashboard') }}" class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Dashboard</a>
@else
<a href="{{ route('admin.login') }}" class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Log in</a>
@if (Route::has('admin.register'))
<a href="{{ route('admin.register') }}" class="ml-4 font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Register</a>
@endif
@endauth
</div>
@endif
paginationの設定
データベースの件数によっては、ページを跨いでの表示になることもあります。
その際にpaginationを利用するのですが、あらかじめデザインパターンがLaravelに用意してあるので、それをターミナルからインストールしておきます。
php artisan vendor:publish --tag=laravel-pagination
Viewの表示でデフォルトに使用するデザインを指定できます。
appフォルダのProviderにある、AppServicePrividerで設定します。
use Illuminate\Pagination\Paginator;
public function boot(): void
{
Paginator::defaultView('default');
Paginator::defaultSimpleView('simple-default');
}
詳細はドキュメントにあります。
https://laravel.com/docs/10.x/pagination#customizing-the-pagination-view
Customizing The Pagination View
Intervention Image のインストール
コンテンツ制作に画像のアップロードは欠かせません。
そのままアップロードであるなら必要無いのですが、大抵の場合、一定のフォーマットを必要とすると思います。Intervention Imageをインストールすることで簡単に希望通りの画像のアップロードができるようになるのでおすすめです。現在V3です。
呼び出し方と書き方が変わったので注意。
composer require intervention/image
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Imagick\Driver;
// create new manager instance with desired driver
$manager = new ImageManager(new Driver());
https://image.intervention.io/v3/basics/instantiation
Instantiation
Discussion