laravel8マルチログイン

#はじめに

フロント画面と管理画面でログイン認証を分けたい場合の
実装方法を記載する。
業務ではオリジナルテンプレートを使用するパターンが多いので、
『laravel/ui』なし、独自コントローラーでの実装を目指します。
ログイン機能を作成します。
※ログアウトやリマインダーは今回はやりません。

#やりたいこと
users:顧客
admins:管理者
別々のログイン画面でログイン認証可能にする。

#環境
PHP 7.3.2
Laravel Framework 8.5.12
MariaDB 10.1.38

#事前準備
Composerをインストール
https://weblabo.oscasierra.net/php-composer-windows-install/

#laravel8環境構築
LaravelをComposerでインストール
`composer create-project --prefer-dist laravel/laravel LaravelMultiLogin "8.*"`
※しばらく時間がかかります

ディレクトリ移動
`cd LaravelMultiLogin`
サーバ起動
`php artisan serve`

ブラウザでhttp://localhost:8000/
にアクセスしてLaravelって表示されれば成功

#ライブラリインストール
・fortify
composer require laravel/fortify
・laravelcollective
composer require laravelcollective/html

#各種設定

①config/app.php 言語設定変更

```config/app.php
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',
'fallback_locale' => 'ja',
'faker_locale' => 'ja_JP',`
```

キャッシュクリア
`php artisan config:cache`

②「.env」設定変更(DB接続先の環境に合わせて変更)

```.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=db_name
DB_USERNAME=root
DB_PASSWORD=
```

③エラーメッセージ日本語化

下記、からダウンロード
https://github.com/Laravel-Lang/lang/tree/master/src/ja

メッセージファイルを配置
C:\XXX\LaravelMultiLogin\resources\lang\ja\auth.php
C:\XXX\LaravelMultiLogin\resources\lang\ja\pagination.php
C:\XXX\LaravelMultiLogin\resources\lang\ja\passwords.php
C:\XXX\LaravelMultiLogin\resources\lang\ja\validation-inline.php
C:\XXX\LaravelMultiLogin\resources\lang\ja\validation.php

```validation.php
'attributes' => [
    'email' => 'メールアドレス',
    'password' => 'パスワード'
],
```

#テーブル作成
マイグレーション作成

```database\migrations\XXXX_XX_XX_XXXXXX_create_admins_table.php
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }
```

マイグレーション実行
`php artisan migrate`

` SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `users` add unique `users_email_unique`(`email`))`

上記エラーが発生した場合は、下記設定をし再度実行
※一度DB内で作成されたテーブルを全て削除

```app\Providers\AppServiceProvider.php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema; // 追加

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
        Schema::defaultStringLength(191); // 追加
    }
}

```

#ログインデータ作成
seedファイル作成
`php artisan make:seed MultiAuthTableSeeder\MultiAuthTableSeeder.php`

```database\seeders
        \DB::table('users')->insert([
            [
                'name' => 'user',
                'email' => 'user@example.com',
                'email_verified_at' => now(),
                'password' => \Hash::make('password'),
                'created_at' => now(),
                'updated_at' => now()
            ],[
                'name' => 'user2',
                'email' => 'user2@example.com',
                'email_verified_at' => now(),
                'password' => \Hash::make('password'),
                'created_at' => now(),
                'updated_at' => now()
            ]
        ]);
        \DB::table('admins')->insert([
            [
                'name' => 'admin',
                'email' => 'admin@example.com',
                'email_verified_at' => now(),
                'password' => \Hash::make('123456789'),
                'created_at' => now(),
                'updated_at' => now()
            ],[
                'name' => 'admin2',
                'email' => 'admin2@example.com',
                'email_verified_at' => now(),
                'password' => \Hash::make('123456789'),
                'created_at' => now(),
                'updated_at' => now()
            ]
        ]);
```

```database\seeders\DatabaseSeeder.php
    public function run()
    {
        // \App\Models\User::factory(10)->create();
        $this->call(MultiAuthTableSeeder::class); // 追加
    }
```

seed実行
`php artisan migrate:fresh --seed`

#コンフィグファイルを変更


```config/auth.php
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'admins' => [               // 追加
            'driver' => 'session',  // 追加
            'provider' => 'admins', // 追加
        ],                          // 追加

        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        'admins' => [                               // 追加
            'driver' => 'eloquent',                 // 追加
            'model' => App\Models\Admin::class,    // 追加
        ],                                        // 追加
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
        'admins' => [                       // 追加
            'provider' => 'admins',         // 追加
            'table' => 'password_resets',   // 追加
            'expire' => 60,                 // 追加
            'throttle' => 60,               // 追加
        ],
    ],

```

キャッシュクリア
`php artisan config:cache`

#モデル作成

php artisan make:model Admin -m
※Userモデルはあるため不要

```app/Models/Admin.php
namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable; // 追加

class Admin extends Authenticatable // 変更
{
    use HasFactory;
}
```

```routes/web.php
// ユーザーログイン
Route::get('user_login', [\App\Http\Controllers\UserLoginController::class, 'showLoginForm'])->name('user_login');
Route::post('user_login', [\App\Http\Controllers\UserLoginController::class, 'login']);

