laravelRequest = request(); $this->request = new CiRequestAdapter($this->laravelRequest); } protected function success($data = null, string $message = 'Success', int $code = Response::HTTP_OK): JsonResponse { return response()->json([ 'status' => true, 'message' => $message, 'data' => $data, ], $code); } protected function error(string $message = 'An error occurred', int $code = Response::HTTP_BAD_REQUEST, ?array $errors = null): JsonResponse { $payload = [ 'status' => false, 'message' => $message, ]; if (!empty($errors)) { $payload['errors'] = $errors; } return response()->json($payload, $code); } protected function respondSuccess($data = null, string $message = 'Success', int $code = Response::HTTP_OK): JsonResponse { return $this->success($data, $message, $code); } protected function respondCreated($data = null, string $message = 'Resource created successfully'): JsonResponse { return $this->success($data, $message, Response::HTTP_CREATED); } protected function respondDeleted($data = null, string $message = 'Resource deleted successfully'): JsonResponse { return $this->success($data, $message, Response::HTTP_OK); } protected function respondError(string $message = 'An error occurred', int $code = Response::HTTP_BAD_REQUEST): JsonResponse { return $this->error($message, $code); } protected function respondValidationError(array $errors, string $message = 'Validation failed'): JsonResponse { return $this->error($message, Response::HTTP_UNPROCESSABLE_ENTITY, $errors); } protected function validateRequest(array $data, array $rules): array { $normalized = []; foreach ($rules as $field => $ruleSet) { $normalized[$field] = $this->normalizeRules($ruleSet); } $validator = Validator::make($data, $normalized); $this->validator = $validator; if ($validator->fails()) { return $validator->errors()->toArray(); } return []; } protected function validate(array $rules): bool { $payload = $this->payloadData(); $errors = $this->validateRequest($payload, $rules); return empty($errors); } protected function payloadData(): array { $json = json_decode($this->laravelRequest->getContent() ?: '', true); if (is_array($json)) { return $json; } return array_merge($this->laravelRequest->query->all(), $this->laravelRequest->request->all()); } protected function normalizeRules(array|string $ruleSet): array|string { if (is_array($ruleSet)) { return $ruleSet; } $parts = explode('|', $ruleSet); $result = []; foreach ($parts as $part) { $part = trim($part); if ($part === '') { continue; } if (preg_match('/^max_length\\[(\\d+)\\]$/', $part, $m)) { $result[] = 'max:' . $m[1]; continue; } if (preg_match('/^min_length\\[(\\d+)\\]$/', $part, $m)) { $result[] = 'min:' . $m[1]; continue; } if (preg_match('/^in_list\\[(.+)\\]$/', $part, $m)) { $result[] = 'in:' . $m[1]; continue; } if (preg_match('/^greater_than_equal_to\\[(.+)\\]$/', $part, $m)) { $result[] = 'gte:' . $m[1]; continue; } if (preg_match('/^less_than_equal_to\\[(.+)\\]$/', $part, $m)) { $result[] = 'lte:' . $m[1]; continue; } if (preg_match('/^valid_date\\[(.+)\\]$/', $part, $m)) { $result[] = 'date_format:' . $m[1]; continue; } if ($part === 'valid_email') { $result[] = 'email'; continue; } if ($part === 'permit_empty') { $result[] = 'nullable'; continue; } if (preg_match('/^is_unique\\[([^\\.]+)\\.([^\\]]+)\\]$/', $part, $m)) { $result[] = 'unique:' . $m[1] . ',' . $m[2]; continue; } if ($part === 'string') { $result[] = 'string'; continue; } if ($part === 'integer') { $result[] = 'integer'; continue; } if ($part === 'numeric') { $result[] = 'numeric'; continue; } if ($part === 'required') { $result[] = 'required'; continue; } if ($part === 'alpha_numeric') { $result[] = 'alpha_num'; continue; } if ($part === 'in_array') { $result[] = 'in_array'; continue; } if ($part === 'valid_url') { $result[] = 'url'; continue; } if (strpos($part, 'max_size') === 0) { $result[] = str_replace('max_size', 'max', $part); continue; } $result[] = $part; } return $result; } protected function paginate($source, int $page = 1, int $perPage = 20): array { $page = max(1, $page); $perPage = max(1, $perPage); $offset = ($page - 1) * $perPage; if ($source instanceof EloquentBuilder || $source instanceof \Illuminate\Database\Query\Builder) { $total = $source->toBase()->getCountForPagination(); $items = $source->offset($offset)->limit($perPage)->get()->toArray(); } elseif (is_array($source)) { $total = count($source); $items = array_slice($source, $offset, $perPage); } else { return [ 'data' => [], 'pagination' => [ 'current_page' => $page, 'per_page' => $perPage, 'total' => 0, 'total_pages' => 0, ], ]; } return [ 'data' => array_values($items), 'pagination' => [ 'current_page' => $page, 'per_page' => $perPage, 'total' => $total, 'total_pages' => (int) ceil($total / $perPage), ], ]; } protected function getCurrentUserId(): ?int { $userId = (int) (session()->get('user_id') ?? 0); return $userId > 0 ? $userId : null; } protected function getCurrentUser(): ?object { $userId = $this->getCurrentUserId(); if (!$userId) { return null; } $user = app(User::class); $row = $user->find($userId); if (!$row) { return null; } $name = trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? '')); if ($name === '') { $name = $row['email'] ?? 'User #' . $userId; } $roles = (array) (session()->get('roles') ?? []); if (empty($roles)) { try { $roles = DB::table('user_roles ur') ->select('LOWER(r.name) AS name') ->join('roles r', 'r.id', '=', 'ur.role_id') ->where('ur.user_id', $userId) ->whereNull('ur.deleted_at') ->pluck('name') ->map(fn($name) => strtolower((string) $name)) ->unique() ->values() ->toArray(); } catch (\Throwable $e) { Log::error('Unable to load user roles: ' . $e->getMessage()); $roles = []; } } return (object) [ 'id' => (int) $userId, 'name' => $name, 'email' => $row['email'] ?? null, 'roles' => $roles, ]; } }