Laravel7でエラー修正しながらAuthとVoyagerでMultiAuthする!Laravel超初心者の勉強

2020-05-29

MultiAuthという響きがかっこいいのでやってみようと思います。
LaravelのAuthで一般ユーザー、Voyagerで管理者みたいなマルチ認証の外枠を目指します。

参考サイト:Laravel5.6, 5.7「Voyager」パッケージの認証をフロントと分離!(multi-auth)

基本的に上記サイト通りに進めればうまくいくのです(詳しい説明などは上記サイトをご参照ください。)が、Laravel7ではauthの入れ方が違ったり、エラーが発生するのでその対処法を書きたいと思います。
(ここで挫折しかけましたがなんとか乗り切ったので一緒に頑張りましょう。)

少しわかりづらいですが、同じエラーに悩まされるとも限らないので、エラー箇所はあとから修正した箇所を記載しています。

新規プロジェクト作成済み、データベースの設定済みの状況からスタートします。

動作環境

ざっくりMac El capitan
mysql@5.6
PHP7.2.5
Laravel Framework 7.6.2
ローカル環境です。

Laravel7でauth

特に説明はしませんが、以下のコマンドを順次実行していきます。

composer require laravel/ui --dev
php artisan ui vue --auth
npm install
npm run dev

全て実行できたらAuthが作成されました。

ユーザー用のテーブル作成、各種設定

テーブルを作成

ユーザー用にテーブルを作成し各種設定を行っていきます。

php artisan make:model Customer -m

作成したModelを修正します。

ファイルパス:database/migrations/作成した日時_create_customer_table.php

同じ、階層にいる2014_10_12_000000_create_users_table.phpの中身をコピーして貼り付け、中身をUserからCustomerへ変更します。(複数系の箇所はUserに合わせて複数形へ)

準備ができたらマイグレーションしテーブルを作成します。

php artisan migrate

auth.phpの編集

ファイルパス:config/auth.php (修正箇所太字)

<?php
return [
'defaults' => [
    'guard' => 'customer',
    'passwords' => 'customer',
],
  'guards' => [
      'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
        'customer' => [
            'driver' => 'session',
            'provider' => 'customer',
        ],

  ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        'customer' => [
            'driver' => 'eloquent',
            'model' => App\Customer::class,

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

    ],
    'password_timeout' => 10800,
];

RegisterController.phpの編集

ファイルパス:app/http/controller/auth/RegisterController.php (コメントアウト省略)

<?php
namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Customer;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller{

use RegistersUsers;

protected $redirectTo = RouteServiceProvider::HOME;

public function __construct() {
$this->middleware('guest');
}

protected function validator(array $data) {
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => 'required|string|email|max:255|unique:customers',
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
protected function create(array $data) {
return Customer::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}

(テーブル名が勝手に複数形になることを知らなくて大混乱しました。)

Customer.phpの編集 (2ファイル追加)

追加ファイル 1つ目

追加:app/Customer.php

appにあるUser.phpをコピーし、Customer.phpを作成します。
中身のUserをCustomerに変更して保存。( 2箇所 )

上記は「Call to undefined method App\Customer::getAuthIdentifierName()」こんなようなエラーがでたので対応しました。

追加ファイル 2つ目

追加:vendor/laravel/framework/src/Illuminate/Foundation/Auth/Customer.php

こちらも同じ場所にあるUser.phpを複製し、Customer.phpを作成します。
中身のUserをCustomerに変更して保存。( 1箇所 )

上記はレジスター画面でユーザ登録すると「Symfony\Component\ErrorHandler\Error\FatalError Class 'Illuminate\Foundation\Auth\Customer' not found」というエラーがでたので対応しました。

サーバーでAuthの確認

Authが問題なく動くかサーバーを起動し動作確認します。ユーザー登録しログインできれば問題なしです。

laravel auth

念のため、ログアウトし再度ログインできるかもみておきました。

Voyagerをインストール

Voyagerをインストールします。手順は以下参照。

.envを編集

ファイルパス:.env

以下を追加

SESSION_COOKIE=auth
SESSION_COOKIE_ADMIN=auth-admin

auth.phpを編集

ファイルパス:config/auth.php

<?php
$conf = [
'defaults' => [
    'guard' => 'customer',
    'passwords' => 'customer',
],
  'guards' => [
      'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'api' => [
            'driver' => 'token',
            'provider' => 'users',
            'hash' => false,
        ],
        'customer' => [
            'driver' => 'session',
            'provider' => 'customer',
        ],
  ],
    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\User::class,
        ],
        'customer' => [
            'driver' => 'eloquent',
            'model' => App\Customer::class,
        ],
    ],
    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],
        'customer' => [
            'provider' => 'customer',
            'table' => 'password_resets',
            'expire' => 60,
        ],
    ],
    'password_timeout' => 10800,
];