Route::prefix('user')->group(function () {

    Route::middleware('auth:web')->group(function () {
        Route::get('dashboard', function () {
            Auth::guard('web')->logout();
            return 'ユーザーでログイン';
        });
    });
});

Route::prefix('admin')->group(function () {
    // 管理者ログイン
    Route::get('admin_login', [\App\Http\Controllers\AdminLoginController::class, 'showLoginForm'])->name('admin.admin_login');
    Route::post('admin_login', [\App\Http\Controllers\AdminLoginController::class, 'login']);


    Route::middleware('auth:admins')->group(function () {
        Route::get('dashboard', function () {
            Auth::guard('admins')->logout();
            return '管理者でログイン';
        });
    });
});

```

#コントローラー作成

php artisan make:controller UserLoginController

```app/Http/Controllers/UserLoginController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserLoginController extends Controller
{
    //
    public function showLoginForm()
    {
        return view('user.login');
    }

    public function login(Request $request)
    {
        $this->validate($request, [
            'email' => 'required|email', 'password' => 'required',
        ]);

        $credentials = $request->only(['email', 'password']);

        if (\Auth::guard('web')->attempt($credentials)) {

            return redirect('user/dashboard'); // ログインしたらリダイレクト

        }

        return back()->withInput($request->only('email'))
            ->withErrors([
                'email' => ['認証情報が記録と一致しません。']
            ]);
    }
}

```

#view作成

#未ログイン時のリダイレクト先変更
```/app/Exceptions/Handler.php
use Illuminate\Auth\AuthenticationException; // 追加

    protected function unauthenticated($request, AuthenticationException $exception)
    {
        if ($request->expectsJson()) {
            return response()->json(['message' => $exception->getMessage()], 401);
        }
        if ($request->is('admin') || $request->is('admin/*')) {
            return redirect('admin/admin_login');
        }
        return redirect('user_login');
    }
```
unauthenticated メソッド追加

```resources/views/user/login.blade.php
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>ログイン</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" crossorigin="anonymous">
</head>
<body>
<div id="app">
    <div class="container">
        <div class="row justify-content-center mt-5">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">ユーザーログイン</div>
                    <div class="card-body">
                        {{ Form::open(['method' => 'post']) }}
                            <div class="form-group">
                                {{ Form::label('email', 'メールアドレス') }}
                                {{ Form::text('email', null, [
                                    'class' => 'form-control' . ($errors->has('email') ? ' is-invalid' : '')
                                ]) }}
                                @error('email')
                                    <div class="invalid-feedback">
                                        {{ $message }}
                                    </div>
                                @enderror
                            </div>
                            <div class="form-group">
                                {{ Form::label('password', 'パスワード') }}
                                {{ Form::password('password', [
                                    'class' => 'form-control' . ($errors->has('password') ? ' is-invalid' : '')
                                ]) }}
                                @error('password')
                                    <div class="invalid-feedback">
                                        {{ $message }}
                                    </div>
                                @enderror
                            </div>
                            <div>
                                <button type="submit" class="btn btn-primary">ログイン</button>
                            </div>
                        {{ Form::close() }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>
```

php artisan make:controller AdminLoginController

```app/Http/Controllers/AdminLoginController.php

<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

class AdminLoginController extends Controller
{
    //
    public function showLoginForm()
    {
        return view('admin.login');
    }

    public function login(Request $request)
    {
        $this->validate($request, [
            'email' => 'required|email', 'password' => 'required',
        ]);

        $credentials = $request->only(['email', 'password']);

        if (\Auth::guard('admins')->attempt($credentials)) {

            return redirect('admin/dashboard'); // ログインしたらリダイレクト

        }

        return back()->withInput($request->only('email'))
            ->withErrors([
                'email' => ['認証情報が記録と一致しません。']
            ]);
    }
}
```

```resources/views/admin/login.blade.php
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>ログイン</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" crossorigin="anonymous">
</head>
<body>
<div id="app">
    <div class="container">
        <div class="row justify-content-center mt-5">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">管理者ログイン</div>
                    <div class="card-body">
                        {{ Form::open(['method' => 'post']) }}
                            <div class="form-group">
                                {{ Form::label('email', 'メールアドレス') }}
                                {{ Form::text('email', null, [
                                    'class' => 'form-control' . ($errors->has('email') ? ' is-invalid' : '')
                                ]) }}
                                @error('email')
                                    <div class="invalid-feedback">
                                        {{ $message }}
                                    </div>
                                @enderror
                            </div>
                            <div class="form-group">
                                {{ Form::label('password', 'パスワード') }}
                                {{ Form::password('password', [
                                    'class' => 'form-control' . ($errors->has('password') ? ' is-invalid' : '')
                                ]) }}
                                @error('password')
                                    <div class="invalid-feedback">
                                        {{ $message }}
                                    </div>
                                @enderror
                            </div>
                            <div>
                                <button type="submit" class="btn btn-primary">ログイン</button>
                            </div>
                        {{ Form::close() }}
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>
```

#参考記事

https://blog.capilano-fw.com/?p=8159
https://www.webopixel.net/php/1665.html