Laravel 5.7 でフル機能マルチテナント・SaaS型アプリ基盤をゼロから3時間で構築する Part2

[前回](https://qiita.com/hitotch/items/9e22921209eda18d326b)の続きです。
なお、[参考記事はこちら。](https://blog.usejournal.com/writing-a-full-featured-multi-tenant-laravel-app-from-scratch-a0e1a7350d9d)

# 認証と認可できるところまで作る

　各テナント内で、認証と認可ができるようにします。

　ここで使うパッケージは、[Spatie](https://github.com/spatie)による[laravel-permission](https://github.com/spatie/laravel-permission)です。ユーザーに直接Permissionを与えたり、RoleにPermissionを与えてUserにRoleを割り当てたりできます。

## laravel-permissionをインストール

　先に[ドキュメント](https://github.com/spatie/laravel-permission/blob/master/README.md)を読んで、何となく使い方を把握しておくと楽です。英語が読めなくても[使い方の箇所](https://github.com/spatie/laravel-permission/blob/master/README.md#usage)を何となく見てるだけでも分かりそうです。

　パッケージを入れます。

```
composer require spatie/laravel-permission
```

　さて、このパッケージにマルチテナントアプリだということを教えなくてはいけません。修正対象はPermissionとRoleのModelなのですが、ただ、もちろんあとでアップデートすることも考えて、直接Modelのコードを修正するのはやめておきます。具体的には、PermissionとRoleのModelを自作して、パッケージにこれらを使うように教えることにします。これはパッケージのvendor configファイルでの設定で実現可能です。まずpublishしましょう。

```
php artisan vendor:publish
```

　今回は、以下を選びます。

**Provider: Spatie\Permission\PermissionServiceProvider**

　これにより、`config/permission.php`と`xxxx_xx_xx_xxxxxx_create_permission_tables.php`の２つが生成されます。

　Migrationである`xxxx_xx_xx_xxxxxx_create_permission_tables.php`は、テナント用のテーブルとしなくてはいけないので、`database/migrations`フォルダから`database/migrations/tenant`フォルダに移動します。

　次に、`config/permission.php`を編集します。`models`セクションに以下のように追記です。(元の``'permission'``と``'role'``はコメントアウト)

```php:config/permission.php
...
    'models' => [
        // For Tenancy, the custom permission/role models are used.
        'permission' => App\Permission::class,
        'role' => App\Role::class,
...
        //'permission' => Spatie\Permission\Models\Permission::class,
...
        //'role' => Spatie\Permission\Models\Role::class,
...
```

　実際にModelを作ります。

```
php artisan make:model Permission
php artisan make:model Role
```

　空っぽのModelが２つできるので、ここにマルチテナントアプリであることを教えて（テナント用のデータベース接続を使わせる）、なおかつlaravel-permissionパッケージの継承を行います。

```php:app/Permission.php
<?php
namespace App;
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Spatie\Permission\Models\Permission as BasePermission;
class Permission extends BasePermission
{
    use UsesTenantConnection;
}
```

```php:app/Role.php
<?php
namespace App;
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Spatie\Permission\Models\Role as BaseRole;
class Role extends BaseRole
{
    use UsesTenantConnection;
}
```

## RoleとPermissionの初期設定をSeedする

　テナントが作られたとき、テナントデータベースに、RoleとPermissionの初期設定をSeedしたいので、それを設定してきます。これに必要なのは、どのSeederを使うべきかを教えるだけです。`config/tenancy.php`の`'tenant-seed-class'`設定部分を見つけ、以下のように編集します。

```php:config/tenancy.php
...
'tenant-seed-class' => TenantDatabaseSeeder::class,
...
```

　これにより、テナントデータベースが作られた後に'database/seeds/TenantDatabaseSeeder.php'クラスが実行されます。

　Seederを作りましょう。

```
php artisan make:seeder TenantDatabaseSeeder
```

　中を編集します。

```php:database/seeds/TenantDatabaseSeeder.php
<?php
use App\Permission;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Role;
class TenantDatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->addRolesAndPermissions();
    }
    private function addRolesAndPermissions()
    {
        // create permissions for an admin
        $adminPermissions = collect(['create user', 'edit user', 'delete user'])->map(function ($name) {
            return Permission::create(['name' => $name]);
        });
        // add admin role
        $adminRole = Role::create(['name' => 'admin']);
        $adminRole->givePermissionTo($adminPermissions);
        // add a default user role
        Role::create(['name' => 'user']);
    }
}
```

　ポイントとしては、`['create user', 'edit user', 'delete user']`という３つのPermissionを作って、それをadminというRoleに割り当てて、さらに別途userというRoleを作っています。user Roleには何もPermissionが与えられていないですね。これらのPermissionは例なので深く考えないことにして、こんな感じで初期権限割り当てができるということだけ理解すれば十分です。

## 管理ユーザーにadmin権限を与える

　Part1では管理ユーザーを作っただけで権限を与えていませんでしたが、ここで権限を与える処理を加えます。

　ただ、その前にUserモデルがそもそもPermissionとRoleに対応しなければいけないのと、そもそもまだマルチテナント接続しなければいけないことに気づいてないので、以下のように追記編集します。

```php:app/User.php
use Hyn\Tenancy\Traits\UsesTenantConnection;
use Spatie\Permission\Traits\HasRoles;

class User extends Authenticatable
{
    use UsesTenantConnection;
    use HasRoles;

    // ...
}
```

**今後作るほかのModelにも、`use UsesTenantConnection;`を忘れないようにしてください。忘れるとハマります。**

　では、`app/Console/Commands/CreateTenant.php`に権限設定コードを入れましょう。

```php:app/Console/Commands/CreateTenant.php
...
    private function addAdmin($name, $email, $password)
    {
        $admin = User::create(['name' => $name, 'email' => $email, 'password' => Hash::make($password)]);
        $admin->guard_name = 'web';
        $admin->assignRole('admin');
        return $admin;
    }
...
```

　2行追加しました。ガードでwebを指定し、管理ユーザーに権限を付与しています。UIでこの操作を行う場合ガードは気にしなくていいで鵜が、今回はコマンドラインで実行しているので、webに対しての権限付与であることを明示します。

　もう一度テナントを作ってみましょう。

```
php artisan tenant:create john john@example.com cafejohn
```

　成功したらテナントのデータベースを見てみましょう。`model_has_permissions`, `model_has_roles`, `role_has_permissions`, そして `roles`のテーブルができているはずです。

　今後UIを作ることになったとしても、このコマンドをまねして作れば簡単です。

[→ Part3へ](https://qiita.com/hitotch/items/8162d5a0594c72db6fed)