Memo

メモ > 技術 > フレームワーク: Laravel6 > Laravelでプログラム作成

■Laravelでプログラム作成
■言語ファイルを配置
$ cd /var/www/main $ php -r "copy('https://readouble.com/laravel/6.x/ja/install-ja-lang-files.php', 'install-ja-lang.php');" $ php -f install-ja-lang.php $ php -r "unlink('install-ja-lang.php');"
validation.php言語ファイル 6.x Laravel https://readouble.com/laravel/6.x/ja/validation-php.html ■タイムゾーンとロケールを変更 config/app.php でタイムゾーンとロケールを変更
'timezone' => 'Asia/Tokyo', 'locale' => 'ja',
設定 6.x Laravel https://readouble.com/laravel/6.x/ja/configuration.html ■デフォルトのマイグレーションを調整 ※調整内容は一例。案件に合わせて調整する 不要なマイグレーションを削除 database/migrations/2019_08_19_000000_create_failed_jobs_table.php デフォルトのマイグレーションを調整 database/migrations/2014_10_12_000000_create_users_table.php
public function up() { Schema::create('users', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->bigIncrements('id'); $table->string('name')->comment('名前'); $table->string('email')->unique()->comment('メールアドレス'); $table->timestamp('email_verified_at')->nullable()->comment('メールアドレス確認日時'); $table->string('password')->comment('パスワード'); $table->rememberToken()->comment('トークン'); $table->timestamps(); }); DB::statement('ALTER TABLE users COMMENT \'ユーザ\''); }
database/migrations/2014_10_12_100000_create_password_resets_table.php
public function up() { Schema::create('password_resets', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->string('email')->index()->comment('メールアドレス'); $table->string('token')->comment('トークン'); $table->timestamp('created_at')->nullable(); }); DB::statement('ALTER TABLE password_resets COMMENT \'パスワードリセット\''); }
マイグレーションを実行
$ php artisan migrate
データベース:マイグレーション 6.x Laravel https://readouble.com/laravel/6.x/ja/migrations.html ■認証機能を作成 ユーザ登録と認証機能を作成する ※「php artisan make:auth」はLaravel6では使えない ※「composer require laravel/ui --dev」もLaravel7が出たからかLaravel6ではエラーになるようになった
$ composer require laravel/ui 1.* $ php artisan ui vue --auth
画面の右上に「LOGIN」「REGISTER」が表示されていて、ここからログインと登録ができる この時点ではCSSとJavaScriptがコンパイルされていないので、見た目は質素なものになるが動作はする 認証処理の流れはLaravel5から大きく変わっていないようなので、「Laravel.txt」の「認証」にある処理の流れを参考にする 認証 6.x Laravel https://readouble.com/laravel/6.x/ja/authentication.html 更新! Laravel6/7「make:Auth」が無くなった 〜Laravel6/7でのLogin機能の実装方法〜MyMemo - Qiita https://qiita.com/daisu_yamazaki/items/a914a16ca1640334d7a5 Laravel6 ログイン機能を実装する - Qiita https://qiita.com/ucan-lab/items/bd0d6f6449602072cb87 [PHP]Laravel6でmake:authするには - Qiita https://qiita.com/kusumoto-t/items/fc6ef3f5bf1dbe5dc579 Laravel 6でユーザー登録機能・ログイン機能を実装する https://www.no-title.com/programming/laravel/authentication ■CSSとJavaScriptをコンパイルしない代わりにBootstrapを導入 ※npmでコンパイルするのが本来の手順だと思われるが、動作しないのでBootstrapで対応する 以下にBootstrapがある。今回はこれを使用する https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css resources/views/layouts/app.blade.php
<title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> <!-- Styles --> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
■Userモデルの配置場所を変更 app直下にモデルを大量に作ると管理しづらいので、フォルダにまとめるようにする Laravelは「モデルの役割と置き場所は自分で決めてくれ」という思想 app/User.php を app/Models/User.php へ移動
namespace App; ↓ namespace App\Models;
それに伴い、以下も調整 config/auth.php
'model' => App\User::class, ↓ 'model' => App\Models\User::class,
app/Http/Controllers/Auth/RegisterController.php
use App\User; ↓ use App\Models\User;
■メールの送信 「MAIL_DRIVER=sendmail」での対応だと、ローカルではメール送信にかなりタイムラグがあるみたい いったんGmailのSMTPで送信してみる .env
MAIL_DRIVER=smtp MAIL_HOST=smtp.gmail.com MAIL_PORT=465 MAIL_USERNAME=xxxxx@gmail.com MAIL_PASSWORD=xxxxx MAIL_ENCRYPTION=ssl
パスワードリセット機能でメール送信を試す メールで以下のようなリンクが送られてきて、ここからリセットできる http://laravel6.local/password/reset/dc56b315b5cd842e1ae6b888897bf62e8739902d35186fd02d542f942dd6fc0... ■パスワードリセットメールのカスタマイズ 通知クラスを作成
$ php artisan make:notification ResetPasswordNotification
以下にパスワードリセットの実際のクラスがあるので、これを参考に上記クラスを調整する vendor/laravel/framework/src/Illuminate/Auth/Notifications/ResetPassword.php 完成した ResetPassword.php は以下のとおり。ここではメールの件名のみカスタマイズしている(実際は変数化してconfigなどで管理するといい) なお、「['text' => 'emails.password_reset']」部分を「'emails.password_reset'」にするとHTMLメールとして送信される
<?php namespace App\Notifications; use Illuminate\Auth\Notifications\ResetPassword; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Lang; class ResetPasswordNotification extends ResetPassword { use Queueable; /** * Build the mail representation of the notification. * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable) { if (static::$toMailCallback) { return call_user_func(static::$toMailCallback, $notifiable, $this->token); } return (new MailMessage) /* ->subject(Lang::get('Reset Password Notification')) ->line(Lang::get('You are receiving this email because we received a password reset request for your account.')) ->action(Lang::get('Reset Password'), url(config('app.url').route('password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false))) ->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.'.config('auth.defaults.passwords').'.expire')])) ->line(Lang::get('If you did not request a password reset, no further action is required.')); */ ->subject('パスワードリセット通知') ->view(['text' => 'emails.password_reset'], [ 'reset_url' => url(config('app.url').route('password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)) ]); } }
メール本文は resources/views/emails/password_reset.blade.php に作成する 文字列に「&」が入るとエスケープされるため、URLをテキストで表示する場合は「!!」を使っておくと無難(この機能に限れば「&」は入らないので問題無いが)
パスワードリセット通知 {!! $reset_url !!}
app/Models/User.php に sendPasswordResetNotification メソッドを追加する このメソッドの実態は vendor/laravel/framework/src/Illuminate/Auth/Passwords/CanResetPassword.php にある CanResetPassword クラスは vendor/laravel/framework/src/Illuminate/Foundation/Auth/User.php 経由で呼ばれている 使用するクラスは、上で作成した ResetPasswordNotification にする
use App\Notifications\ResetPasswordNotification; /** * Send the password reset notification. * * @param string $token * @return void */ public function sendPasswordResetNotification($token) { $this->notify(new ResetPasswordNotification($token)); }
パスワードリセットを行い、「パスワードリセット通知」という件名で通知が来ることを確認する 本文も上で指定したものになっていることを確認する Laravel6.xでパスワードリセットメールをカスタマイズする - Qiita https://qiita.com/daijin/items/8bc658c9b14cdd15260c ■マイページと編集機能を実装 http://laravel6.local/mypage/ から、マイページにログインできるようにする app/Http/Controllers/Auth/LoginController.php app/Http/Controllers/Auth/RegisterController.php app/Http/Controllers/Auth/ResetPasswordController.php app/Http/Controllers/Auth/VerificationController.php を編集
protected $redirectTo = '/home'; ↓ protected $redirectTo = '/mypage';
app/Http/Middleware/RedirectIfAuthenticated.php を編集
return redirect('/home'); ↓ return redirect('/mypage');
app/Http/Controllers/MypageController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Hash; use App\Http\Controllers\Controller; use App\Models\User; class MypageController extends Controller { /** * インスタンス作成 * * @return void */ public function __construct() { } /** * マイページ * * @param Request $request * @return \Illuminate\Contracts\Support\Renderable */ public function index(Request $request) { return view('mypage/index'); } /** * 基本情報編集画面 * * @param Request $request * @return \Illuminate\Contracts\Support\Renderable */ public function basis(Request $request) { // ユーザー情報取得 $user = Auth::guard()->user(); return view('mypage/basis', [ 'user' => $user, ]); } /** * 基本情報編集 * * @param Request $request * @return \Illuminate\Http\RedirectResponse */ public function basisUpdate(Request $request) { // 入力内容をチェック $validator = Validator::make($request->all(), [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255'], 'password' => ['nullable', 'string', 'min:8', 'confirmed'], ]); if ($validator->fails()) { return redirect('mypage/basis')->withErrors($validator)->withInput(); } // 編集 $userId = Auth::guard()->user()->id; $user = User::find($userId); $user->name = $request->name; $user->email = $request->email; if (!empty($request->password)) { $user->password = Hash::make($request->password); } $user->save(); return redirect('mypage/basis')->with('message', '基本情報を編集しました。'); } }
resources/views/mypage/index.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">マイページ</div> <div class="card-body"> <ul> <li><a href="{{ route('mypage.basis') }}">基本情報編集</a></li> <li><a href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a></li> </ul> </div> </div> </div> </div> </div> @endsection
resources/views/mypage/basis.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">基本情報編集</div> <div class="card-body"> <form method="POST" action="{{ route('mypage.basis.update') }}"> @csrf @if (session('message')) <div class="box"> <div class="alert alert-success"> {{ session('message') }} </div> </div> @endif <div class="form-group row"> <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label> <div class="col-md-6"> <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name', $user->name ?? '') }}" required autocomplete="name" autofocus> @error('name') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email', $user->email ?? '') }}" required autocomplete="email"> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" autocomplete="new-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label> <div class="col-md-6"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" autocomplete="new-password"> </div> </div> <div class="form-group row mb-0"> <div class="col-md-6 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Register') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
routes/web.php にルーティングを追加
// 認証後のコンテンツ Route::group(['middleware' => 'auth'], function () { // マイページ Route::group(['as' => 'mypage'], function () { // マイページ Route::get('/mypage', 'MypageController@index')->name('.index'); // 基本情報編集画面 Route::get('/mypage/basis', 'MypageController@basis')->name('.basis'); // 基本情報編集 Route::post('/mypage/basis_update', 'MypageController@basisUpdate')->name('.basis.update'); }); });
バリデーション 6.x Laravel https://readouble.com/laravel/6.x/ja/validation.html Laravel|【保存版】バリデーションルールのまとめ - わくわくBank https://www.wakuwakubank.com/posts/376-laravel-validation/ Laravel ログイン中、ログイン画面にアクセスした際のリダイレクト先変更 - Qiita https://qiita.com/Ioan/items/551d379fe99ffcbd06b4 ■マルチ認証 http://laravel6.local/mypage/ のマイページは残しつつ http://laravel6.local/admin/login から、管理画面にログインできるようにする database/migrations/2020_04_08_000000_create_admins_table.php
<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateAdminsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('admins', function (Blueprint $table) { $table->engine = 'InnoDB'; $table->bigIncrements('id'); $table->string('name')->comment('名前'); $table->string('email')->unique()->comment('メールアドレス'); $table->timestamp('email_verified_at')->nullable()->comment('メールアドレス確認日時'); $table->string('password')->comment('パスワード'); $table->rememberToken()->comment('トークン'); $table->timestamps(); }); DB::statement('ALTER TABLE admins COMMENT \'管理者\''); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('admins'); } }
database/seeds/AdminsTableSeeder.php
<?php use Illuminate\Database\Seeder; class AdminsTableSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { if (DB::table('admins')->count() > 0) { return; } DB::table('admins')->insert([ 'name' => 'admin', 'email' => 'admin@example.com', 'password' => Hash::make('abcd1234'), 'remember_token' => Str::random(10), ]); } }
database/seeds/DatabaseSeeder.php
// $this->call(UsersTableSeeder::class); ↓ $this->call(AdminsTableSeeder::class);
マイグレーションとシーダーを実行 Seederを追加した直後に「php artisan db:seed」を実行すると、AdminsTableSeederクラスが見つからないとなった 「composer dump-autoload」を実行すれば解決した
$ composer dump-autoload $ php artisan migrate $ php artisan db:seed
main/app/Models/Admin.php
<?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class Admin extends Authenticatable { use Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }
config/auth.php にある guards の設定に以下を追加
'admin' => [ 'driver' => 'session', 'provider' => 'admins', ],
同ファイルにある providers の設定に以下を追加
'admins' => [ 'driver' => 'eloquent', 'model' => App\Models\Admin::class, ],
同ファイルにある passwords の設定に以下を追加(テーブルは password_resets 以外にすべきか?要検証)
'admins' => [ 'provider' => 'admins', 'table' => 'password_resets', 'expire' => 60, ],
app/Http/Middleware/Authenticate.php に admin 用の分岐を追加
use Illuminate\Support\Facades\Route; if (! $request->expectsJson()) { if (Route::is('admin.*')) { return route('admin.login'); } else { return route('login'); } }
app/Http/Middleware/RedirectIfAuthenticated.php に admin 用の分岐を追加
if (Auth::guard($guard)->check() && $guard === 'user') { return redirect('/mypage'); } elseif (Auth::guard($guard)->check() && $guard === 'admin') { return redirect('/admin/home'); }
app/Http/Controllers/Admin/HomeController.php
<?php namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class HomeController extends Controller { public function __construct() { $this->middleware('auth:admin'); } public function index() { return view('admin.home'); } }
app/Http/Controllers/Admin/Auth/LoginController.php
<?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { use AuthenticatesUsers; protected $redirectTo = '/admin/home'; public function __construct() { $this->middleware('guest:admin')->except('logout'); } protected function guard() { return Auth::guard('admin'); } public function showLoginForm() { return view('admin.auth.login'); } public function logout(Request $request) { Auth::guard('admin')->logout(); return $this->loggedOut($request); } public function loggedOut(Request $request) { return redirect(route('admin.login')); } }
resources/views/layouts/admin.blade.php
<!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Scripts --> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" defer></script> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> <!-- Styles --> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div id="app"> <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> {{ config('app.name', 'Laravel') }} </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav mr-auto"> </ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ml-auto"> <!-- Authentication Links --> @guest <li class="nav-item"> <a class="nav-link" href="{{ route('admin.login') }}">{{ __('Login') }}</a> </li> @else <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> {{ Auth::user()->name }} <span class="caret"></span> </a> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('admin.logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;"> @csrf </form> </div> </li> @endguest </ul> </div> </div> </nav> <main class="py-4"> @yield('content') </main> </div> </body> </html>
resources/views/admin/auth/login.blade.php
@extends('layouts.admin') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Login') }}</div> <div class="card-body"> <form method="POST" action="{{ route('admin.login') }}"> @csrf <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <div class="col-md-6 offset-md-4"> <div class="form-check"> <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}> <label class="form-check-label" for="remember"> {{ __('Remember Me') }} </label> </div> </div> </div> <div class="form-group row mb-0"> <div class="col-md-8 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Login') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
resources/views/admin/home.blade.php
@extends('layouts.admin') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">管理者用ページ</div> <div class="card-body"> <ul> <li><a href="{{ route('admin.logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a></li> </ul> </div> </div> </div> </div> </div> @endsection
routes/web.php に admin 用のルーティングを追加
// 管理者 Route::namespace('Admin')->prefix('admin')->group(function () { Route::name('admin.')->group(function () { // ログイン認証関連 Auth::routes([ 'register' => false, 'reset' => false, 'verify' => false ]); }); // 認証後のコンテンツ Route::middleware('auth:admin')->group(function () { Route::group(['as' => 'admin'], function () { // ダッシュボード Route::get('/home', 'HomeController@index')->name('.home'); }); }); });
Laravel6でマルチ認証を実装する(UserとAdminの階層を分ける) - Qiita https://qiita.com/namizatop/items/5d56d96d4c255a0e3a87 Laravelマルチ認証でセッション用テーブル、設定を分ける(Laravel6で確認済み) - Qiita https://qiita.com/lixwork/items/11f7463d6cf35cb46553 Laravel6.7でマルチログインをできる限り自分好みに実装したはなし - Qiita https://qiita.com/wasipo/items/b06be610bb7ba95954df Laravel6x マルチ認証(userとadmin)の実装でハマった事|Makoto|note https://note.com/makoto0419/n/n9d0e1c1dc90d ■バリデーションをリクエストに移行 デフォルトのコードではコントローラ内でバリデーションを行っているが、リクエストクラスにまとめることが推奨されている ユーザ情報のバリデーションをリクエストクラスにまとめてみる もともとのユーザ新規登録処理は vendor/laravel/framework/src/Illuminate/Foundation/Auth/RegistersUsers.php にある 一部このファイルの処理を参考にしている
$ php artisan make:request StoreUserRequest $ php artisan make:request UpdateUserRequest
app/Http/Requests/StoreUserRequest.php
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class StoreUserRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]; } /** * バリデーションエラーのカスタム属性の取得 * * @return array */ public function attributes() { return [ 'name' => '名前', 'email' => 'メールアドレス', 'password' => 'パスワード', ]; } }
main/app/Http/Requests/UpdateUserRequest.php
<?php namespace App\Http\Requests; class UpdateUserRequest extends StoreUserRequest { /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { $rules = parent::rules(); $rules['email'] = ['required', 'string', 'email', 'max:255']; $rules['password'] = ['nullable', 'string', 'min:8', 'confirmed']; return $rules; } }
main/app/Http/Controllers/MypageController.php
use App\Http\Requests\UpdateUserRequest; /** * 基本情報編集 * * @param Request $request * @return \Illuminate\Http\RedirectResponse */ public function basisUpdate(UpdateUserRequest $request) { // 編集 $userId = Auth::guard()->user()->id;
app/Http/Controllers/Auth/RegisterController.php
use App\Http\Requests\StoreUserRequest; use Illuminate\Auth\Events\Registered; // validatorメソッドは削除 /** * Handle a registration request for the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function register(StoreUserRequest $request) { event(new Registered($user = $this->create($request->all()))); $this->guard()->login($user); return $this->registered($request, $user) ?: redirect($this->redirectPath()); }
バリデーション 6.x Laravel https://readouble.com/laravel/6.x/ja/validation.html なお、バリデーション時にエラーがあると自動でリダイレクト処理が走るが、場合によってはその処理が邪魔になることがある これは、StoreUserRequest 内で以下のようにカラの failedValidation を定義することで抑制できるみたい (未検証)
protected function failedValidation(Validator $validator) { }
この場合、コントローラー側で以下のようにしてエラー判定できるみたい (未検証)
protected function register(StoreUserRequest $request) { if ($request->getValidator()->fails()) { $validator = $request->getValidator();
必要に応じて、この書き方を使用するといいかもしれない 【Laravel】フォームリクエストを使いつつ勝手にリダイレクトさせない - Qiita https://qiita.com/rana_kualu/items/77134af5477d27bf2cc2 ■CRUDを作成 管理画面でユーザ情報の登録編集削除をできるようにする app/Http/Controllers/Admin/UserController.php
<?php namespace App\Http\Controllers\Admin; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use App\Http\Controllers\Controller; use App\Http\Requests; use App\Http\Requests\StoreUserRequest; use App\Http\Requests\UpdateUserRequest; use App\Models\User; class UserController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('auth'); } /** * Display a list of all users. * * @param Request $request * @return Response */ public function index(Request $request) { return view('admin.user.index', [ 'users' => User::orderBy('created_at', 'asc')->get() ]); } /** * Display a form of new user. * * @param Request $request * @return Response */ public function create(Request $request) { return view('admin.user.form'); } /** * Create a new user. * * @param Request $request * @return Response */ public function store(StoreUserRequest $request) { $user = new User; $user->name = $request->name; $user->email = $request->email; $user->password = Hash::make($request->password); $user->save(); return redirect()->route('admin.user.index')->with('message', '登録しました。'); } /** * Display a form of edit user. * * @param Request $request * @return Response */ public function edit(Request $request, $id) { $user = User::find($id); return view('admin.user.form', [ 'user' => $user, ]); } /** * Update a user. * * @param Request $request * @return Response */ public function update(UpdateUserRequest $request, $id) { $user = User::find($id); $user->name = $request->name; $user->email = $request->email; if (!empty($request->password)) { $user->password = Hash::make($request->password); } $user->save(); return redirect()->route('admin.user.index')->with('message', '編集しました。'); } /** * Destroy the given user. * * @param Request $request * @param string $id * @return Response */ public function destroy(Request $request, $id) { User::findOrFail($id)->delete(); return redirect()->route('admin.user.index')->with('message', '削除しました。'); } }
resources/views/admin/home.blade.php
<div class="card-body"> <ul> <li><a href="{{ route('admin.home') }}">ホーム</a></li> <li><a href="{{ route('admin.user.index') }}">ユーザ管理</a></li> <li><a href="{{ route('admin.logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a></li> </ul> </div>
resources/views/admin/user/index.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">ユーザ一覧</div> <div class="card-body"> @if (session('message')) <div class="box"> <div class="alert alert-success"> {{ session('message') }} </div> </div> @endif <p><a href="{{ route('admin.user.create') }}" class="btn btn-primary">ユーザ登録</a></p> <table class="table table-striped"> <thead> <th>名前</th> <th>メール</th> <th>編集</th> <th>削除</th> </thead> <tbody> @foreach ($users as $user) <tr> <td class="table-text"><div>{{ $user->name }}</div></td> <td class="table-text"><div>{{ $user->email }}</div></td> <td class="table-text"><a href="{{ route('admin.user.edit', ['id' => $user->id]) }}" class="btn btn-primary">Edit</a></td> <td> <form action="{{ route('admin.user.delete', ['id' => $user->id]) }}" method="POST"> {{ csrf_field() }} {{ method_field('DELETE') }} <button type="submit" class="btn btn-danger"> Delete </button> </form> </td> </tr> @endforeach </tbody> </table> </div> </div> </div> </div> @endsection
resources/views/admin/user/form.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">ユーザ @if (!Request::is('*/create')) {{ '編集' }} @else {{ '登録' }} @endif</div> <div class="card-body"> @if (count($errors) > 0) <div class="alert alert-danger"> @foreach ($errors->all() as $error) {{ $error }}<br> @endforeach </div> @endif <form action="{{ Request::is('*/create') ? route('admin.user.create') : route('admin.user.edit', ['id' => $user->id]) }}" method="POST" class="form-horizontal"> @if (!Request::is('*/create')) {{ method_field('put') }} @endif {{ csrf_field() }} <div class="form-group"> <label for="name" class="col-sm-3 control-label">Name</label> <div class="col-sm-6"> <input type="text" name="name" id="name" class="form-control" value="{{ old('name', isset($user) ? $user->name : '') }}"> </div> </div> <div class="form-group"> <label for="email" class="col-sm-3 control-label">Email</label> <div class="col-sm-6"> <input type="text" name="email" id="email" class="form-control" value="{{ old('email', isset($user) ? $user->email : '') }}"> </div> </div> <div class="form-group"> <label for="password" class="col-sm-3 control-label">Password</label> <div class="col-sm-6"> <input type="password" name="password" id="password" class="form-control" value="{{ old('password') }}"> </div> </div> <div class="form-group"> <label for="password-confirm" class="col-sm-3 control-label">Password</label> <div class="col-sm-6"> <input type="password" name="password_confirmation" id="password-confirm" class="form-control"> </div> </div> <div class="form-group"> <div class="col-sm-offset-3 col-sm-6"> <button type="submit" class="btn btn-primary"> <i class="fa fa-btn fa-plus"></i>@if (!Request::is('*/create')) {{ '編集' }} @else {{ '登録' }} @endif </button> </div> </div> </form> </div> </div> </div> </div> @endsection
routes/web.php
// 管理者 Route::namespace('Admin')->prefix('admin')->group(function () { Route::name('admin.')->group(function () { // ログイン認証関連 Auth::routes([ 'register' => false, 'reset' => false, 'verify' => false ]); }); // 認証後のコンテンツ Route::middleware('auth:admin')->group(function () { Route::group(['as' => 'admin'], function () { // ダッシュボード Route::get('/home', 'HomeController@index')->name('.home'); // ユーザ管理 Route::get('/user', 'UserController@index')->name('.user.index'); Route::get('/user/create', 'UserController@create')->name('.user.create'); Route::post('/user/create', 'UserController@store')->name('.user.store'); Route::get('/user/edit/{id}', 'UserController@edit')->name('.user.edit'); Route::put('/user/edit/{id}', 'UserController@update')->name('.user.update'); Route::delete('/user/delete/{id}', 'UserController@destroy')->name('.user.delete'); }); }); });
■サービスとリポジトリを作成 コントローラ内で直接モデルを呼び出さず、サービスとリポジトリを経由するように変更する 設計方針は人それぞれだが、ここでは 「コントローラからは常にサービスを呼び出し、リポジトリとモデルは呼び出さない」 「データベース操作はリポジトリに閉じ込め、テストのために差し替え可能な状態にする」 とする ※app/Http/Controllers/Auth/RegisterController.php ファイルの register メソッド内に event(new Registered($user)); の処理があると、ユーザ登録時にnginxタイムアウトになってしまうことがあった 冒頭で use Illuminate\Auth\Events\Registered; をインポートするようにして解決したみたい ※似て非なる処理が増えて管理しづらくなってしまうので、 「interface Repository」を作って以下にあるような雛形を定義し、 それを継承して「abstract class UserRepository implements Repository」を作って具体的な処理を実装し、 それを「use App\Contracts\Repositories\UserRepository as UserRepositoryContract;」として読み込んで継承して「class UserRepository implements UserRepositoryContract」を作って必要な追加処理があれば実装する …とする方がいいか app/Contracts/Repositories/UserRepository.php
<?php namespace App\Contracts\Repositories; interface UserRepository { /** * 取得 * * @param int $id * @return mixed */ public function find($id); /** * 検索 * * @param array $conditions * @param array $orders * @param int|null $limit * @return mixed */ public function search(array $conditions, array $orders, $limit); /** * 件数 * * @return int */ public function count(); /** * 保存 * * @param array $data * @param int|null $id * @return mixed */ public function save(array $data, $id); /** * 削除 * * @param int $id * @return mixed */ public function delete($id); }
app/Repositories/UserRepository.php
<?php namespace App\Repositories; use App\Contracts\Repositories\UserRepository as UserRepositoryContract; use App\Models\User; class UserRepository implements UserRepositoryContract { /** @var User */ protected $user; /** * コンストラクタ * * @param User $user * @return void */ public function __construct(User $user) { $this->user = $user; } /** * 取得 * * @param int $id * @return mixed */ public function find($id) { return $this->user->find($id); } /** * 検索 * * @param array $conditions * @param array $orders * @param int|null $limit * @return mixed */ public function search(array $conditions = array(), array $orders = array(), $limit = null) { $query = $this->user->query(); $query = $this->setConditions($query, $conditions); foreach ($orders as $order) { $query->orderBy($order[0], $order[1]); } if ($limit == null) { return $query->get(); } else { return $query->paginate($limit); } } /** * 件数 * * @param array $conditions * @return int */ public function count(array $conditions = array()) { $query = $this->user->query(); $query = $this->setConditions($query, $conditions); return $query->count(); } /** * 保存 * * @param array $data * @param int|null $id * @return User */ public function save(array $data, $id = null) { return $this->user->updateOrCreate(['id' => $id], $data); } /** * 削除 * * @param int $id * @return mixed */ public function delete($id) { return $this->user->findOrFail($id)->delete(); } /** * 検索条件を設定 * * @param int $query * @param array $conditions * @return \Illuminate\Database\Query\Builder */ private function setConditions($query, array $conditions = array()) { if (isset($conditions['id'])) { $query->where('id', $conditions['id']); } if (isset($conditions['name'])) { $query->where('name', $conditions['name']); } if (isset($conditions['name_like'])) { $query->where('name', 'like', $conditions['name_like']); } return $query; } }
app/Services/UserService.php
<?php namespace App\Services; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use App\Contracts\Repositories\UserRepository; use App\Http\Requests\StoreUserRequest; use App\Http\Requests\UpdateUserRequest; class UserService { /** @var UserRepository */ protected $userRepository; /** * コンストラクタ * * @param UserRepository $userRepository * @return void */ public function __construct(UserRepository $userRepository) { $this->userRepository = $userRepository; } /** * 1件取得 * * @param int $id * @return mixed */ public function getUser($id) { return $this->userRepository->find($id); } /** * 検索して取得 * * @param array $conditions * @param array $orders * @param int $limit * @return mixed */ public function getUsers(array $conditions = array(), array $orders = array(), $limit = null) { return $this->userRepository->search($conditions, $orders, $limit); } /** * 件数を取得 * * @param array $conditions * @return int */ public function countUsers(array $conditions = array()) { return $this->userRepository->count($conditions); } /** * 登録 * * @param StoreUserRequest $request * @return \App\Models\User */ public function storeUser(StoreUserRequest $request) { $input = $request->only([ 'name', 'email', 'password', ]); // パスワードを暗号化 $input['password'] = Hash::make($input['password']); // 保存 try { return DB::transaction(function () use ($input) { $result = $this->userRepository->save($input); return $result; }); } catch (\Exception $e) { Log::error('UserService:storeUser', ['message' => $e->getMessage(), 'input' => $input]); return null; } } /** * 編集 * * @param UpdateUserRequest $request * @param int $id * @return \App\Models\User */ public function updateUser(UpdateUserRequest $request, $id) { $input = $request->only([ 'name', 'email', 'password', ]); // パスワードを暗号化 if (empty($input['password'])) { unset($input['password']); } else { $input['password'] = Hash::make($input['password']); } // 保存 try { return DB::transaction(function () use ($input, $id) { $result = $this->userRepository->save($input, $id); return $result; }); } catch (\Exception $e) { Log::error('UserService:updateUser', ['message' => $e->getMessage(), 'input' => $input, 'id' => $id]); return null; } } /** * 削除 * * @param int $id * @return \App\Models\User */ public function deleteUser($id) { try { return DB::transaction(function () use ($id) { return $this->userRepository->delete($id); }); } catch (\Exception $e) { Log::error('UserService:deleteUser', ['message' => $e->getMessage(), 'id' => $id]); return null; } } }
app/Providers/AppServiceProvider.php
public function register() { $this->app->bind( \App\Contracts\Repositories\UserRepository::class, \App\Repositories\UserRepository::class ); }
resources/views/admin/user/index.blade.php
@elseif (session('error')) <div class="box"> <div class="alert alert-danger"> {{ session('error') }} </div> </div> @endif
app/Http/Controllers/Auth/RegisterController.php
<?php namespace App\Http\Controllers\Auth; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Auth\Events\Registered; use App\Http\Controllers\Controller; use App\Http\Requests\StoreUserRequest; use App\Models\User; use App\Services\UserService; class RegisterController extends Controller { /* |-------------------------------------------------------------------------- | Register Controller |-------------------------------------------------------------------------- | | This controller handles the registration of new users as well as their | validation and creation. By default this controller uses a trait to | provide this functionality without requiring any additional code. | */ use RegistersUsers; /** * Where to redirect users after registration. * * @var string */ protected $redirectTo = '/mypage'; /** * Create a new controller instance. * * @param UserService $userService * @return void */ public function __construct(UserService $userService) { $this->middleware('guest'); $this->userService = $userService; } /** * Handle a registration request for the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function register(StoreUserRequest $request) { event(new Registered($user = $this->userService->storeUser($request))); $this->guard()->login($user); return $this->registered($request, $user) ?: redirect($this->redirectPath()); } }
app/Http/Controllers/MypageController.php
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Hash; use App\Http\Controllers\Controller; use App\Http\Requests\UpdateUserRequest; use App\Models\User; use App\Services\UserService; class MypageController extends Controller { /** * インスタンス作成 * * @param UserService $userService * @return void */ public function __construct(UserService $userService) { $this->userService = $userService; } /** * マイページ * * @param Request $request * @return \Illuminate\Contracts\Support\Renderable */ public function index(Request $request) { return view('mypage/index'); } /** * 基本情報編集画面 * * @param Request $request * @return \Illuminate\Contracts\Support\Renderable */ public function basis(Request $request) { // ユーザー情報取得 $user = Auth::guard()->user(); return view('mypage/basis', [ 'user' => $user, ]); } /** * 基本情報編集 * * @param Request $request * @return \Illuminate\Http\RedirectResponse */ public function basisUpdate(UpdateUserRequest $request) { // 編集 $id = Auth::guard()->user()->id; if ($this->userService->updateUser($request, $id)) { return redirect()->route('mypage.basis')->with('message', '基本情報を編集しました。'); } else { return redirect()->route('mypage.basis')->with('message', '基本情報を編集できませんでした。'); } } }
app/Http/Controllers/Admin/UserController.php
<?php namespace App\Http\Controllers\Admin; use Illuminate\Http\Request; use App\Http\Controllers\Controller; use App\Http\Requests; use App\Http\Requests\StoreUserRequest; use App\Http\Requests\UpdateUserRequest; use App\Models\User; use App\Services\UserService; class UserController extends Controller { /** * Create a new controller instance. * * @param UserService $userService * @return void */ public function __construct(UserService $userService) { $this->middleware('auth'); $this->userService = $userService; } /** * Display a list of all users. * * @param Request $request * @return Response */ public function index(Request $request) { return view('admin.user.index', [ 'users' => $this->userService->getUsers([], [['id', 'desc']]), ]); } /** * Display a form of new user. * * @param Request $request * @return Response */ public function create(Request $request) { return view('admin.user.form'); } /** * Create a new user. * * @param Request $request * @return Response */ public function store(StoreUserRequest $request) { if ($this->userService->storeUser($request)) { return redirect()->route('admin.user.index')->with('message', 'ユーザを登録しました。'); } else { return redirect()->route('admin.user.index')->with('error', 'ユーザを登録できませんでした。'); } } /** * Display a form of edit user. * * @param Request $request * @return Response */ public function edit(Request $request, $id) { return view('admin.user.form', [ 'user' => $this->userService->getUser($id), ]); } /** * Update a user. * * @param Request $request * @return Response */ public function update(UpdateUserRequest $request, $id) { if ($this->userService->updateUser($request, $id)) { return redirect()->route('admin.user.index')->with('message', 'ユーザを編集しました。'); } else { return redirect()->route('admin.user.index')->with('error', 'ユーザを編集できませんでした。'); } } /** * Destroy the given user. * * @param Request $request * @param string $id * @return Response */ public function destroy(Request $request, $id) { if ($this->userService->deleteUser($id)) { return redirect()->route('admin.user.index')->with('message', 'ユーザを削除しました。'); } else { return redirect()->route('admin.user.index')->with('error', 'ユーザを削除できませんでした。'); } } }
■新規ユーザ登録時に確認画面を表示 データ登録前に確認画面を表示する例として作成 データの一時的な保持にはフォームのhiddenではなく、セッションを使う routes/web.php
Auth::routes(); Route::post('confirm', 'Auth\RegisterController@confirm')->name('confirm');
app/Http/Controllers/Auth/RegisterController.php
use Illuminate\Http\Request; /** * Show the application registration form. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function showRegistrationForm(Request $request) { if ($request->referer === 'confirm' && $request->session()->has('post.register')) { $post = $request->session()->get('post.register'); } else { $post = []; } return view('auth.register', [ 'user' => $post, ]); } /** * Confirm a registration request for the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Contracts\Support\Renderable */ protected function confirm(StoreUserRequest $request) { $post = $request->all(); $request->session()->put('post.register', $post); return view('auth.confirm', [ 'user' => $post, ]); } /** * Handle a registration request for the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function register(Request $request) { if (!$request->session()->has('post.register')) { return redirect()->route('register'); } $post = $request->session()->get('post.register'); $postRequest = new StoreUserRequest(); $postRequest->merge($post); $request->session()->forget('post.register'); event(new Registered($user = $this->userService->storeUser($postRequest))); $this->guard()->login($user); return $this->registered($request, $user) ?: redirect($this->redirectPath()); }
resources/views/auth/register.blade.php
<form method="POST" action="{{ route('confirm') }}"> <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name', $user['name'] ?? '') }}" required autocomplete="name" autofocus> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email', $user['email'] ?? '') }}" required autocomplete="email"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" value="{{ old('password', $user['password'] ?? '') }}" required autocomplete="new-password"> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" value="{{ old('password_confirmation', $user['password_confirmation'] ?? '') }}" required autocomplete="new-password">
resources/views/auth/confirm.blade.php
@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">{{ __('Register') }}</div> <div class="card-body"> <form method="POST" action="{{ route('register') }}"> @csrf <div class="form-group row"> <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label> <div class="col-md-6"> {{ $user['name'] }} </div> </div> <div class="form-group row"> <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> {{ $user['email'] }} </div> </div> <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> ********** </div> </div> <div class="form-group row mb-0"> <div class="col-md-6 offset-md-4"> <a href="{{ route('register') }}?referer=confirm">Back</a> <button type="submit" class="btn btn-primary"> {{ __('Register') }} </button> </div> </div> </form> </div> </div> </div> </div> </div> @endsection
■論理削除に対応 データを物理削除から論理削除に変更する まずはマイグレーションを作成 $ php artisan make:migration alter_tables 作成されたマイグレーションを以下のように修正
/** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { $table->softDeletes(); }); Schema::table('admins', function (Blueprint $table) { $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('deleted_at'); }); Schema::table('admins', function (Blueprint $table) { $table->dropColumn('deleted_at'); }); }
マイグレーションを実行
$ php artisan migrate
プログラムを修正 app\Models\User.php
use Illuminate\Database\Eloquent\SoftDeletes; use Notifiable, SoftDeletes;
app\Models\Admin.php
use Illuminate\Database\Eloquent\SoftDeletes; use Notifiable, SoftDeletes;
■CSSファイルやJSファイルのキャッシュ読み込み対策を行う ファイル名の後ろに、自動的にタイムスタンプが付くようにする app\Providers\BladeServiceProvider.php を作成
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Blade; class BladeServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { /** * CSSファイルやJSファイルのパスに、ファイルのタイムスタンプを追加 * * @param string $file * @return string */ Blade::directive('asset', function ($file) { $file = str_replace(["'", '"'], '', $file); $path = public_path() . '/' . $file; try { $timestamp = "?<?php echo \File::lastModified('{$path}') ?>"; } catch (\Exception $e) { $timestamp = ''; } return asset($file) . $timestamp; /* // ロードバランサーを考慮する場合(環境によってはURLを自動取得できないので、.envのAPP_URLを参照) $app_url = env('APP_URL'); if (empty($app_url)) { return asset($file) . $timestamp; } else { return $app_url . '/' . $file . $timestamp; } */ }); } }
config\app.php を編集
'providers' => [ 〜略〜 App\Providers\BladeServiceProvider::class,
ビュー内で以下のように asset を使用すると、ファイル名の最後に「?1597309521」のような値が追加される
<script src="@asset('js/common.js')" defer></script> <link href="@asset('css/common.css')" rel="stylesheet">

Advertisement