一つのユーザーテーブルを複数のモデルとガードに分ける

## 背景

ロールを元にユーザーテーブルのガードを分けたかった。

実践的には、ガードを分けるよりも、一つのガードでログインした後に権限でアクセス制御したほうがいいと思う。あくまでも実験的なものとして覚え書きを残す。

## 動作環境

-   Mac OS X 10.15.7
-   Laravel 8.33.1
-   Laravel Permission 4.0.0

## プロジェクトの作成

https://qiita.com/uguisu_an/items/82db934bb588273b9d79

## Laravel-Permission の導入

```sh
composer require spatie/laravel-permission
php artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider"
```

## モデルの作成

```sh
php artisan make:model Student -f
php artisan make:model Teacher -f
```

`app/Models/Student.php`

```php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Spatie\Permission\Traits\HasRoles;

class Student extends Model
{
    use HasFactory;
    use HasRoles;

    protected $table = 'users';

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

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

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    protected static function booted(): void
    {
        static::creating(function ($student) {
            $student->assignRole('student');
        });

        static::addGlobalScope('role', function (Builder $builder) {
            $builder->role('student');
        });
    }
}
```

`app/Models/Teacher.php`

```php
namespace App\Models;

use Spatie\Permission\Traits\HasRoles;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Teacher extends Model
{
    use HasFactory;
    use HasRoles;

    protected $table = 'users';

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

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

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    protected static function booted(): void
    {
        static::creating(function ($teacher) {
            $teacher->assignRole('teacher');
        });

        static::addGlobalScope('role', function (Builder $builder) {
            $builder->role('teacher');
        });
    }
}
```

`User` に書いてそれぞれ継承でもいいと思う。

## ガードの設定

プロパイダーの設定を `auth.providers` に追記する。

```php
'students' => [
    'driver' => 'eloquent',
    'model' => App\Models\Student::class,
],
'teachers' => [
    'driver' => 'eloquent',
    'model' => App\Models\Teacher::class,
],
```

ガードの設定を `auth.guards` に追記する。

```php
'student' => [
    'driver' => 'session',
    'provider' => 'students',
],
'teacher' => [
    'driver' => 'session',
    'provider' => 'teachers',
],
```

## ファクトリの作成

`UserFactory` の `definition()` をコピーして、`StudentFactory` と `TeacherFactory` を用意する。

## ロールの投入

マイグレーションして、`Role` を用意する。

```sh
php artisan migrate
php artisan permission:create-role student student
php artisan permission:create-role teacher teacher
```

## 動作確認

`Tinker` で動作確認する。

```sh
php artisan tinker
```

`Student`, `Teacher` を作成して、それぞれ別々に取得できるのを確かめる。

```php
Student::factory(3)->create();
Teacher::factory(3)->create();
Student::all();
Teacher::all();
```

## 補足：ガードを追加したくない場合

`Laravel-Permission` はガードに紐づいているプロバイダーの `model` を元にロールを絞り込む。
ガードを新たに追加しなくても、モデルに `public function guardName()` か `protected $guard_name` を明示すると特定のガードを流用できる。

この方法を選んだ場合、「ロールの投入」でも第二引数の `guard_name` を合わせること。

## 参考資料

-   https://readouble.com/laravel/8.x/ja/eloquent.html#anonymous-global-scopes
-   https://readouble.com/laravel/8.x/ja/eloquent.html#events-using-closures
-   https://spatie.be/docs/laravel-permission/v3/basic-usage/basic-usage
-   https://spatie.be/docs/laravel-permission/v3/basic-usage/multiple-guards
-   https://qiita.com/kd9951/items/c4e5526a6feb4437d1f0