速習Larave3(その12)、役割の導入No.1

タグ: Laravel3  

今回はユーザーにロール(役割)を割りつけてみたいと思います。あなたは管理者ですが、他の人にも記事を投稿してもらおうというわけです。

行うのは、ユーザーロールの導入です。

認証にACL(Active Controle List)を採用したがる人がいます。主にWindowsで採用されているファイルの管理方法です。Linuxでも最近は使用できます。(微妙に違います)

ユーザーがいて、ユーザーは複数のグループに所属し、グループごとにやれる・やれないを割り振るという手法です。やれるやれないとはページの表示やルートのアクセスに対してですが、高度なものになるとテーブルとかDBとかの単位でも制御を可能にします。

これは細かい制御ができます。問題は複雑さです。WebアプリにもACLを採用したがる人がいるようです。しかし、複雑が故に、一般のユーザーが自由に使いこなすことは難しいのです。よほど大きな会社で組織的に分かれており、設定ができる専門の人がいるとか、会社の中でコンピューターを趣味にしている人がいて、その人が管理を引き受けるとか、そんな状況でなければ、たいてい宝の持ち腐れです。最初に一回設定し、それを変更せず使い続けることになりがちです。

エンジニアの悪い癖は、高度なものは便利だろうと色々付け加えてしまうことです。そして、それをユーザーに押し付けます。一般的なユーザーは高度なものを便利だとは考えず、面倒くさいと思うのです。ですから、iなんとかは売れるのです。昔からの格言に従っていますからね。「シンプルさは大切」、「けどそれを実現するための複雑さは隠せ」。

ほとんどのWebアプリであれば、いくつかの役割が揃っていれば十分です。Wordpressのようにです。システムを管理する人、記事を書く人、サイトに登録した人などです。サイトに登録した人は新規ポストの投稿を受け取ったり、コメントを書いたりできるわけです。CMSなら管理する人、記事の発行を許可する人、記事を書く人、登録者です。ショッピングサイトなら、管理者、出店者、購入者です。このくらいの役割分けでしたら、一般のユーザーにも馴染みがあります。なにせ会社に勤めれば、地位で段階が分かれていますしその地位により行うべきことが異なっています。店主・顧客の関係は、毎日の買い物で十分に馴染んでいます。誰もが理解可能です。

ですから、まずは馴染みが深い役割による、サイトの管理を最初に考えましょう。それで管理しきれないような複雑なシステムであればACLは役立つでしょう。

まずはユーザーの役割をどう実現するか考えましょう。一番簡単なのは上下のあるレベルとして導入することです。

役割 レベル 可能なこと
管理者 100 ユーザーの一覧表示・追加・更新・削除、記事の削除
投稿者 50 記事の追加・更新
登録者 10 (今のところ無し)
ゲスト(未ログイン) 0 記事一覧表示・記事表示

レベルを数値で表しました。数字の高いレベルは、低いレベルのできることは全部できると約束付けます。すると数字の大きさの比較だけで、できるできないを判断できます。

このレベルをusersテーブルの構造を変更し、取り入れることもできますし、もしくは1:1の関係を付けて別テーブルにすることもできます。

Laravelの場合、usersテーブルに入っている情報は、ログイン時にAuth::user()メソッドを使用し取得できます。そのため、役割を調べるためにわざわざテーブルへアクセスしなくても良くなります。テーブル設計で1:1の関係が初めから分かっているときは、一つにまとめるのが原則です。その原則に沿ってusersテーブルの構造を変更するのでしたら、こうなります。

usersテーブル
名称 カラム名
ID id
ユーザー名 username
パスワード password
役割 role

今回は機能追加のパターンです。「既に存在するテーブルの構成は変えたくない・変えられない」場合も現実には多いでしょう。そんな時は別テーブルで持ち、関連付けることになります。

usersテーブル
名称 カラム名
ID id
ユーザー名 username
パスワード password
rolesテーブル
名称 カラム名
ID id
ユーザーID user_id
役割 role

rolesテーブルのユーザーIDがusersテーブルのIDと紐ついています。usersテーブルは現在のまま変えなくても済みます。