$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
 if (strpos($uri, '/admin/') === 0 || $uri === '/admin') {
    $conf['defaults'] = [
        'guard' => 'user',
        'passwords' => 'users',
    ];
}
 return $conf;

session.phpを編集

ファイルパス:config/session.php

<?php

use Illuminate\Support\Str;

$conf = [
    'driver' => env('SESSION_DRIVER', 'file'),
    'lifetime' => env('SESSION_LIFETIME', 120),
    'expire_on_close' => false,
    'encrypt' => false,
    'files' => storage_path('framework/sessions'),
    'connection' => env('SESSION_CONNECTION', null),
    'table' => 'sessions',
    'store' => env('SESSION_STORE', null),
    'lottery' => [2, 100],
    'cookie' => env(
        'SESSION_COOKIE',
        Str::slug(env('APP_NAME', 'laravel'), '_').'_session'
    ),
    'path' => '/',
    'domain' => env('SESSION_DOMAIN', null),
    'secure' => env('SESSION_SECURE_COOKIE'),
    'http_only' => true,
    'same_site' => 'lax',
];

$uri = isset($SERVER['REQUEST_URI']) ? $_SERVER['REQUESTURI'] : '';
if (strpos($uri, '/admin/') === 0 || $uri === '/admin') {
    $conf['cookie'] = env(
        'SESSION_COOKIE_ADMIN',
        str_slug(env('APP_NAME', 'laravel'), '_').'_admin_session'
    );
}
return $conf;

サーバーで動作確認

「http://localhost:8000/admin/login」でVoyagerへ。

ここまででうまくいく人もいるんだと思います。が試行錯誤へ突入。

試行錯誤編

動作確認してみると問題発生。

InvalidArgumentExceptionAuth guard [user] is not defined.

上記エラーにぶち当たったので、キャッシュをクリア。

php artisan config:clear
php artisan config:cache

続いて、ユーザを作成したところ以下のエラーにぶちあたる。

BadMethodCallException
Call to undefined method App\Customer::setRole()

サーバーから確認するとこんなエラー。

BadMethodCallException
Call to undefined method App\Customer::hasPermission()

Userを使って欲しいんだけれどなんで。

こんなエラーがでたり。

TypeError Argument 2 passed to Illuminate\Auth\SessionGuard::__construct() must be an instance of Illuminate\Contracts\Auth\UserProvider, null given, called in laraval/blog_master_autu_voyage_with_bootstrap /vendor/laravel/framework/src/Illuminate/Auth/AuthManager.php on line 125

いろいろ試すも、どれも不発。認証しているテーブルがUserでかぶってしまうしで試行錯誤すること2日。ようやく改善策見つけました。

再びauth.phpを編集

ファイルパス:config/auth.php

省略

$uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
 if (strpos($uri, '/admin/') === 0 || $uri === '/admin') {
    $conf['defaults'] = [
        'guard' => 'web',
        'passwords' => 'users',
    ];
}
 return $conf;

guardをwebに変更して動作確認を行ったところ、なんと!解決できました。ほんの少しだけguardに詳しくなれた。

が、しかし。喜びもつかの間、LaravelのRegesterでユーザー登録すると以下のエラー。

BadMethodCallException
Call to undefined method App\Customer::setRole()

見ていくと、voyagerが干渉しているもよう。(vendor/tcg/voyager/src/VoyagerServiceProvider.php:105)

app/config/voyager.phpの設定を変えてとりあえず問題の箇所へ遷移しないようごまかす。

'user' => [
'add_default_role_on_register' => false, // trueからfalseへ
'default_role' => 'user',
'admin_permission' => 'browse_admin',
'namespace' => App\User::class,
'redirect' => '/admin'
],

デフォルトのロールを割り当てる機能らしい。もう面倒くさいととりあえずfalseにしてなかったことにしました。もともと、Voyagerは管理用でユーザー登録の機能は実装する予定なかったので。

でも気持ち悪い、なんでvoyager干渉しているのか突き詰めたい。だが能力が足りない。どなたかただしい方法知っていたら教えてください。

おわり

なんどマルチ認証諦めようと思ったことか。それでもなんとなくでマルチ認証(一応)できてしまうんだからやっぱりLaravelすごいな。

AuthとVoyagerを一度も単体で導入したことない方はまず単体から実施したほうがすんなり実施できると思います。