Laravel Filamentでユーザー画面を作ってみた


:::note info
前提
+ Laravel Version11
+ Docker for Windows Desktop
+ Ubuntu環境下でLaravel Sailを使用
+ 過去記事「[Filamentを使ってみた](https://qiita.com/AKFJ21/items/46e99aac0a11b247a592)」の状態（既にadmin用のパネルをインストール済）
+ 過去記事「[Laravelでモデルの定義をしてみた](https://qiita.com/AKFJ21/items/4024b00d527fa4433303)」の設定済（掲示板モデルの定義）
+ 過去記事「[Laravelでデータベースのテーブル定義をしてみた](https://qiita.com/AKFJ21/items/44a033c120745683ee85)」の設定済（掲示板テーブルの定義）
:::

## 概要
Laravel Filamentは管理画面を簡単に作成できるという記事が多いのですが、通常のユーザー画面も簡単に作れないかと思って試して見ました。

## 課題
+ アクセスしているユーザーに認可されているデータのみを表示・編集・削除可能とし、それ以外のデータは表示・編集・削除不可とする必要がある。

:::note alert
+ 認可・非認可データの制御やセキュリティ対策はFilament公式ページにも記載されている通り、自己責任でお願いします。
:::

## 作業
+ 1.ユーザー用パネルのインストール
+ 2.リソース作成
+ 3.リソース/モデル設定

## 1.ユーザー用パネルのインストール
参考： https://filamentphp.com/docs/3.x/panels/installation

```shell:コマンド
./vendor/bin/sail php artisan filament:install --panels
```

```shell:コンソール画面
 ┌ What is the ID? ─────────────────────────────────────────────┐
 │ admin                                                        │
 └──────────────────────────────────────────────────────────────┘
```
既にadminのIDでパネルをインストールしているので、今回はcustomerとしてインストールしてみました。
```shell:コンソール画面
 ┌ What is the ID? ─────────────────────────────────────────────┐
 │ customer                                                     │
 └──────────────────────────────────────────────────────────────┘
```
### ログイン画面が管理者用とユーザー用とで異なる
#### 管理者用ログイン画面（http://localhost/admin/）
![login_admin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3886350/c1b52ba7-039d-2b24-8293-83502f5f06ef.png)
#### ユーザー用ログイン画面（http://localhost/customer/）
![login_customer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3886350/7cb4b8d0-b8a0-e813-6036-9a9842f71ced.png)

### 最初にインストールしたパネルのIDが管理者用、後でインストールしたパネルのIDが通常のログイン画面
２つのパネルのログイン画面が違ったので、Laravelをまっさらな状態にし直し、Filamentのパネルをインストールする際、最初にIDをcustomerとして、２回目のIDをadminとしてインストールしてみたところ、最初にインストールしたIDが管理者用になって、その後でインストールした場合のIDは通常のログイン画面になるようです。

## 2.リソースの作成
掲示板モデルのリソースを作成します。
```shell:コマンド
./vendor/bin/sail php artisan make:filament-resource -G Board
```
```shell:コンソール画面
 ┌ Which panel would you like to create this in? ───────────────┐
 │ › ● admin                                                    │
 │   ○ customer                                                 │
 └──────────────────────────────────────────────────────────────┘
```
どちらのパネルIDに設定するか聞いてきましたので、customerを選択しました。

管理者パネルにもBoardリソースを作成します。
上と同じコマンドを実行し、adminパネルを選択して作成します。

## 3.リソース/モデル設定
このままだと、ログインできる全てのユーザーが、自分以外のIDの新規投稿や変更、削除が出来てしまいます。

### 3.1 画面上に表示されるuser_idを非表示
新規作成画面でuser_idをユーザーが入力できるのは問題ですので非表示にします。
また、一覧画面ではユーザー自身の投稿一覧のみを表示するので、ユーザー名表示を非表示にします。
同様に一覧画面のcreated_at項目とupdated_at項目の表示設定を非表示にします。

```shell:app/Filament/Customer/Resource/BoardResource.php(抜粋)
    public static function form(Form $form): Form
    {
        return $form
            ->schema([
                //  Forms\Components\Select::make('user_id')
                //      ->relationship('user', 'name')
                //      ->required(),
                Forms\Components\TextInput::make('title')
                    ->required()
                    ->maxLength(100),
　　・・・
    public static function table(Table $table): Table
    {
        return $table
            ->columns([
                //  Tables\Columns\TextColumn::make('user.name')
                //      ->numeric()
                //      ->sortable(),
                Tables\Columns\TextColumn::make('title')
                    ->searchable(),
                Tables\Columns\TextColumn::make('category')
                    ->searchable(),
                Tables\Columns\TextColumn::make('texts')
                    ->searchable(),
                Tables\Columns\TextColumn::make('date_expiry')
                    ->date()
                    ->sortable(),
                //  Tables\Columns\TextColumn::make('created_at')
                //      ->dateTime()
                //      ->sortable()
                //      ->toggleable(isToggledHiddenByDefault: true),
                //  Tables\Columns\TextColumn::make('updated_at')
                //      ->dateTime()
                //      ->sortable()
                //      ->toggleable(isToggledHiddenByDefault: true),
            ])       
```

### 3.2 新規登録/編集時にuser_idを設定
上のままだと、新規登録/編集画面でuser_idの値が入らず、データベースに保存されないので、保存時にuser_idが設定されるようにします。

```shell:app/Filament/Customer/Resource/BoardResource/Pages/CreateBoard.php(抜粋)
use Illuminate\Support\Facades\Auth;
class CreateBoard extends CreateRecord
{
    protected static string $resource = BoardResource::class;
    protected function mutateFormDataBeforeCreate(array $data): array
    {
        $data['user_id'] = Auth::id();
        return $data;
    }
}
```


```shell:app/Filament/Customer/Resource/BoardResource/Pages/EditBoard.php(抜粋)
use Illuminate\Support\Facades\Auth;
class EditBoard extends EditRecord
{
    protected static string $resource = BoardResource::class;
    protected function getHeaderActions(): array
    {
        return [
            Actions\DeleteAction::make(),
        ];
    }
    protected function mutateFormDataBeforeFill(array $data): array
    {
        $data['user_id'] = Auth::id();
        return $data;
    }
```

### 3.3 一覧画面で自身のデータのみにする設定
現在の状態だと一覧画面で自分以外のデータも表示されてしまうので、自分のデータしかアクセスできないよう設定します。
```shell:app/Models/Board.php(抜粋)
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Auth;
class Board extends Model
{
    protected static function booted(): void
    {
        static::addGlobalScope('team', function (Builder $query) {
            if (Auth::hasUser()) {
                $query->where('user_id', Auth::user()->id);
            }
        });
    }
```
上の設定をすると管理者用パネルで問題が起こります。
管理者パネルでは全てのレコードを表示対象にしたいのに、そのようにならなくなります。
そのため管理者パネルでは、モデルに設定されたグローバルスコープを除外するようにします。
```shell:app/Filament/Resource/BoardResource.php(抜粋)
    public static function table(Table $table): Table
    {
        return $table
            ->modifyQueryUsing(fn(Builder $query) => $query->withoutGlobalScopes())
            ->columns([
                Tables\Columns\TextColumn::make('user.name')
                    ->numeric()
                    ->sortable(),
```

ただし、これだと管理者パネルのBoards一覧画面で全てのレコードが表示されますが、ユーザーが作成したレコードに対して編集を行おうとしても編集画面が開きません。

$tableに対してmodifyQueryUsing()を設定するのを元に戻して、BoardResourceに以下の設定をします。
```shell:app/Filament/Resource/BoardResource.php(抜粋)
class BoardResource extends Resource
{
　　・・・
    public static function getEloquentQuery(): Builder
    {
        return parent::getEloquentQuery()->withoutGlobalScopes();
    }
```

以上で、基本的なところは使えそうなユーザー画面になりました。

:::note alert
+ 認可・非認可データの制御やセキュリティ対策は自己責任でお願いします。
:::