I'm developing an app that has several types of users: Admins, Clients and Members (3 separate panels). All users have a base record in App\Models\Users and log-in using the same form, but then have extended model and are redirected to the correct Panel based on the "role" added to the users table ("admin","client","member"). I am using a "safeid" field in all the tables as a foreign_id linking the base users table (because the incremental id in users will not match the incremental ids of the other tables).
This is working, in the Resources (List) page it is displaying the combined model data in the columns. The problem/issue is the edit form will only show editable data from the extended model (not the base user fields).
I'm wondering if maybe extending Model\User is not the best way to achieve my goal. Just getting back to Laravel (created a basic app two years ago). There are so many posts and videos regarding what I am trying to do, but I cannot find a definitive answer/example of what I am trying to achieve in Filament using extended models. Here's some code:
App\Model|User
<?php
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Filament\Models\Contracts\FilamentUser;
use Filament\Panel;
use Illuminate\Support\Str;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable implements MustVerifyEmail, FilamentUser
{
use HasFactory;
use Notifiable;
use HasRoles;
use Notifiable;
use TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'role',
'safeid',
'last_login_at',
'last_login_ip',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
protected static function booted(): void
{
static::creating(function (User $user) {
$user->safeid = (string) Str::ulid();
});
self::updated(static function (User $user): void {
$user->updated_at = Carbon::now()->toDateTimeString();
});
}
public function canAccessPanel(Panel $panel): bool
{
if ($this->role === $panel->getId()) {
return true;
}
return false;
}
/**
* Get the admin associated with the user.
*/
public function admin(): BelongsTo
{
return $this->BelongsTo(Admin::class, 'safeid', 'safeid');
}
}
App\Models\Admin
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Admin extends User
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'phone',
];
public function __construct(array $attributes = array())
{
$this->fillable(array_merge($this->getFillable(), ['name', 'email']));
parent::__construct($attributes);
}
public function user(): HasOne
{
return $this->hasOne(User::class, 'safeid', 'user_safeid');
}
}
Filament\Resources\AdminResource
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\AdminResource\Pages;
use App\Filament\Resources\AdminResource\RelationManagers;
use App\Models\Admin;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Forms\Components\TextInput;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class AdminResource extends Resource
{
protected static ?string $model = Admin::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
public static function form(Form $form): Form
{
return $form
->schema([
TextInput::make('user.name')
->required()
->label('Name'),
TextInput::make('user..email')
->required()
->email()
->label('Email'),
TextInput::make('phone')
->required()
->label('Phone'),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('user.name'),
Tables\Columns\TextColumn::make('user.email'),
Tables\Columns\TextColumn::make('phone'),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListAdmins::route('/'),
'create' => Pages\CreateAdmin::route('/create'),
'edit' => Pages\EditAdmin::route('/{record}/edit'),
];
}
}
Filament\Resources\AdminResource\Pages\EditAdmin
<?php
namespace App\Filament\Resources\AdminResource\Pages;
use App\Filament\Resources\AdminResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditAdmin extends EditRecord
{
protected static string $resource = AdminResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
protected function mutateFormDataBeforeFill(array $data): array
{
return $data;
}
}