feat(admin): add admin creation with permissions

This commit is contained in:
2026-08-19 18:23:21 +03:30
parent 407a3e1bec
commit 9d5e325c3f
20 changed files with 2324 additions and 75 deletions
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace App\Enums\Admin;
enum AdminPermission:string
{
case MANAGE_ADMINS = 'manage-admins';
case MANAGE_TOURS = 'manage-tours';
case MANAGE_FLIGHTS = 'manage-flights';
case MANAGE_COMMENTS = 'manage-comments';
case MANAGE_USERS = 'manage-users';
case MANAGE_SETTINGS = 'manage-settings';
public function label(): string
{
return match($this) {
self::MANAGE_ADMINS => 'مدیریت مدیران',
self::MANAGE_TOURS => 'مدیریت تورها',
self::MANAGE_FLIGHTS => 'مدیریت پروازها',
self::MANAGE_COMMENTS => 'مدیریت نظرات',
self::MANAGE_USERS => 'مدیریت کاربران',
self::MANAGE_SETTINGS => 'تنظیمات سایت',
};
}
}
@@ -0,0 +1,199 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\AdminStoreRequest;
use App\Models\Admin;
use App\Models\AdminPermission;
use App\Models\File;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
class AdminController extends Controller
{
public function index(Request $request)
{
$title = 'لیست مدیران';
$admins = Admin::query()
->when($request->filled('search'), function (Builder $query) use ($request) {
$search = $request->input('search');
$query->whereAny([
'first_name',
'last_name',
'username',
'email'
], 'LIKE', "%$search%");
})
->when($request->filled('sort'), function (Builder $query) use ($request) {
$sort = $request->input('sort');
switch ($sort) {
case 'name_desc':
{
$query
->orderByDesc('first_name');
break;
}
case 'email':
{
$query
->orderBy('email');
break;
}
case 'newest':
{
$query
->orderByDesc('created_at');
break;
}
case 'name_asc':
default:
{
$query
->orderBy('first_name');
}
}
}, function (Builder $query) {
$query->orderBy('first_name');
})
->paginate()
->withQueryString();
return view('admin.admins.index', compact('title', 'admins'));
}
public function edit(Admin $admin)
{
return view('admin.admins.edit', compact('admin'));
}
public function create()
{
$title = 'ایجاد کاربر جدید';
return view('admin.admins.create');
}
public function store(AdminStoreRequest $request)
{
$data = $request->validated();
$avatarPath = null;
DB::beginTransaction();
try {
$admin = Admin::create([
'first_name' => $data['first_name'],
'last_name' => $data['last_name'],
'username' => $data['username'],
'email' => $data['email'] ?? null,
'mobile' => $data['mobile'] ?? null,
'password' => Hash::make($data['password']),
'role' => $data['role'],
'status' => $data['status'],
]);
if ($request->hasFile('image')) {
$image = $request->file('image');
$imageName = $admin->id
. '_'
. time()
. '_'
. rand(11111, 99999)
. '.'
. $image->extension();
$avatarPath = $image->storeAs(
'admin_avatars',
$imageName,
'public'
);
$file = File::create([
'name' => $imageName,
'size' => $image->getSize(),
'original_name' => $image->getClientOriginalName(),
'path' => $avatarPath,
'extension' => $image->extension(),
]);
// AdminAvatar::create([
// 'admin_id' => $admin->id,
// 'file_id' => $file->id,
// ]);
}
foreach ($data['permissions'] as $permission) {
AdminPermission::create([
'admin_id' => $admin->id,
'permission' => $permission,
]);
}
DB::commit();
return redirect()
->route('admin.admins.index')
->with('success', 'مدیر جدید با موفقیت ایجاد شد.');
} catch (\Throwable $exception) {
DB::rollBack();
if ($avatarPath) {
Storage::disk('public')->delete($avatarPath);
}
Log::error('خطا در ایجاد مدیر جدید', [
'message' => $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
]);
return back()
->withInput(
$request->except([
'password',
'password_confirmation',
'image',
])
)
->with('error', 'خطایی هنگام ایجاد مدیر رخ داد.');
}
}
public function destroy()
{
}
public function trash()
{
}
public function restore()
{
}
public function forceDelete()
{
}
}
@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware\Admin;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckAdminPermission
{
/**
* Handle an incoming request.
*
* @param Closure(Request): (Response) $next
*/
public function handle(Request $request, Closure $next, string $permission)
{
$admin = auth('admin')->user();
if (! auth('admin')->user()?->hasPermission($permission)) {
abort(403);
}
return $next($request);
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Http\Middleware\Admin;
use App\Enums\Admin\AdminRole;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckSuperAdmin
{
/**
* Handle an incoming request.
*
* @param Closure(Request): (Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if (auth('admin')->user()?->role !== AdminRole::SUPERADMIN) {
abort(403);
}
return $next($request);
}
}
@@ -0,0 +1,92 @@
<?php
namespace App\Http\Requests\Admin;
use App\Enums\Admin\AdminPermission;
use App\Enums\Admin\AdminRole;
use App\Enums\Admin\AdminStatus;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
class AdminStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'first_name' => [
'required',
'string',
'min:2',
'max:64',
'persian_alpha'
],
'last_name' => [
'required',
'string',
'min:2',
'max:64',
'persian_alpha'
],
'username' => [
'required',
'string',
'min:2',
'max:100',
'regex:/^[A-Za-z0-9_]+$/',
'unique:admins,username'
],
'email' => [
'nullable',
'email',
'max:150',
'unique:users,email'
],
'mobile' => [
'nullable',
'string',
'ir_mobile:zero'
],
'password' => [
'required',
'min:8',
'max:255',
'alpha_num',
],
'role' => [
'required',
Rule::enum(AdminRole::class),
],
'status' => [
'required',
Rule::enum(AdminStatus::class),
],
'permissions' => [
'nullable',
'array',
'min:1',
],
'permissions.*' => [
'required',
'string',
'distinct',
Rule::enum(AdminPermission::class),
],
];
}
}
+18 -2
View File
@@ -6,6 +6,8 @@
namespace App\Models;
use App\Enums\Admin\AdminRole;
use App\Enums\Admin\AdminStatus;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\SoftDeletes;
@@ -42,8 +44,8 @@ class Admin extends User
protected $casts = [
'file_id' => 'int',
'role' => 'int',
'status' => 'int',
'role' => AdminRole::class,
'status' => AdminStatus::class,
'last_login_at' => 'datetime'
];
@@ -73,4 +75,18 @@ class Admin extends User
{
return $this->hasMany(Comment::class);
}
public function permissions()
{
return $this->hasMany(AdminPermission::class);
}
public function hasPermission(string $permission): bool
{
if ($this->role === AdminRole::SUPERADMIN) {
return true;
}
return $this->permissions()
->where('permission', $permission)
->exists();
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
/**
* Created by Reliese Model.
*/
namespace App\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
/**
* Class AdminPermission
*
* @property int $id
* @property int $admin_id
* @property string $permission
* @property Carbon|null $created_at
* @property Carbon|null $updated_at
*
* @package App\Models
*/
class AdminPermission extends Model
{
protected $table = 'admin_permissions';
public static $snakeAttributes = false;
protected $casts = [
'admin_id' => 'int'
];
protected $fillable = [
'admin_id',
'permission'
];
public function admin()
{
return $this->belongsTo(Admin::class);
}
}