278 lines
8.7 KiB
PHP
Executable File
278 lines
8.7 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Controllers\Api\CiRequestAdapter;
|
|
use App\Models\User;
|
|
use Illuminate\Contracts\Validation\Validator as ValidatorContract;
|
|
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class BaseApiController extends Controller
|
|
{
|
|
protected Request $laravelRequest;
|
|
protected CiRequestAdapter $request;
|
|
protected ValidatorContract|null $validator = null;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->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) (auth()->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;
|
|
}
|
|
|
|
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,
|
|
];
|
|
}
|
|
}
|