【Laravel】管理者のユーザー承認機能をつくる

さてさて、前回は ログイン日時の統計をとって棒グラフにしてみる という記事を公開したのですが、この記事を書いている時にある機能をこのブログではまだ紹介していないことを思い出しました。

それは、

管理者のユーザー承認機能

です。

これはどういうものかというと、次のような流れがイメージしやすいと思います。

  1. 誰かが、サイトにユーザー登録する
  2. 管理者がその人をユーザーとして承認(もしくは非承認)する
  3. ユーザーとして承認された人はログインができるようになる

つまり、ユーザー登録してもらった人が本当にユーザーとしてふさわしいかどうかをチェックし、合格/不合格を設定するという機能になります。

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

開発環境: Laravel 6.x

前提として

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

認証用のフィールドを users テーブルに追加する

php artisan make:migration add_accepted_to_users

database/migrations/****_**_**_******_add_two_factor_auth_fields_to_users.phpというファイルが作成されるので、開いて中身を以下のようにします。(太字が追加したコード)

<?php

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

class AddAcceptedToUsers extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->boolean('accepted')
                ->default(false)
                ->comment('管理者承認')
                ->after('remember_token');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('accepted');
        });
    }
}

変更したらマイグレーションを実行しましょう。

php artisan migrate

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

承認されていないユーザーがログインできないようにする

現在のままでは、acceptedに関係なく誰でもログインできてしまいますので、app/Http/Controllers/Auth/LoginController.phpに以下のコードを追加し、未承認の人は強制的にログアウトさせるようにします。

// 省略

class LoginController extends Controller
{
    // 省略

    protected function authenticated(Request $request, $user)
    {
        if(!$user->accepted) {  // 未承認の場合

            \Auth::logout();
            return redirect('/login')->withErrors([
                'email' => 'このアカウントはまだ承認されていません'
            ]);

        }
    }
}

ユーザー登録されたときの処理をつくる

Laravelは、デフォルトではユーザー登録されると自動ログインされることになっていますが、これは承認機能としては正しくないので、強制的に無効にします。

また、ユーザー登録があるかどうかを毎回チェックするのはめんどうなので、ユーザー登録があったらメールで知らせるようにしておきましょう。

Mailableをつくる

まずは送信するメールを作成します。
以下のコマンドを実行してください。

php artisan make:mail UserRegistered

すると、app/Mail/UserRegistered.phpというファイルが作成されるので中身を以下のように変更します。

<?php

namespace App\Mail;

use App\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class UserRegistered extends Mailable
{
    use Queueable, SerializesModels;

    private $user;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->subject('ユーザー登録がありました')
            ->view('emails.auth.registered')
            ->with('user', $this->user);
    }
}

ユーザー登録したとき処理をつくる

続いて、ユーザー登録したときの処理です。
やることは以下の2つです。

  • ユーザー登録を知らせるメールを送信する
  • 自動ログインを無効にする

では、app/Http/Controllers/Auth/RegisterController.phpを開いてregistered()を追加してください。

// 省略

class RegisterController extends Controller
{
    // 省略

    protected function registered(Request $request, $user)
    {
        $admin_email = 'admin@example.com';
        \Mail::to($admin_email)->send(new UserRegistered($user));

        \Auth::logout();
        return '登録が完了しました。承認されるまでしばらくお待ちください';
    }
}

registered()では、まず上の2行で先ほどのメールを送信しています。(なお、テストのためメールアドレスを直に書いていますがconfig/app.phpなどのコンフィグファイルに入れておく方がいいでしょう)

実際に送信されたものは次のとおりです。

また、下の2行では自動ログインされたものを強制的にログアウトし、メッセージを表示するようにしています。

実際登録するとこのようにメッセージ表示されます。

※ なお、こちらもテストなので簡略なものにしていますが、実際には以下のようにリダイレクトして完了メッセージを表示するほうがいいでしょう。

protected function registered(Request $request, $user)
{
    // 省略

    return redirect('/register_complete'); // 移動先で完了メッセージを表示
}

承認機能をつくる

では、ここからは登録されたユーザーを管理者が承認する機能をつくっていきます。

ルートをつくる

まずはルートです。
今回はAjax側とブラウザ側の2つに分けて実装します。

Route::prefix('admin')->group(function(){

    Route::get('user_accept', 'Admin\UserAcceptController@index');
    Route::get('ajax/user_accept', 'Admin\Ajax\UserAcceptController@index');
    Route::post('ajax/user_accept/accept', 'Admin\Ajax\UserAcceptController@accept');

});

【注意】今回はテストですのでこのようになっていますが、このままでは誰でもこれらのURLにアクセスすることができてしまいます。そのため、実際の運用では、管理者だけしかアクセスできないようにミドルウェアなどをつくる必要があります。ミドルウェアの作成は、「こんなとき」のミドルウェア全7実例:ユーザータイプで許可/拒否 を参考にしてみてください。

コントローラーをつくる

続いてコントローラーです。
以下のコマンドを実行してください。

php artisan make:controller Admin\\UserAcceptController
php artisan make:controller Admin\\Ajax\\UserAcceptController

続いて各ファイルの中身を変更します。

まずは、ブラウザ側です。

app/Http/Controllers/Admin/UserAcceptController.phpというファイルが作成されているので、以下のようにしてください。

<?php

namespace App\Http\Controllers\Admin;

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

class UserAcceptController extends Controller
{
    public function index() {

        return view('admin.user_accept.index');

    }
}

そして、Ajax側です。
ファイルは、app/Http/Controllers/Admin/Ajax/UserAcceptController.phpです。

<?php

namespace App\Http\Controllers\Admin\Ajax;

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

class UserAcceptController extends Controller
{
    public function index() {

        $query = \App\User::query();

        // 必要に応じてここで検索

        return $query->get();

    }

    public function accept(Request $request) {

        $user = \App\User::find($request->user_id);
        $user->accepted = $request->accept;
        $result = $user->save();
        return ['result' => $result];

    }
}

なお、accept()は以下2つのデータが送信されてくることを想定しています。

  • user_id ・・・ ユーザーID
  • accepted ・・・ 承認の true or false

ビューをつくる

さらに、ブラウザで表示するためのビューをつくります。

resources/views/admin/user_accept/index.blade.phpというファイルを作成して中身を以下のようにしてください。

<html>
<head>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div id="app">
        <table class="table">
            <thead>
                <th>名前</th>
                <th>E-Mail</th>
                <th>承認状態</th>
                <th></th>
                <th></th>
            </thead>
            <tbody>
                <tr v-for="user in users">
                    <td v-text="user.name"></td>
                    <td v-text="user.email"></td>
                    <td>
                        <div class="text-success" v-if="user.accepted">承認済み</div>
                        <div class="text-danger" v-else>未承認</div>
                    </td>
                    <td>
                        <button type="button" class="btn btn-sm btn-primary" @click="accept(user.id, true)">承認する</button>
                        <button type="button" class="btn btn-sm btn-light" @click="accept(user.id, false)">しない</button>
                    </td>
                </tr>
            </tbody>
        </table>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.0/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.0"></script>
    <script>

        new Vue({
            el: '#app',
            data: {
                users: []
            },
            methods: {
                getUsers() {

                    const url = '/admin/ajax/user_accept';
                    axios.get(url)
                        .then(response => {

                            this.users = response.data;

                        });

                },
                accept(userId, accepted) {

                    if(confirm('承認状態を変更します。よろしいですか?')) {

                        const url = '/admin/ajax/user_accept/accept';
                        const params = {
                            user_id: userId,
                            accept: accepted
                        };
                        axios.post(url, params)
                            .then(response => {

                                if(response.data.result) {

                                    this.getUsers();

                                }

                            });

                    }

                }
            },
            mounted() {

                this.getUsers();

            }
        });

    </script>
</body>
</html>

この中では、getUsers()を使ってユーザーデータを取得し、accept()で承認の有無を変更するようにしています。

また、取得したユーザーは<table></table>タグを使って一覧表示していますが、テストのため検索やページ機能は省略しています。

実際に表示したものがこちらです。

※ なお、名前やE-Mailだけで承認/非承認することはあまりないでしょう。実際の運用では例えば、URLやコメントなどがここに追加されることになるかと思います。

お疲れ様でした😊✨

テストしてみる

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

まずはページを表示したところです。

太郎さんを承認してみましょう。
承認ボタンをクリックします。

すると確認ポップアップがでるので「OK」をクリック。

すると、太郎が承認されました。

成功です😊✨

ダウンロードする

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

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

【Laravel】管理者のユーザー承認機能をつくる

おわりに

ということで、今回は前回に引き続きLaravelのログインに関連する内容をお届けしました。

今回はテクニックはログインだけではなく、何か別の投稿されたものを承認/非承認するという応用もできますので、ぜひ参考にしていただけると嬉しいです。

ちなみに、私が個人的に(ほぼボランティアとして)提供しているプレスリリースのサイトも同じような構成で実装しています。

ぜひこちらもアクセスしてみてくださいね。

ではでは〜!

この記事が役立ちましたらシェアお願いします😊✨