Laravel 5.5 MultiAuth 実装(userとadminをわけてログイン)

## はじめに
今回初めてqiitaを書くため、読みにくい点が多いかと思いますがご容赦ください。
laravel5.5を使用しており、最新のバージョンではありません。

## MultiAuth（マルチログイン）とは
MultiAuth(マルチログイン)は、『ユーザと管理者の２つに分けてログインが行える機能である』という想定をしています。
アプリを使用するゲストユーザ → user
アプリを管理する管理者 → admin

## 前提
- laravelの環境構築を終えている
- MAMPを使用
- DBとの接続を終えている
- 管理者側はログインのみの実装(管理者は1人と想定しSeederでデータを入れる)
- ゲストユーザは新規登録、ログインが行える

## 実装手順
[1.ゲストユーザ側の認証機能の実装](#1-ゲストユーザ側の認証機能の実装)
[2.モデルのディレクトリを変更](#2-モデルのディレクトリを変更)
[3.管理者側のモデル作成](#3-管理者側のモデル作成)
[4.管理者側のモデル編集](#4-管理者側のモデル編集)
[5.管理者側のマイグレーションファイル編集](#5-管理者側のマイグレーションファイル編集)
[6.管理者側のログイン情報をseedで作成](#6-管理者側のログイン情報をseedで作成)
[7.管理者側のコントローラー編集](#7-管理者側のコントローラー編集)
[8.管理者側のビュー画面作成](#8-管理者側のビュー画面作成)
[9.管理者側のビュー画面編集](#9-管理者側のビュー画面編集)
[10.認証ファイルの編集](#10-認証ファイルの編集)
[11.ルーティングの設定](#11-ルーティングの設定)

#1. ゲストユーザ側の認証機能の実装
laravelのバージョンによってコマンドが異なります。

```php:ターミナル
//Larvel5.8まで
$php artisan make:auth

//Laravel6.x 以降
$composer require laravel/ui --dev
$php artisan ui bootstrap --auth
```
特にカラムなどの変更がなければ、続けて以下のコマンドを打ちます。

```php:ターミナル
$php artisan migrate
```
この作業だけで、ゲストユーザ側はログイン、ログアウト、新規登録が行えます。

#2. モデルのディレクトリを変更
モデルを作成する際は、Modelsディレクトリで管理したいと思います。
ファイルの修正が必要になってくるのですが、簡潔に分かりやすく説明してくださっている
以下のqiitaを参考にしてみてください。
参考: [【Laravel】モデルのディレクトリ構成変更についてのメモ](https://qiita.com/vrvr/items/4755e758f5d4b2e07579)


ここまで作業ができたら、
`$php artisan serve`でサーバーを立ち上げます。
新規登録、ログイン、ログアウトが問題なく出来ているか確認してみましょう。

#3. 管理者側のモデル作成

以下のコマンドを打ちます。
**-m**をつけることでマイグレーションファイルも一緒に生成してくれます。

```php:ターミナル
$php artisan make:model Models/Admin -m
```
`Models/Admin.php(モデル)`と
`create_admins_table.php ファイル(マイグレーション)`が生成されるはずです。

#4. 管理者側のモデル編集
Models/User.phpの中身をコピーしてModels/Admin.phpに貼り付けます。
Userの部分をAdminに変更しましょう。

```diff_php:app/Models/Admin.php
<?php

namespace App\Models;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

+ class Admin extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'email', 'password',　 //今回はnameを登録しないので'name'を削除してます
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
}
```

#5. 管理者側のマイグレーションファイル編集
今回はemailとpasswordからログインを行うため、必要なカラムを追記します。
＊ xxxxは作成した日付などの数字が入ります。

```diff_php:database/migrations/xxxx_create_admins_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateAdminsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('admins', function (Blueprint $table) {
            $table->increments('id');
+           $table->string('email')->unique();
+           $table->string('password');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('admins');
    }
}
```

#6. 管理者側のログイン情報をseedで作成
まずは、以下のコマンドでSeederを作成しましょう.

```php:ターミナル
$php artisan make:seeder AdminsTableSeeder
```
seedsディレクトリの中に`AdminsTableSeeder.php`が作成されますので、編集していきましょう。
emailやpasswordは何でも構いませんので自由に決めてください。

⚠️emailのドメインはなるべく`example.com`を使用した方が良いです。
理由が気になる方はググってみてください！

```diff_php:database/seeds/AdminsTableSeeder.php
<?php

use Illuminate\Database\Seeder;

class AdminsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
+       DB::table('admins')->insert([
+           'email' => 'test@example.com',
+           'password' => Hash::make('000000'),
+       ]);
    }
}

```
`DatabaseSeeder.php`に以下を追記します。

```diff_php:database/seeds/DatabaseSeeder.php
<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
+       $this->call(AdminsTableSeeder::class);
    }
}

```

編集が終わったら、dbに反映させましょう。

```php
$php artisan migrate --seed
```

MAMPを起動させphpMyAdminを開き、
以下のようにadminsテーブルに情報が入っているか確認してみましょう。
[![Image from Gyazo](https://i.gyazo.com/8990aba30b8e372ec9590c44742d7764.png)](https://gyazo.com/8990aba30b8e372ec9590c44742d7764)

#7. 管理者側のコントローラー編集
ターミナルでapp/Http/Controllers/に移動しAdminディレクトリを作成します。
右クリックでフォルダを作成でも構いません。

```php:ターミナル
$cd app/Http/Controllers/
$mkdir Admin
```
Adminディレクトリの中に
`LoginController.php`と`HomeController.php`を作成します。
⚠️自分のいるディレクトリが正しいか確認してからコマンドを打ちましょう。
(Controllers/配下にいるとコマンドが打てません！)

```php:ターミナル
$php artisan make:controller Admin/LoginController 
$php artisan make:controller Admin/HomeController 
```
Adminディレクトリの中に作成されているか確認しましょう。

`Admin/LoginController`から編集していきます。
既存の`Auth/LoginController`をコピーして貼り付けた後、変更していきます。

```diff_php:app/Http/Controllers/Admin/LoginController.php
<?php
+ namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
+ use Illuminate\Support\Facades\Auth;

class LoginController extends Controller
{
    use AuthenticatesUsers;

+   protected $redirectTo = 'admin/home';
    
    public function __construct()
    {
+       $this->middleware('guest:admin')->except('logout');
    }

+   public function loginForm()
+   {
+       return view('admin.login');
+   }

+   protected function guard()
+   {
+       return Auth::guard('admin');
+   }
}
```
続いて`Admin/HomeController`を
既存の`HomeController`を参照しながら追記していきます。

```diff_php: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');
    }
}
```

#8. 管理者側のビュー画面作成
ターミナルでresources/views/に移動しadminディレクトリを作成します。
右クリックでフォルダを作成でも構いません。

```php:ターミナル
$cd resources/views/
$mkdir admin
```
adminディレクトリの中に
`login.blade.php`と`home.blade.php`を作成します。

```php:ターミナル
$cd admin
$touch login.blade.php
$touch home.blade.php
```
adminディレクトリの中に作成されているか確認しましょう。

`admin/login.blade.php`から編集していきます。
既存の`auth/login.blade.php`をコピーして貼り付けた後、2箇所の変更をしていきます。

```diff_php:resources/views/admin/login.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
-               <div class="panel-heading">Login</div>
+               <div class="panel-heading">Admin Login</div>

                <div class="panel-body">
-                   <form class="form-horizontal" method="POST" action="{{ route('login') }}">
+                   <form class="form-horizontal" method="POST" action="{{ route('admin.login') }}">
                        {{ csrf_field() }}
                     ...省略
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```
`admin/home.blade.php`も編集していきます。
既存の`home.blade.php`をコピーして貼り付けた後、1箇所だけ変更をしていきます。

```diff_php:resources/views/admin/home.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">Dashboard</div>

                <div class="panel-body">
                    @if (session('status'))
                        <div class="alert alert-success">
                            {{ session('status') }}
                        </div>
                    @endif
-                   You are logged in!
+                   You are logged in admin!
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
```

#9. 管理者側のビュー画面編集

続いて`welcome.blade.php`を編集していきます。
`@auth`ではなく`@if`に変更しています。
`Auth::guard('admin')->check()`を使用し、
管理者かゲストユーザかどちらのログインであるかを判断しそれぞれのhomeに飛ばすように設定しました。
ログインをしていない場合は新規登録、それぞれのログインを表示します。

```diff_php:resources/views/welcome.blade.php
<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        ...省略
    </head>
    <body>
        <div class="flex-center position-ref full-height">
            @if (Route::has('login')) 
                <div class="top-right links">
+                   @if (Auth::guard('admin')->check())
+                       <a href="{{ route('admin.home') }}">Home</a>
+                   @elseif (Auth::guard('user')->check())
+                       <a href="{{ route('home') }}">Home</a>
+                   @else
+                       <a href="{{ route('login') }}">Login</a>
+                       <a href="{{ route('register') }}">Register</a>
+                       <a href="{{ route('admin.login') }}">Admin Login</a>
+                   @endif
                </div>
            @endif

            <div class="content">
                ...省略
            </div>
        </div>
    </body>
</html>

```
次に`app.blade.php`を編集していきます。
`@guest`はログインをしているかどうかで表示を分けています。
今回、管理者にはnameカラムを実装していません。
そのため、if文で管理者であればemailを表示するように、
ゲストユーザであればnameを表示するように書き換えました。

```diff_php:resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    ...省略
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="navbar-header">
                    ...省略
                </div>

                <div class="collapse navbar-collapse" id="app-navbar-collapse">
                    <!-- Left Side Of Navbar -->
                    <ul class="nav navbar-nav">
                        &nbsp;
                    </ul>

                    <!-- Right Side Of Navbar -->
                    <ul class="nav navbar-nav navbar-right">
                        <!-- Authentication Links -->
                        @guest
                            <li><a href="{{ route('login') }}">Login</a></li>
                            <li><a href="{{ route('register') }}">Register</a></li>
+                           <li><a href="{{ route('admin.login') }}">Admin Login</a></li>
                        @else
                            <li class="dropdown">
                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false" aria-haspopup="true" v-pre>
+                                   @if (Auth::guard('admin')->check())
+                                       {{ Auth::user()->email }}
+                                   @else
+                                       {{ Auth::user()->name }}
+                                   @endif
                                    <span class="caret"></span>
                                </a> 

                                <ul class="dropdown-menu">
                                    ...省略
                                </ul>
                            </li>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        @yield('content')
    </div>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
```

#10. 認証ファイルの編集
どんなログイン方法にするか、どのデータベースの情報を見にいくかの設定をしています。

```diff_php:config/auth.php
<?php

return [

    'defaults' => [
-       'guard' => 'web',
+       'guard' => 'user',
        'passwords' => 'users',
    ],

    'guards' => [
-       'web' => [
+       'user' => [
            'driver' => 'session',
            'provider' => 'users',
        ],

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

+       'admin' => [ 
+           'driver' => 'session',
+           'provider' => 'admins',
+       ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
-           'model' => App\User::class,
+           'model' => App\Models\User::class,
        ],

+       'admins' => [
+           'driver' => 'eloquent',
+           'model' => App\Models\Admin::class,
+       ],

        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
        ],

+       'admins' => [
+           'provider' => 'admins',
+           'table' => 'password_resets',
+           'expire' => 60,
+      ],
    ],

];

```
#11. ルーティングの設定
`Auth::routes()`は、authの認証のルーティングを用意してくれています。(ありがたいですね)
アプリ内の以下のフォルダに記述されています。
`vendor/laravel/framework/src/Illuminate/Routing/Router.php`
今回、ゲストユーザはこちらを使用します。

`prefix('admin')`とすることでurlの最初に`/admin`がつきます。
ログインであれば`/admin/login`となります。

`middleware('auth')`はログインしているときのみ、表示ができるようになります。
`middleware('auth:user')`だと、ゲストユーザとしてログインしている時
`middleware('auth:admin')`だと、管理者としてログインしている時

```php:routes/web.php
<?php

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index')
->middleware('auth:user')
->name('home');

Route::prefix('admin')
->group(function () {
    Route::get('/login', 'Admin\LoginController@loginForm')->name('admin.login');
    Route::post('/login', 'Admin\LoginController@login');
});

Route::prefix('admin')
->middleware('auth:admin')
->group(function () {
    Route::get('/home', 'Admin\HomeController@index')->name('admin.home');
});
```

## 最後に
うまく反映されない場合はキャッシュをクリアすることをお勧めします。

```php:ターミナル
//アプリケーションのキャッシュクリア
$php artisan cache:clear
//configのキャッシュクリア
$php artisan config:clear
//ルーティング系のキャッシュクリア
$php artisan route:clear
```

## 参考文献

この記事は以下の情報を参考にして執筆しました。

- [【Laravel】マルチログイン(ユーザーと管理者など)機能を設定してみた【体験談】](https://coinbaby8.com/laravel-multi-login.html)
- [Laravel6でマルチ認証を実装する（UserとAdminの階層を分ける）](https://qiita.com/namizatork/items/5d56d96d4c255a0e3a87)