feat: add app and database modules
This commit is contained in:
@@ -0,0 +1,367 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file is strictly prohibited without prior written
|
||||
* permission from the author.
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\AccessControl;
|
||||
|
||||
use App\Models\Permission;
|
||||
use App\Support\DataTable;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PermissionManagementController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
// Middleware handled in web.php
|
||||
}
|
||||
|
||||
// show all permissions with relations
|
||||
public function index(Request $request)
|
||||
{
|
||||
if (DataTable::isDataTableRequest($request)) {
|
||||
return $this->dataTable($request);
|
||||
}
|
||||
|
||||
// render view with data
|
||||
return view('pages.access_control.permissions');
|
||||
}
|
||||
|
||||
protected function dataTable(Request $request)
|
||||
{
|
||||
try {
|
||||
$query = Permission::query()->with(['roles:id,name', 'creator:id,email', 'updater:id,email']);
|
||||
$recordsTotal = Permission::count();
|
||||
$globalSearch = DataTable::globalSearch($request);
|
||||
|
||||
$authUser = $request->user();
|
||||
$canManage = $authUser->can('manage access rights');
|
||||
|
||||
if ($canManage) {
|
||||
$status = DataTable::columnSearch($request, 0);
|
||||
$name = DataTable::columnSearch($request, 1);
|
||||
$guard = DataTable::columnSearch($request, 2);
|
||||
$role = DataTable::columnSearch($request, 3);
|
||||
} else {
|
||||
$status = null;
|
||||
$name = DataTable::columnSearch($request, 0);
|
||||
$guard = DataTable::columnSearch($request, 1);
|
||||
$role = DataTable::columnSearch($request, 2);
|
||||
}
|
||||
|
||||
if ($status) {
|
||||
$query->where('is_active', $status === 'active');
|
||||
}
|
||||
|
||||
if ($name) {
|
||||
$query->where('name', 'like', "%{$name}%");
|
||||
}
|
||||
|
||||
if ($guard) {
|
||||
$query->where('guard_name', 'like', "%{$guard}%");
|
||||
}
|
||||
|
||||
if ($role) {
|
||||
$query->whereHas('roles', function ($roleQuery) use ($role) {
|
||||
$roleQuery->where('name', 'like', "%{$role}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($canManage) {
|
||||
$createdAt = DataTable::columnSearch($request, 4);
|
||||
$createdBy = DataTable::columnSearch($request, 5);
|
||||
$updatedAt = DataTable::columnSearch($request, 6);
|
||||
$updatedBy = DataTable::columnSearch($request, 7);
|
||||
} else {
|
||||
$createdAt = DataTable::columnSearch($request, 3);
|
||||
$createdBy = DataTable::columnSearch($request, 4);
|
||||
$updatedAt = DataTable::columnSearch($request, 5);
|
||||
$updatedBy = DataTable::columnSearch($request, 6);
|
||||
}
|
||||
|
||||
if ($createdAt) {
|
||||
$query->whereDate('created_at', $createdAt);
|
||||
}
|
||||
|
||||
if ($createdBy) {
|
||||
$query->whereHas('creator', function ($creatorQuery) use ($createdBy) {
|
||||
$creatorQuery->where('email', 'like', "%{$createdBy}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($updatedAt) {
|
||||
$query->whereDate('updated_at', $updatedAt);
|
||||
}
|
||||
|
||||
if ($updatedBy) {
|
||||
$query->whereHas('updater', function ($updaterQuery) use ($updatedBy) {
|
||||
$updaterQuery->where('email', 'like', "%{$updatedBy}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($globalSearch) {
|
||||
$query->where(function ($searchQuery) use ($globalSearch) {
|
||||
$searchQuery
|
||||
->where('name', 'like', "%{$globalSearch}%")
|
||||
->orWhere('guard_name', 'like', "%{$globalSearch}%")
|
||||
->orWhereHas('roles', function ($roleQuery) use ($globalSearch) {
|
||||
$roleQuery->where('name', 'like', "%{$globalSearch}%");
|
||||
})
|
||||
->orWhereHas('creator', function ($creatorQuery) use ($globalSearch) {
|
||||
$creatorQuery->where('email', 'like', "%{$globalSearch}%");
|
||||
})
|
||||
->orWhereHas('updater', function ($updaterQuery) use ($globalSearch) {
|
||||
$updaterQuery->where('email', 'like', "%{$globalSearch}%");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Perform filtered count WITHOUT eager loading or ordering
|
||||
$countQuery = clone $query;
|
||||
$countQuery->setEagerLoads([]);
|
||||
$countQuery->orders = null;
|
||||
$recordsFiltered = $countQuery->count();
|
||||
[$orderIndex, $orderDirection] = DataTable::order($request, $canManage ? 4 : 3, 'desc');
|
||||
|
||||
$sortColumn = match (true) {
|
||||
$canManage && $orderIndex === 0 => 'is_active',
|
||||
($canManage && $orderIndex === 1) || (! $canManage && $orderIndex === 0) => 'name',
|
||||
($canManage && $orderIndex === 2) || (! $canManage && $orderIndex === 1) => 'guard_name',
|
||||
($canManage && $orderIndex === 4) || (! $canManage && $orderIndex === 3) => 'created_at',
|
||||
($canManage && $orderIndex === 6) || (! $canManage && $orderIndex === 5) => 'updated_at',
|
||||
default => 'created_at',
|
||||
};
|
||||
|
||||
$permissions = $query
|
||||
->orderBy($sortColumn, $orderDirection)
|
||||
->skip(DataTable::start($request))
|
||||
->take(DataTable::length($request))
|
||||
->get();
|
||||
|
||||
$rows = $permissions->map(function (Permission $permission) use ($canManage) {
|
||||
$row = [];
|
||||
|
||||
if ($canManage) {
|
||||
$row[] = sprintf(
|
||||
'<div class="form-check form-switch d-flex align-items-center gap-2"><input class="form-check-input permission-toggle" type="checkbox" %s data-id="%d" data-name="%s"><span class="status-label %s">%s</span></div>',
|
||||
$permission->is_active ? 'checked' : '',
|
||||
$permission->id,
|
||||
e($permission->name),
|
||||
$permission->is_active ? 'text-success' : 'text-danger',
|
||||
$permission->is_active ? 'Active' : 'Inactive'
|
||||
);
|
||||
}
|
||||
|
||||
$roleNames = $permission->roles->pluck('name');
|
||||
$roleBadges = $roleNames->isNotEmpty()
|
||||
? $roleNames->map(fn ($role) => '<span class="badge badge-slate-pill">'.e($role).'</span>')->implode(' ')
|
||||
: '-';
|
||||
|
||||
$row[] = e($permission->name);
|
||||
$row[] = e($permission->guard_name);
|
||||
$row[] = $roleBadges;
|
||||
$row[] = e(format_datetime($permission->created_at));
|
||||
$row[] = e($permission->creator->email ?? 'System');
|
||||
$row[] = e(format_datetime($permission->updated_at));
|
||||
$row[] = e($permission->updater->email ?? '-');
|
||||
|
||||
if ($canManage) {
|
||||
$row[] = '<div class="text-end">'
|
||||
.'<button class="btn btn-link btn-edit" data-id="'.$permission->id.'" data-name="'.e($permission->name).'" data-guard="'.e($permission->guard_name).'" data-created="'.e(format_datetime($permission->created_at)).'" data-created-by="'.e($permission->creator->email ?? 'System').'" data-updated-at="'.e(format_datetime($permission->updated_at)).'" data-updated-by="'.e($permission->updater->email ?? '-').'" data-bs-toggle="modal" data-bs-target="#editPermissionModal"><i class="bi bi-pencil"></i></button>'
|
||||
.'<button class="btn btn-link text-danger btn-delete" data-id="'.$permission->id.'" data-name="'.e($permission->name).'"><i class="bi bi-trash"></i></button>'
|
||||
.'</div>';
|
||||
}
|
||||
|
||||
return $row;
|
||||
})->all();
|
||||
|
||||
return DataTable::response($request, $recordsTotal, $recordsFiltered, $rows);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('DataTable Error [PermissionManagement]: '.$e->getMessage());
|
||||
|
||||
return DataTable::response($request, 0, 0, []);
|
||||
}
|
||||
}
|
||||
|
||||
// create new permission
|
||||
public function store(Request $request)
|
||||
{
|
||||
// validate input fields
|
||||
$request->validate([
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:3',
|
||||
'max:100',
|
||||
'regex:/^[a-zA-Z0-9_\-\.\/]+$/',
|
||||
Rule::unique('permissions', 'name')
|
||||
->where(fn ($query) => $query->where('guard_name', $request->guard_name)
|
||||
),
|
||||
],
|
||||
'guard_name' => [
|
||||
'required',
|
||||
'string',
|
||||
'in:web,api',
|
||||
],
|
||||
], [
|
||||
'name.required' => __('Permission name is required.'),
|
||||
'name.regex' => __('Permission name contains invalid characters.'),
|
||||
'name.unique' => __('A permission with this Name & Guard combination already exists.'),
|
||||
'guard_name.required' => __('Guard is required.'),
|
||||
'guard_name.in' => __('Invalid guard selected.'),
|
||||
]);
|
||||
|
||||
try {
|
||||
// save permission record
|
||||
Permission::create([
|
||||
'name' => $request->name,
|
||||
'guard_name' => $request->guard_name,
|
||||
'created_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
// success feedback
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => __('Permission has been successfully created.'),
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', __('Permission has been successfully created.'));
|
||||
|
||||
} catch (\Exception $e) {
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => __('Failed to create permission: ').$e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', __('Failed to create permission: ').$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// update permission by id
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
|
||||
// validate request input
|
||||
$request->validate([
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:3',
|
||||
'max:100',
|
||||
'regex:/^[a-zA-Z0-9_\-\.\/]+$/',
|
||||
Rule::unique('permissions', 'name')
|
||||
->where(fn ($query) => $query->where('guard_name', $request->guard_name)
|
||||
)
|
||||
->ignore($id),
|
||||
],
|
||||
'guard_name' => [
|
||||
'required',
|
||||
'string',
|
||||
'in:web,api',
|
||||
],
|
||||
], [
|
||||
'name.required' => __('Permission name is required.'),
|
||||
'name.regex' => __('Permission name contains invalid characters.'),
|
||||
'name.unique' => __('A permission with this Name & Guard combination is already registered.'),
|
||||
'guard_name.required' => __('Guard is required.'),
|
||||
'guard_name.in' => __('Invalid guard selected.'),
|
||||
]);
|
||||
|
||||
try {
|
||||
// find permission record
|
||||
$permission = Permission::findOrFail($id);
|
||||
|
||||
// update permission values
|
||||
$permission->update([
|
||||
'name' => $request->name,
|
||||
'guard_name' => $request->guard_name,
|
||||
'updated_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
// success response
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => __('Permission has been successfully updated.'),
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('success', __('Permission has been successfully updated.'));
|
||||
|
||||
} catch (QueryException $e) {
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => __('Failed to update permission: ').$e->getMessage(),
|
||||
], 500);
|
||||
}
|
||||
|
||||
return redirect()->back()->with('error', __('Failed to update permission: ').$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// toggle active or inactive status
|
||||
public function toggleStatus(Request $request)
|
||||
{
|
||||
// validate status request
|
||||
$request->validate([
|
||||
'id' => 'required|exists:permissions,id',
|
||||
'status' => 'required|in:activate,deactivate',
|
||||
]);
|
||||
|
||||
$permission = Permission::findOrFail($request->id);
|
||||
|
||||
$permission->update([
|
||||
'is_active' => $request->status === 'activate' ? 1 : 0,
|
||||
'updated_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
// respond to client (ajax)
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => __('Permission status updated successfully.'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$permission = Permission::findOrFail($id);
|
||||
$permission->delete(); // soft delete (not permanent)
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => __('Permission has been archived.'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user