ifnex/04_Laravel/app/Filament/Resources/UserResource.php
Kazem Alghasi a0bc871c8d feat(auth): implement role-based access control using spatie/laravel-permission
Integrate Spatie Laravel Permission to replace the legacy role system.
This includes:
- Adding `spatie/laravel-permission` dependency.
- Implementing `Role` and `User` model updates with `HasRoles` trait.
- Adding migrations for permission and role tables.
- Creating `RoleResource` and `UserResource` for Filament administration.
- Adding a `RoleAndPermissionSeeder` for initial setup.
- Updating `User` model helper methods to utilize role checks.
2026-08-10 02:44:23 +03:30

270 lines
11 KiB
PHP

<?php
namespace App\Filament\Resources;
use App\Filament\Resources\UserResource\Pages;
use App\Models\User;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Tables\Actions\Action;
use Filament\Notifications\Notification;
use Illuminate\Support\Facades\Hash;
use Spatie\Permission\Models\Role;
use Illuminate\Support\Facades\Auth;
class UserResource extends Resource
{
protected static ?string $model = User::class;
protected static ?string $navigationIcon = 'heroicon-o-users';
protected static ?string $navigationLabel = 'کاربران';
protected static ?string $modelLabel = 'کاربر';
protected static ?string $pluralModelLabel = 'کاربران';
protected static ?string $navigationGroup = 'تنظیمات سیستم';
protected static ?int $navigationSort = 1;
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Section::make('اطلاعات اصلی')
->schema([
Forms\Components\TextInput::make('name')
->label('نام و نام خانوادگی')
->required()
->maxLength(255),
Forms\Components\TextInput::make('email')
->label('ایمیل')
->email()
->required()
->unique(ignoreRecord: true)
->maxLength(255),
Forms\Components\TextInput::make('phone')
->label('شماره موبایل')
->tel()
->maxLength(20),
Forms\Components\TextInput::make('password')
->label('رمز عبور')
->password()
->revealable()
->required(fn (string $operation): bool => $operation === 'create')
->dehydrated(fn ($state): bool => filled($state))
->dehydrateStateUsing(fn ($state): string => Hash::make($state))
->helperText('برای تغییر رمز عبور، رمز جدید را وارد کنید. در غیر این صورت خالی بگذارید.'),
])->columns(2),
Forms\Components\Section::make('نقش و دسترسی‌ها')
->schema([
Forms\Components\Select::make('roles')
->label('نقش‌های کاربر')
->relationship('roles', 'name')
->multiple()
->preload()
->searchable()
->required()
->minItems(1)
->helperText('حداقل یک نقش باید انتخاب شود'),
Forms\Components\Toggle::make('is_active')
->label('وضعیت فعال')
->default(true)
->helperText('کاربران غیرفعال نمی‌توانند وارد سیستم شوند'),
]),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('id')
->label('شناسه')
->sortable()
->searchable()
->toggleable(isToggledHiddenByDefault: true),
Tables\Columns\TextColumn::make('name')
->label('نام')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('email')
->label('ایمیل')
->searchable()
->sortable()
->copyable()
->copyMessage('ایمیل کپی شد')
->copyMessageDuration(1500),
Tables\Columns\TextColumn::make('phone')
->label('موبایل')
->searchable()
->toggleable(),
Tables\Columns\TextColumn::make('roles.name')
->label('نقش‌ها')
->badge()
->color(fn (string $state): string => match ($state) {
'super_admin' => 'danger',
'admin' => 'warning',
'staff' => 'info',
'customer' => 'success',
default => 'gray',
})
->separator('، '),
Tables\Columns\IconColumn::make('is_active')
->label('فعال')
->boolean()
->sortable()
->trueIcon('heroicon-o-check-circle')
->falseIcon('heroicon-o-x-circle')
->trueColor('success')
->falseColor('danger'),
Tables\Columns\TextColumn::make('created_at')
->label('تاریخ عضویت')
->dateTime('Y/m/d')
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
Tables\Filters\SelectFilter::make('role')
->label('نقش')
->options([
'super_admin' => 'مدیر ارشد',
'admin' => 'مدیر',
'staff' => 'کارمند',
'customer' => 'مشتری',
])
->query(function ($query, array $data) {
return $query->when($data['value'], function ($query, $role) {
return $query->role($role);
});
}),
Tables\Filters\TernaryFilter::make('is_active')
->label('وضعیت فعال'),
])
->actions([
Tables\Actions\EditAction::make()
->label('ویرایش'),
Action::make('toggleActive')
->label(fn ($record) => $record->is_active ? 'غیرفعال کردن' : 'فعال کردن')
->icon(fn ($record) => $record->is_active ? 'heroicon-o-x-circle' : 'heroicon-o-check-circle')
->color(fn ($record) => $record->is_active ? 'danger' : 'success')
->requiresConfirmation()
->action(function (User $record) {
$record->update(['is_active' => !$record->is_active]);
Notification::make()
->title($record->is_active ? 'کاربر فعال شد' : 'کاربر غیرفعال شد')
->success()
->send();
}),
Action::make('resetPassword')
->label('بازنشانی رمز')
->icon('heroicon-o-key')
->color('warning')
->form([
Forms\Components\TextInput::make('password')
->label('رمز عبور جدید')
->password()
->revealable()
->required()
->minLength(8),
])
->requiresConfirmation()
->action(function (User $record, array $data) {
$record->update([
'password' => Hash::make($data['password']),
]);
Notification::make()
->title('رمز عبور با موفقیت بازنشانی شد')
->success()
->send();
}),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make()
->label('حذف انتخاب‌شده‌ها'),
]),
])
->defaultSort('created_at', 'desc');
}
public static function getRelations(): array
{
return [];
}
public static function getPages(): array
{
return [
'index' => Pages\ListUsers::route('/'),
'create' => Pages\CreateUser::route('/create'),
'edit' => Pages\EditUser::route('/{record}/edit'),
];
}
// ─── Access Control ───
protected static function getCurrentUser(): ?\App\Models\User
{
$user = Auth::user();
return $user instanceof \App\Models\User ? $user : null;
}
// فقط super_admin و admin می‌توانند کاربران را ببینند
public static function canViewAny(): bool
{
$user = static::getCurrentUser();
return $user !== null && $user->hasAnyRole(['super_admin', 'admin']);
}
// فقط super_admin می‌تواند کاربر جدید بسازد
public static function canCreate(): bool
{
$user = static::getCurrentUser();
return $user !== null && $user->hasRole('super_admin');
}
// super_admin و admin می‌توانند ویرایش کنند
public static function canEdit($record): bool
{
$user = static::getCurrentUser();
if ($user === null) return false;
// admin نمی‌تواند super_admin را ویرایش کند
if ($user->hasRole('admin') && $record->hasRole('super_admin')) {
return false;
}
return $user->hasAnyRole(['super_admin', 'admin']);
}
// فقط super_admin می‌تواند کاربر حذف کند
public static function canDelete($record): bool
{
$user = static::getCurrentUser();
if ($user === null) return false;
// super_admin نمی‌تواند خودش را حذف کند
if ($record->id === $user->id) {
return false;
}
return $user->hasRole('super_admin');
}
}