マルチログインでモデルを変更したけどsessionテーブルも変更したい

# 背景
マルチログインで、databaseにそれぞれセッション持たせたかった。

<font color="gray">素直に認証・認可使い分けておけという話もあるけど、そうもいかないこともある</font>

# 前提
Laravel 11
テーブル例：users、sessions、admins、admin_sessions

`config/auth.auth`には`guard`として、`user` `admin` を追加済み


# 結論
## セッションテーブルの切り替え

認証ユーザー属性によって、参照するテーブルを切り替える。

### ミドルウェアの作成

<details><summary>サンプルコード</summary>

```php:CustomSessionTable.php

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Symfony\Component\HttpFoundation\Response;

class CustomSessionTable
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        // ガードに応じてセッションテーブルを変更する
        if (Auth::guard('admin')->check()) {
            Config::set('session.table', 'admin_sessions');
        } else {
            Config::set('session.table', 'sessions');
        }

        return $next($request);
    }
}


```

</details>

### ミドルウェアの登録

Laravel 11 からは　`bootstrap/app.php` に登録する


<details><summary>サンプルコード</summary>

```php:app.php

.
.
.

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        api: __DIR__.'/../routes/api.php',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->append(
            \App\Http\Middleware\CustomSessionTable::class
        );
    })
    ->withExceptions(function (Exceptions $exceptions) {
    })->create();

.
.
.


```

</details>



## sessionテーブルの参照カラムをデフォルト（user_id）から変更

sessionsテーブル書き込み時には、user_id カラムに紐づくusersテーブルのidが格納される。
admin_sessions を参照していても同様に、user_idを参照する。

admin_sessions テーブルの中に、多分user_id定義すれば済む話だけど
なんか気持悪いので、参照カラムを切り替える。

### カスタムセッションハンドラ の追加

path はお好きに。
例は `App\Extensions`


<details><summary>サンプルコード</summary>

```php:DatabaseSessionHandler.php

<?php
namespace App\Extensions;

use Illuminate\Contracts\Auth\Guard;
use Illuminate\Session\DatabaseSessionHandler as BaseDatabaseSessionHandler;
use SessionHandlerInterface;

class DatabaseSessionHandler extends BaseDatabaseSessionHandler implements SessionHandlerInterface
{
    protected function addUserInformation(&$payload)
    {
        if ($this->container->bound(Guard::class)) {
            if(config('session.table') === 'admin_sessions') {
                $payload['admin_id'] = $this->container->make(Guard::class)->id();
            } else {
                $payload['user_id'] = $this->container->make(Guard::class)->id();
            }
        }

        return $this;
    }
}


```

</details>



### カスタムプロバイダ の追加

AppServiceProviderに直接入れてもいいけど
分離したい

<details><summary>サンプルコード</summary>

```php:CustomSessionServiceProvider
<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Extensions\DatabaseSessionHandler;

class CustomSessionServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->app->resolving('session', function ($session) {
            $session->extend('database', function ($app) {
                $connection = $app->make('db')->connection();
                $table = config('session.table');
                $minutes = config('session.lifetime');
                
                return new DatabaseSessionHandler($connection, $table, $minutes);
            });
        });
    }
}


```

</details>

### プロバイダの登録


Laravel 11 からは　`bootstrap/providers.php` に登録する

<details><summary>サンプルコード</summary>

```php:providers.php

<?php

return [
    App\Providers\AppServiceProvider::class,
    App\Providers\CustomSessionServiceProvider::class,
];

```

</details>


# 〆

Laravelは8以来触ってなかったけど、洗練されて来ている感