【Laravel Jetstream】複数モデルでログインできるようにする(Multi Auth)

こんにちは❗フリーランス・エンジニアの 九保すこひ です。

さてさて、Laravel 8.xがリリースされてしばらく経ちましたが、やはり驚きとともに迎えられたのがログインパッケージJetstreamではないでしょうか。

というのも、Jetstreamはインストールするだけでスマホのアプリと連携した2段階認証を実装していたりと、とても多機能だからですね。

そして、今回の記事はこのJetstreamにある機能をつけてみたくなったことが始まりでした。

それは・・・・・・・

マルチ認証

です。

ここで言うマルチ認証とは、一般的なusersテーブルにroleデータもたせたものではなく、全く別のモデルをいくつか使った「本格的な」マルチ認証です。

この方式のメリットとしては、各モデルで保持するデータが別でもOKという点です。(例えば、芸人ユーザーなら「師匠の名前」、ミュージシャンなら「担当パート」という具合に専用のデータを持たせていいということになります)

そこで❗

今回は複数モデルを使ったマルチ認証を実装する方法をご紹介します。

ぜひ皆さんのお役に立てると嬉しいです😊✨
(最後に実際に開発したソースコード一式をダウンロードできますよ👍)

「見られると緊張するので、
撮影しながらピアノ練習中!」

前提として

すでにLaravelにログイン機能Jetstreamがインストールされていることが前提です。
まだの方は以下のURLを参考にしてみてください。

📝 Laravel8.x以降でログイン機能をインストールする方法

やりたいこと

少しでもイメージしやすくしたいので、今回は以下の3つのモデルでログインするように実装してみます。

  • Comedian: 芸人
  • Musician: ミュージシャン
  • Athlete: アスリート

では、実際にやっていきましょう!

モデル&マイグレーションをつくる

まずはじめに、先ほど紹介した3タイプのユーザー(芸人、ミュージシャン、アスリート)のモデルとマイグレーションをつくっていきます。

以下のコマンドを実行してください。

php artisan make:model Comedian -m
php artisan make:model Musician -m
php artisan make:model Athlete -m

モデルを設定する

すると、モデルのファイルが作成されていますので、ログインに対応させるため、それぞれAuthenticatableextends(継承)するように変更します。

app/Models/Comedian.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable; // 👈ここを追加しました

