Laravel5.5 Multi-Auth 分かりやすいフォルダ構成 備忘録

##はじめに
LaravelでMulti-Authを実装する記事は既に十分すぎるほど良記事が存在する。基本的な実装方法はそちらを参考にしてもらいたい。

この記事はコピペ用

[[Laravel] 5.3でMulti-Authを使う](http://www.84kure.com/blog/2016/06/06/laravel-5-3%E3%81%A7multi-auth%E3%82%92%E4%BD%BF%E3%81%86/)( 基本的な流れはこちらの記事で掴める )

[Laravel 5.4/5.5 で Multi-Auth を実装する](https://qiita.com/s_of_p/items/3c066f56123cb05a24f1)( v5.4, v5.5で実装する際のTipsが載っている )

## 環境
 - PHP 7.2.14
 - Laravel 5.5
 - make:authした状態
 - DBの接続、Userモデルと同様にAdminモデルを作成しマイグレート済み
 - 
##やりたいこと
これまでの記事ではUserとAdminの複数認証を作る時パターン１のようにデフォルトのAuthをUserのコントローラーとして使っている。筆者はパターン２のように明示的にファイル構造を構築したかった。その時のメモをここに記す。

##### パターン1
```パターン1.
Controller
　　　├── Auth
　　　│   ├── ForgotPasswordController.php
　　　│   ├── LoginController.php
　　　│   ├── RegisterController.php
　　　│   └── ResetPasswordController.php
　　　├── Admin
　　　│   ├── Auth
　　　│   │   ├── ForgotPasswordController.php
　　　│   │   ├── LoginController.php
　　　│   │   ├── RegisterController.php
　　　│   │   └── ResetPasswordController.php
　　　│   └── HomeController.php
　　　├── Controller.php
　　　└── HomeController.php
```

##### パターン2
```パターン2.
Controller
　　　├── Admin
　　　│   ├── Auth
　　　│   │   ├── ForgotPasswordController.php
　　　│   │   ├── LoginController.php
　　　│   │   ├── RegisterController.php
　　　│   │   └── ResetPasswordController.php
　　　│   └── HomeController.php
　　　├── User
　　　│   ├── Auth
　　　│   │   ├── ForgotPasswordController.php
　　　│   │   ├── LoginController.php
　　　│   │   ├── RegisterController.php
　　　│   │   └── ResetPasswordController.php
　　　│   └── HomeController.php
　　　└── Controller.php
```
## ルーティング

これまでの記事では一部しかオーバーライドしかしていなかったが、ここもAuth:routes();を追っかけて全て明示しオーバーライドしてあげる。

**Auth:Route()は以下の部分を呼んでる**

##### Auth:Route()
```/vendor/laravel/framework/src/Illuminate/Routing/Router.php
public function auth()
    {
        // Authentication Routes...
        $this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
        $this->post('login', 'Auth\LoginController@login');
        $this->post('logout', 'Auth\LoginController@logout')->name('logout');

        // Registration Routes...
        $this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
        $this->post('register', 'Auth\RegisterController@register');

        // Password Reset Routes...
        $this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
        $this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
        $this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
        $this->post('password/reset', 'Auth\ResetPasswordController@reset');
    }

```

↓これを自分流に。UserとAdminのルートを引く

##### UserとAdminのルーティング
<details><summary>UserとAdminのルーティング</summary><div>

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

Route::group(['prefix' => 'user', 'namespace' => 'User'],function(){

    Route::get('/home', 'HomeController@index')->name('user.home');
    // Authentication Routes...
    Route::get('login', 'Auth\LoginController@showLoginForm')->name('user.login');
    Route::post('login', 'Auth\LoginController@login');
    Route::post('logout', 'Auth\LoginController@logout')->name('user.logout');
    // Registration Routes...
    Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('user.register');
    Route::post('register', 'Auth\RegisterController@register');
    // Password Reset Routes...
    Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('user.password.request');
    Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('user.password.email');
    Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('user.password.reset');
    Route::post('password/reset', 'Auth\ResetPasswordController@reset');

});


Route::group(['prefix' => 'admin', 'namespace' => 'Admin'],function(){

    Route::get('/home', 'HomeController@index')->name('admin.home');
    // Authentication Routes...
    Route::get('login', 'Auth\LoginController@showLoginForm')->name('admin.login');
    Route::post('login', 'Auth\LoginController@login');
    Route::post('logout', 'Auth\LoginController@logout')->name('admin.logout');
    // Registration Routes...
    Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('admin.register');
    Route::post('register', 'Auth\RegisterController@register');
    // Password Reset Routes...
    Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('admin.password.request');
    Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('admin.password.email');
    Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('admin.password.reset');
    Route::post('password/reset', 'Auth\ResetPasswordController@reset');

});

```
</div></details>

## コントローラー
Authファイルができていると思うが、[パターン2](#やりたいこと)の構造にコピペ。
色々書き換える。気をつけたほうがいいのはviewの参照先とインポート周りの忘れがないように。

##### HomeController.php
<details><summary>HomeController.php</summary><div>

```HomeController.php
<?php

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class HomeController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth:admin');
    }

    /**
     * Show the application dashboard.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('admin.home');
    }
}
```
</div></details>

##### ForgotPasswordController.php
<details><summary>ForgotPasswordController.php</summary><div>

```ForgotPasswordController.php
<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;

class ForgotPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset emails and
    | includes a trait which assists in sending these notifications from
    | your application to your users. Feel free to explore this trait.
    |
    */

    use SendsPasswordResetEmails;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest:admin');
    }

    public function showLinkRequestForm()
     {
         return view('admin.auth.passwords.email');
     }

     public function broker()
   {
        return \Password::broker('admins');
   }
}

