Laravelで管理画面を作成した方法【第1回マッヒーの勉強部屋】

こんにちは、新人エンジニアの「マッヒー」です。
弊社「インスパイアデザイン」では、月に1回勉強会が開催されており、社長のマーチンさん、ベテランエンジニアのトミーさんからのご指導のもと日々勉強させていただいております。

現在私はPHPフレームフレームであるLaravelの学習中であり、WordPressと同じブログサイトの構築を目指しています。
そこで今回は「管理画面の実装をしよう」というお題をいただきましたので、実行したことを本記事にまとめていきたいと思います。

管理者用テーブルの作成

まずは、管理者情報などを格納するためのテーブルを作成します。
Laravelには、ユーザー認証用のテーブルである、「Users」が元々用意されているが、これを使うのは面白くないのと、今後複数のユーザー認証テーブルが必要になったときに、自作できるようにしておかないと困ると思いますので、今回は自作する方法で試していこうと思います。

マイグレーションの作成

まずは、マイグレーションの作成です。
以下のコマンドで、「create_admin_users_table」というマイグレーションを作成します。

$ php artisan make:migration create_admin_users_table

作成されたマイグレーションファイルのupメソッドに下記を追記します。

public function up()
{
    Schema::create('admin_users', function (Blueprint $table) {
        $table->increment('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->string('password');
        $table->timestamps();
    });
}

以下のコマンドでマイグレーションを実行してテーブルを作成します。

$ php artisan migrate

作成されるテーブルは以下のようなイメージです。

idnameemailpasswordcreated_atupdated_at
1mahhimahhi@example.com3498uf4g92022/6/4 20:002022/6/4 20:00
2tarotaro@example.com439t8jt3482022/6/4 21:002022/6/4 21:00

モデルの作成

続いて、データベースの操作をEloquentで操作できるようにするためにモデルを作成します。
以下のコマンドを実行して「AdminUser」というモデルを作成します。

$ php artisan make:model AdminUser

作成したモデル内に以下のを追記をします。
◆ここで注意点
└通常のモデルは 親クラス「Model」を継承しているが、認証関連のモデルはUsersテーブル同様に、「Authenticatable」というクラスを継承します。

use Illuminate\Foundation\Auth\User as Authenticatable; // ← Authenticatable 名前空間のインポート
class AdminUser extends Authenticatable // 「Model」 ではなく、「Authenticatable」
{
    use HasFactory;

    protected $table = 'admin_users';

    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    protected $hidden = [
        'password',
    ];
}

上記コードのメンバー変数である、$table、$fillable、$hiddenについて簡単に説明させて下さい。
$tableでは、データベースのテーブル名を指定します。この記述は、モデル名がテーブル名の単数形であれば不要になります。しかし、今回はテーブル名が「admin_users」となっており、スネークケースで命名されているため、モデル名を「admin_user」としてもLaravelは関連付けを自動で行ってくれません。なので今回は $table を指定する必要があったのです。
$fillable は、代入可能なカラム名を配列で指定します。逆を言えば $fillable に指定しなかったカラム名はLaravelからは操作ができない ということになります。
$hidden は、取り出し不可能にするカラム名を配列で指定します。$hidden に指定されたカラムはLaravelから取り出すことができなくなります。例えば社外秘のパスワードなどを指定することが多いみたいです。

Seederを使ってテストユーザーの登録

次は、管理者テーブルにテストユーザーを登録します。
以下のコマンドで「AdminUserSeeder」というSeederを作成します。

$ php artisan make:seeder AdminUserSeeder

作成されたAdminUserSeederに以下を追記します。

use App\Models\AdminUser;
use Illuminate\Support\Facades\Hash;

class AdminUserSeeder extends Seeder
{
    public function run()
    {
        // テストユーザーの登録
        $param = [
            'name' => 'admin',
            'email' => 'example@test.com',
            'password' => Hash::make('password'),
        ];
        $adminUser = new AdminUser;
        $adminUser->fill($param)->save();
    }
}

AdminUserと、パスワードのハッシュ化のために使うHathファサードをuseしています。
変数 $param にそれぞれ登録する値を代入し、モデルクラスのfillメソッドとsaveメソッドを呼び出し、データベースに格納する命令を記述しています。

次に、DatabaseSeeder.phpのrunメソッド内にAdminUserSeederを実行するための命令を記述します。

public function run()
{
    $this->call(AdminUserSeeder::class);
}

最後に以下のコマンドで、Seederを実行します。

$ php artisan db:seeder

これで、「admin」という管理者ユーザーを登録することができました。

認証機能とAuthについて

素のPHPでユーザー認証機能をフルスクラッチで開発しようとすると、ソースコードの量が膨大になり、セキュリティホールにも常に気を配る必要があり大変です。
しかし、Laravelにはユーザー認証用の「Authファサード」が用意されており、私たちはこれを利用して簡単にユーザー認証機能を実装することが可能です。

ファサードとは、複雑なデータベース処理などを隠蔽して、利用者にはシンプルなインターフェイスを提供する仕組みです。

http://www.itsenka.com/contents/development/designpattern/facade.html#:~:text=%E3%80%8CFacade%E3%80%8D%E3%81%A8%E3%81%84%E3%81%86%E8%8B%B1%E5%8D%98%E8%AA%9E%E3%81%AF,%E3%81%95%E3%81%9B%E3%81%AA%E3%81%84%E3%81%A8%E3%81%84%E3%81%86%E7%9B%AE%E7%9A%84%E3%82%82%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82

Authファサードの使い方

Authファサードを使用できるようにするためには、以下の名前空間を use する必要あがある。

use Illuminate\Support\Facades\Auth;

そして例えば、メールアドレスとパスワードの入力を元にユーザー認証を行う場合、以下のようなコードになる。

if ( Auth::attempt(['email' => $request->email, 'password' => $request->password]) )
{
    $request->session()->regenerate();
    return redirect('ログイン後のリダイレクト先URL');
}
return back()->withErrors([
    'login_error' => 'メールアドレスかパスワードが間違っています。',
]);

Providerの追加

Usersテーブル以外でもAuthを使用できるようにするために、auth.phpを編集します。

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
    // 以下を追記
    'admin_users' => [
        'driver' => 'eloquent',
        'model' => App\Models\AdminUser::class,
    ],
],

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    // 以下を追記
    'admin' => [
        'driver' => 'session',
        'provider' => 'admin_users', // providerに指定した名前
    ],
],