class Comedian extends Authenticatable // 👈ここを変更しました
{

// 省略

app/Models/Musician.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable; // 👈ここを追加しました

class Musician extends Authenticatable // 👈ここを変更しました
{

// 省略

app/Models/Athlete.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\User as Authenticatable; // 👈ここを追加しました

class Athlete extends Authenticatable // 👈ここを変更しました
{

// 省略

マイグレーションをつくる

先ほどのコマンドで同時にマイグレーションも作成されています(-mをつけたからです👍)ので、こちらも設定していきましょう。

【芸人さんのテーブル】

database/migrations/****_**_**_******_create_comedians_table.php

<?php

// 省略

class CreateComediansTable extends Migration
{
    public function up()
    {
        Schema::create('comedians', function (Blueprint $table) {
            $table->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->string('tv_programs')->nullable()->comment('代表的なテレビ番組');
            $table->string('master')->nullable()->comment('師匠');

            $table->timestamps();
        });
    }


【ミュージシャンのテーブル】

database/migrations/****_**_**_******_create_musicians_table.php

<?php

// 省略

class CreateMusiciansTable extends Migration
{
    public function up()
    {
        Schema::create('musicians', function (Blueprint $table) {
            $table->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->string('instruments')->comment('楽器');
            $table->string('genre')->comment('音楽ジャンル');

            $table->timestamps();
        });
    }


【アスリートのテーブル】

database/migrations/****_**_**_******_create_athletes_table.php

<?php

// 省略

class CreateAthletesTable extends Migration
{
    public function up()
    {
        Schema::create('athletes', function (Blueprint $table) {
            $table->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->string('sports_type')->comment('スポーツの種類');
            $table->boolean('is_active')->comment('現役 or 引退済み');

            $table->timestamps();
        });
    }

では、これで一旦マイグレーションを実行してDBテーブルを作成しておきましょう。

以下のコマンドを実行してください。

php artisan migrate

するとテーブルは以下のようになります。

ここでお気づきかもしれませんが、これらのテーブル構成は3つ全て同じにする必要はありません。(ただし、ログインに必要なメールアドレスとパスワードは用意する必要があります)

ここが冒頭でも書いたマルチ認証のメリットです👍

もちろん、通常どおりUserモデルだけを使い、roleの中身でユーザータイプを分ける方法もありますが、その場合は保存する内容が同じか似ている場合に向いているので、どちらを選択するかは保存するデータの中身で決めるといいでしょう。

テストデータをつくる

では、まだ3つのテーブルは空のままですので、作業をしやすいようにそれぞれ1件ずつテストデータを追加しましょう。

以下のコマンドを実行してください。

php artisan make:seed MultiAuthTableSeeder

するとSeederファイル(テストデータの設定ファイル)が作成されるので中身を以下のように変更してください。

database/seeders/MultiAuthTableSeeder.php

<?php

namespace Database\Seeders;

use App\Models\Athlete;
use App\Models\Comedian;
use App\Models\Musician;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Hash;

class MultiAuthTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        // 芸人
        $init_comedians = [
            [
                'name' => '明石家さんま',
                'email' => 'sanma@example.com',
                'password' => 'secret',
                'tv_programs' => 'ホンマでっかTV、お笑い向上委員会',
                'master' => '笑福亭松之助'
            ],

            // ここに追加できます
        ];

        foreach($init_comedians as $init_comedian) {

            $comedian = new Comedian();
            $comedian->name = $init_comedian['name'];
            $comedian->email = $init_comedian['email'];
            $comedian->password = Hash::make($init_comedian['password']);
            $comedian->tv_programs = $init_comedian['tv_programs'];
            $comedian->master = $init_comedian['master'];
            $comedian->save();

        }

        // ミュージシャン
        $init_musicians = [
            [
                'name' => 'Yoshiki',
                'email' => 'yoshiki@example.com',
                'password' => 'secret',
                'instruments' => 'ピアノ、ドラム',
                'genre' => 'ロック、クラシック'
            ],

            // ここに追加できます
        ];

        foreach($init_musicians as $init_musician) {

            $musician = new Musician();
            $musician->name = $init_musician['name'];
            $musician->email = $init_musician['email'];
            $musician->password = Hash::make($init_musician['password']);
            $musician->instruments = $init_musician['instruments'];
            $musician->genre = $init_musician['genre'];
            $musician->save();

        }

        // アスリート
        $init_athletes = [
            [
                'name' => 'ヒクソン・グレイシー',
                'email' => 'rickson@example.com',
                'password' => 'secret',
                'sports_type' => '柔術',
                'is_active' => false
            ],

            // ここに追加できます
        ];

        foreach($init_athletes as $init_athlete) {

            $athlete = new Athlete();
            $athlete->name = $init_athlete['name'];
            $athlete->email = $init_athlete['email'];
            $athlete->password = Hash::make($init_athlete['password']);
            $athlete->sports_type = $init_athlete['sports_type'];
            $athlete->is_active = $init_athlete['is_active'];
            $athlete->save();

        }

    }
}

※ わかりにくくなるので敬称を省略させていただきました。(見てないでしょうが、)ご本人様、ファンの方々どうぞご了承ください。また、年代がバレてしまうテストデータですみません😂

※ なお、通常はComedianMusicianAthleteごとにSeederファイルをつくる方がいいかもしれませんが、今回はテストなので1ファイルにまとめました。また、Laravel 8.xからはfactoryが使いやすくなっていますので、そちらが気になる方は以下のページも参考にしてみてください。

📝 Laravel 8.x の新機能・変更点のまとめ : Factory の構造が変更になる

では、このSeederファイルを登録して有効にしておきましょう。

database/seeders/DatabaseSeeder.php

<?php

// 省略

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(MultiAuthTableSeeder::class); // 👈 ここを追加しました
    }
}

では、これでテストデータを追加してみましょう。
以下のコマンドを実行してください。

php artisan migrate:fresh --seed

するとテーブルの中身は次のようになります。

各モデルでログインできるように設定する

では、先ほど作成した以下3つのモデルを実際にログインで使えるように実装してみましょう。

コンフィグファイルを変更する

コンフィグファイルでログインの設定を変更しましょう。

config/auth.php

<?php

return [

    // 省略

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

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

        // 👇ここを追加しました
        'comedians' => [
            'driver' => 'session',
            'provider' => 'comedians',
        ],
        'musicians' => [
            'driver' => 'session',
            'provider' => 'musicians',
        ],
        'athletes' => [
            'driver' => 'session',
            'provider' => 'athletes',
        ]

    ],

    // 省略

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],
        // 👇ここを追加しました
        'comedians' => [
            'driver' => 'eloquent',
            'model' => App\Models\Comedian::class,
        ],
        'musicians' => [
            'driver' => 'eloquent',
            'model' => App\Models\Musician::class,
        ],
        'athletes' => [
            'driver' => 'eloquent',
            'model' => App\Models\Athlete::class,
        ]
        // 'users' => [
        //     'driver' => 'database',
        //     'table' => 'users',
        // ],
    ],

// 省略

コントローラーをつくる

次にマルチ認証のためのコントローラーをつくります。
以下のコマンドを実行してください。

artisan make:controller MultiAuthController

するとファイルが作成されるので、中身を以下のように変更してください。

app/Http/Controllers/MultiAuthController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class MultiAuthController extends Controller
{
    public function showLoginForm() {

        return view('multi_auth.login');

    }

    public function login(Request $request) {

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

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

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

        }

        return back()->withErrors([
            'auth' => ['認証に失敗しました']
        ]);
    }
}

中身としては、showLoginForm()がブラウザでメールアドレスやパスワードを入力するページ、そして、login()はその送信先になります。

ビューをつくる

先ほどのshowLoginForm()の中で指定したビューをつくっていきます。
以下のファイルを作成してください。

resources/views/multi_auth/login.blade.php

<html>
<head>
    <link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
    <form method="POST" action="multi_login">
        @csrf
        <div class="p-3">
            @error('auth')
            <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-3">
                &#x26A0; {{ $message }}
            </div>
            @enderror
            <label class="block">メールアドレス</label>
            <input class="border rounded mb-3 px-2 py-1" type="text" name="email">
            <label class="block">パスワード</label>
            <input class="border rounded mb-3 px-2 py-1" type="password" name="password">
            <label class="block">ユーザータイプ</label>
            <select name="guard" class="border rounded px-2 py-1 mb-5">
                <option value="">▼選択してください</option>
                <option value="comedians">芸人</option>
                <option value="musicians">ミュージシャン</option>
                <option value="athletes">アスリート</option>
            </select>
            <br>
            <button class="bg-blue-500 text-white rounded px-3 py-2" type="submit">ログイン</button>
        </div>
    </form>
</body>
</html>

※なお、CSSはいま勢いがある(と思われる)Tailwind CSSを使っています👍

ルートをつくる

続いてルートです。
以下の行を追加してください。

routes/web.php

<?php

// 省略

// マルチ認証
// ログイン
Route::get('multi_login', [\App\Http\Controllers\MultiAuthController::class, 'showLoginForm']);
Route::post('multi_login', [\App\Http\Controllers\MultiAuthController::class, 'login']);

// ログアウト
Route::get('multi_login/logout', [\App\Http\Controllers\MultiAuthController::class, 'logout']);

// ログイン後のページ
Route::prefix('comedians')->middleware('auth:comedians')->group(function(){

 Route::get('dashboard', function(){ return '芸人でログイン完了'; });

});
Route::prefix('musicians')->middleware('auth:musicians')->group(function(){

 Route::get('dashboard', function(){ return 'ミュージシャンでログイン完了'; });

});
Route::prefix('athletes')->middleware('auth:athletes')->group(function(){

 Route::get('dashboard', function(){ return 'アスリートでログイン完了'; });

});

ちなみに、ミドルウェアのauth:に続けてコンフィグで設定したユーザー名を書くと、自動的にそのユーザーのログインを判別してくれるようになります。

ログインしていないときの強制リダイレクトをつくる

現時点でもミドルウェアが効いているので、ログインしていないのにログインページへアクセスしようとすると強制リダイレクトされることになります。ただ、移動先が通常のログインページになっているので、これを変更します。

app/Http/Middleware/Authenticate.php

<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Support\Str;

class Authenticate extends Middleware
{
    protected function redirectTo($request)
    {
        if (! $request->expectsJson()) {

            $uri = $request->path();

            // URIが以下3つから始まる場合
            if(Str::startsWith($uri, ['comedians/', 'musicians/', 'athletes/'])) {

                return 'multi_login';

            }

            return route('login');
        }
    }
}

これで全て完了です!

テストしてみる

では、実際にテストしてみましょう❗

まずは、私の大好きな芸人さんの「明石家さんま」さんでログインです。

http://******/multi_login」にブラウザでアクセスし、メールアドレス、パスワード、そして、ユーザータイプを「芸人」にして送信します。

すると・・・・・・

うまくログインができました👍

では、この状態で(実際にはログインしていない)ミュージシャンのページ「http://******/musicians/dashboard」にアクセスしてみましょう。

すると・・・・・・・

はい❗
ミュージシャンとしてはログインしていないので、強制リダイレクトされました。

では最後に、メールアドレス&パスワードは正しいけれど、ユーザータイプをわざと間違えてログインしてみましょう。

すると・・・・・・・・

エラーメッセージが表示されました。
成功です😊✨

ダウンロードする

今回実際に開発した内容を以下からダウンロードすることができます。

【Laravel Jetstream】複数モデルでログインできるようにする

※ただし、マイグレーションなどはご自身で実行してください。

おわりに

ということで今回はLaravel 8.xのログインパッケージJetstreamを使ってマルチ認証を実装してみました。

ところどころLaravel 8.xの書き方になっていますが、根本はこれまでと同じですので応用すれば過去バージョンにも参考になると思います。

なお、冒頭で書いたroleを使ったログインは以下のURLをご覧ください。

📝 シンプル!Laravel5.6で「権限つき」ログインさせる方法

どちらかと言えば、私はこちらの方が好きですが開発する内容によっては使い分けをしています。

ぜひ皆さんもやってみてくださいね。

ではでは〜❗


「電子レンジでパスタが
茹でられるって感動ですね」

今回の技術をつかった開発のご依頼、お待ちしております😊✨ お問い合わせ また、個人レッスンや、わかりにくい部分がありましたらからお気軽にご連絡ください。 どうぞよろしくお願いいたします!
このエントリーをはてなブックマークに追加       follow us in feedly