```
</div></details>

##### LoginController.php
<details><summary>LoginController.php</summary><div>

```LoginController.php
<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Auth;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/admin/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest:admin')->except('logout');
    }

        public function showLoginForm()
        {
            return view('admin.auth.login');
        }

        protected function guard()
        {
            return \Auth::guard('admin'); //管理者認証のguardを指定
        }
        public function logout(Request $request)
        {
            $this->guard()->logout();

            $request->session()->flush();

            $request->session()->regenerate();

            return redirect(route('admin.home'));
        }
}

```
</div></details>

##### RegisterController.php
<details><summary>RegisterController.php</summary><div>

```RegisterController.php
<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;

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 = '/admin/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest:admin');
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:admins',
            'password' => 'required|string|min:6|confirmed',
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\Admin
     */
    protected function create(array $data)
    {
        return Admin::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => bcrypt($data['password']),
        ]);
    }
    public function showRegistrationForm(){
        return view('admin.auth.register');
    }
}

```
</div></details>

##### ResetController.php
<details><summary>ResetController.php</summary><div>

```ResetController.php
<?php

namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;

class ResetPasswordController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Password Reset Controller
    |--------------------------------------------------------------------------
    |
    | This controller is responsible for handling password reset requests
    | and uses a simple trait to include this behavior. You're free to
    | explore this trait and override any methods you wish to tweak.
    |
    */

    use ResetsPasswords;

    /**
     * Where to redirect users after resetting their password.
     *
     * @var string
     */
    protected $redirectTo = 'admin/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest:admin');
    }

    public function showResetForm(Request $request, $token = null)
    {
        return view('admin.auth.passwords.reset')->with(['token' => $token, 'email' => $request->email]);
    }

    protected function guard()
    {
        return \Auth::guard('admin');
    }

    public function broker()
    {
        return \Password::broker('admins');
    }
}
```
</div></details>

## ビュー
ビューはコントローラーの参照先とパスが合っていれば自分好みに構築すればいい。
気をつけないといけないのは、今回はテーブルを二つ作って登録画面やログアウトを別々にしている。
全てのビューの継承元となっているapp.blade.phpをそれぞれ用意しないといけないところ。

メールのテンプレートを作ったりするなら、make:mailコマンドから作る。
テンプレート内のパスでよく引っかかるのでそこだけ注意。構造はこんな感じ


##### ビューのツリー
```
views/
├── admin
│   ├── auth
│   │   ├── login.blade.php
│   │   ├── passwords
│   │   │   ├── email.blade.php
│   │   │   └── reset.blade.php
│   │   └── register.blade.php
│   └── home.blade.php
├── layouts
│   ├── admin_app.blade.php
│   └── user_app.blade.php
├── mail
│   └── pre_register.blade.php
├── user
│   ├── auth
│   │   ├── login.blade.php
│   │   ├── passwords
│   │   │   ├── email.blade.php
│   │   │   └── reset.blade.php
│   │   └── register.blade.php
│   ├── home.blade.php
│   └── login.blade.php
└── vendor
    └── mail
        ├── html
        └── markdown
```

## メール
メールはNotificationを使って飛ばす。その時にNotificationを追加してモデルに一手間加える。
Adminはマークダウンレンダリング,UserはMailMesageインスタンスのtoMailメソッド
コントローラーのbroker()から通知しsendPasswordResetNotification()を呼び出している。
モデルの一手間は両方同じだが、両サンプルソースを載せておく。

##### AdminPasswordResetNotification.php
<details><summary>AdminPasswordResetNotification.php</summary><div>

```app/Notification/AdminPasswordResetNotification.php
<?php
namespace App\Notifications;

use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
use Illuminate\Notifications\Messages\MailMessage;

class AdminPasswordResetNotification extends ResetPasswordNotification
{
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->markdown('mail.pre_register',['url'=>route('admin.home')]);
    }
}

```
</div></details>

##### UserPasswordResetNotification.php
<details><summary>UserPasswordResetNotification.php</summary><div>

```app/Notification/UserPasswordResetNotification.php
<?php
namespace App\Notifications;

use Illuminate\Auth\Notifications\ResetPassword as ResetPasswordNotification;
use Illuminate\Notifications\Messages\MailMessage;

class UserPasswordResetNotification extends ResetPasswordNotification
{
    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->line('You are receiving this email because we received a password reset request for your account.')
            ->action('Reset Password', url(config('url').route('user.password.reset', $this->token, false)))
            ->line('If you did not request a password reset, no further action is required.');
    }
}
```
</div></details>

##### Adminモデル
<details><summary>Adminモデル</summary><div>

```site/app/Admin.php
<?php

namespace App;

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

use App\Notifications\AdminPasswordResetNotification;

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',
    ];
    public function sendPasswordResetNotification($token)
    {
        $this->notify(new AdminPasswordResetNotification($token));
    }
}

```
</div></details>

##### Userモデル
<details><summary>Userモデル</summary><div>

```site/app/User.php
<?php

namespace App;

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

use App\Notifications\UserPasswordResetNotification;

class User 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',
    ];

    public function sendPasswordResetNotification($token)
    {
        $this->notify(new UserPasswordResetNotification($token));
    }
}
```
</div></details>

## おわりに
正直やればやるほど訳がわからんくなってくる。これはそういうものなんだというので納得できない性分から元の継承元を見に行くのだが見れば見るほど分からんくなってくる。記事を書いてるうちになんで動いているのか分からんくなってきた。せっかく書いたからサンプルコード備忘録としてあげます。

時間がある時にもう一度作ってこの記事も更新したいと思う。