ここまではユーザー一人が持つ役割はひとつという前提でした。では、この前提を変えましょう。ユーザーは複数の役割を持っており、持っている役割により、アクセスできるページが異なるのです。

するとこれは多対多の関係になります。usersとroles、2つのテーブルでは足りずもうひとつのテーブルを作成する必要があり、そのテーブルに関係づけを保存します。

usersテーブル
名称 カラム名
ID id
ユーザー名 username
パスワード password
rolesテーブル
名称 カラム名
ID id
役割 role
role_userテーブル
名称 カラム名
ID id
ユーザーID user_id
役割ID role_id

role_userテーブルにユーザーと役割の関係を持たせます。これを使い、役割を持っているか調べるわけです。一番複雑な分、やや重たい方法です。ですが、最近はデータベースシステムも良くなっているので、この程度はお茶の子さいさいでしょう。

さて、3つの方法を考えました。どれを採用しましょうか。どれでも良いのですが、今回は最初の一番簡単なパターンです。

まとめておくと以下の方針です。

  • 既存のusersテーブルにroleを付け加える。
  • roleの値は登録ユーザー1、記事投稿者10,管理者100とする。(ゲストは0だが、テーブルには書き込まない。)

今回これを選んだ理由は簡単。テーブルに項目を追加するやり方を覚えるためです。マイグレーションで項目を追加する場合、どうすれば良いのか結構迷うんですよ。ですから、あらかじめ学んでおこうという算段です。

では、マイグレーションを生成してください。コマンドは覚えましたね?

生成したマイグレーションを開き、以下のように更新してください。(クラス名はあなたのマイグレーションコードに合わせてください。)

<?php

class Add_Role_Colum {

    /**
     * Make changes to the database.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function($table) {
            $table->integer('role_id')
                ->unsinged()
                ->default('1');
        });
    }

    /**
     * Revert the changes to the database.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function($table) {
            $table->drop_column('role');
        });
    }

}

ここで覚えて欲しいのは、テーブルに項目を追加するには、Schema::create()ではなく、Schema::table()を使用する点です。

後、デフォルト値の設定はdefault()で行います。roleを指定しない場合は一般ユーザーとして登録します。登録できる最低のレベルで登録するようにしておけば、万が一roleを設定し忘れても、安心です。なにせ、権限が低いですからね。

roleを符号なしの整数にしたのは、将来roleをDBで管理したくなった場合のために、Laravelの生成するIDと型を合わせておくためです。Laravelのマイグレーションでテーブルを作成する場合、idincrement()で作成します。このメソッドで生成された項目は符号なし整数として生成されます。

ついでです。テスト用のユーザーも増やしておきましょう。adminさんはその名の通り管理者にしましょう。わかりやすいですからね。再度、新しいマイグレーションを生成してください。次のようなコードにしてください。(クラス名はあなたのマイグレーションコードに合わせてください。)

<?php

class Make_Users_With_Roles
{

    /**
     * Make changes to the database.
     *
     * @return void
     */
    public function up()
    {
        DB::table('users')
            ->where_username('admin')
            ->update(array(
                'role' => '100',
                'updated_at' => date('Y-m-d H:i:s'),
            ));
        DB::table('users')
            ->insert(array(
                'username' => 'reg',
                'password' => Hash::make('reg'),
                'role' => '1',
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ));
        DB::table('users')
            ->insert(array(
                'username' => 'auth',
                'password' => Hash::make('auth'),
                'role' => '10',
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ));
    }

    /**
     * Revert the changes to the database.
     *
     * @return void
     */
    public function down()
    {
        DB::table('users')
            ->where_username('auth')
            ->delete();
        DB::table('users')
            ->where_username('reg')
            ->delete();
        DB::table('users')
            ->where_username('admin')
            ->update(array(
                'role' => '1',
                'updated_at' => date('Y-m-d H:i:s'),
            ));
    }

}

また、一つだけ新しい要素を入れました。

->where('username', '=', ....)と書く代わりに->where_username(...)と記述できます。

これで準備が整いました。長くなったのでNo.2へ続きます。