このように auth.php を編集することにより、以下のようにguradを指定してAuth機能を使うことができるようになります。

Auth::guard('admin')

コントローラの作成

以下のコマンドで、AuthControllerを作成します。
※今回はAuthディレクトリ内に作成する。

$ php artisan make:controller Auth/AuthController

コントローラ内に以下の3つのメソッドを記述します。
showLogin メソッドはログイン画面のviewを返します。
login メソッドはログインフォームで入力されたメールアドレス、パスワードの値が正しいかどうかのチェックを行っています。
Authクラスのattemptメソッドを用いることによって、フォームの入力情報とデータベースの照合を簡単に行うことができます。
ログインに成功した場合は true を返し、セッションを登録し、/admin (管理画面)へリダイレクトされます。
ログインに失敗した場合は、ログイン画面へリダイレクトさせると同時に、 login_error というセッションにエラーメッセージを渡して表示されるようにします。
logout メソッドは、ログアウトボタンがクリックされた時のログアウト処理がまとめられています。

public function showLogin()
{
    return view('admin.login_form');
}

public function login()
{
    $credentials = $request->only('email', 'password');

    if ( Auth::guard('admin')->attempt( $credentials ) )
    {
        $request->session()->regenerate();
        return redirect('admin');
    }

    return back()->withErrors([
        'login_error' => 'メールアドレスかパスワードが間違っています。',
    ]);
}

public function logout(Request $request)
{
    Auth::guard('admin')->logout();
    $request->session()->invalidate(); // セッションの削除
    $request->session()->regenerateToken(); // セッションの再生成(_token)
    return redirect('admin/login_form');
}

ルーティングの設定

web.php を以下のように記述し、ルーティングの設定を行います。

// ログイン前の処理
Route::group(['middleware' => ['guest:admin']], function () {

    // ログイン画面
    Route::get('admin/login_form', [AuthController::class, 'showLogin'])->name('admin.showLogin');

    // ログインフォームの送信
    Route::post('admin/login_form', [AuthController::class, 'login'])->name('admin.login');
});

// ログイン後の処理
Route::group(['middleware' => ['auth:admin']], function () {

    // 管理画面
    Route::get('admin', [AuthController::class, 'index'])->name('admin.index');

    // ログアウト処理
    Route::get('admin/logout', [AuthController::class, 'logout'])->name('admin.logout');
});

ここではLaravelが標準で用意している guest と auth というミドルウェアを使用します。
guest はログイン前、 auth はログイン後に処理されるミドルウェアで、
guestを指定したrouteは、ログイン前のユーザーしかアクセスすることができなくなります。
└ ログイン後のユーザーがアクセスすると、管理画面へリダイレクトする。
authを指定したrouteは、ログイン中のユーザーしかアクセスすることができなくなります。
└ ログイン前のユーザーがアクセスすると、ログインフォームへリダイレクトする。

しかし、初期状態ではguestミドルウェアでは /login にリダイレクトされ、
authミドルウェアでは /home にリダイレクトされるようになっているため、変更する必要があります。
その設定を次に行います。

リダイレクト先の設定

まずは、authのリダイレクト先の設定から行います。
Middleware ディレクトリ内の RedirectIfAuthenticated.php を開き、以下のように追加修正します。
今回は、引数の $guard が admin だった場合、ADMIN_HOME というURLにリダイレクトされるように設定します。

public function handle(Request $request, Closure $next, ...$guards)
{
    $guards = empty($guards) ? [null] : $guards;

    foreach ($guards as $guard) {
        if (Auth::guard($guard)->check()) {

            // 管理画面へリダイレクト(以下を追加)
            if ($guard == 'admin') {
                return redirect(RouteServiceProvider::ADMIN_HOME); // ← 新たに定義した定数を指定する
            }

            return redirect(RouteServiceProvider::HOME);
        }
    }

    return $next($request);
}

また、定数 ADMIN_HOME は、別ファイルの「RouteServiceProvider.php」に定義しておきます。

public const ADMIN_HOME = '/admin';

次はguestのリダイレクト先の設定を行います。
Authenticate.php の redirextTo メソッドの戻り値に指定します。
admin.showLogin とは、web.phpで指定した name メソッドです。

protected function redirectTo($request)
{
    if (! $request->expectsJson()) {
        return route('admin.showLogin'); // ← web.php の nameメソッドに指定した名前
    }
}

これで、リダイレクトの設定ができました。

テストユーザーでログインしてみる

/admin/login_form へアクセスすると作成したログインフォームが表示されます。
Seederで登録したテストテストアカウント情報を入力して、ログインボタンをクリックすると、

以下のようにな管理画面へリダイレクトされます。

無事に管理画面の実装ができました。

この記事を書いた人

峯岸真弘