Files
sembara/app/Services/InsForge/InsForgeTableService.php

151 lines
4.2 KiB
PHP

<?php
namespace App\Services\InsForge;
use Illuminate\Pagination\LengthAwarePaginator;
abstract class InsForgeTableService
{
public function __construct(protected readonly InsForgeClient $client) {}
abstract protected function table(): string;
/** @return list<string> */
abstract protected function searchableColumns(): array;
abstract protected function sortableColumns(): array;
protected function defaultSortColumn(): string
{
return 'created_at';
}
protected function defaultSortDirection(): string
{
return 'desc';
}
protected function select(): string
{
return '*';
}
/** @param array<string, mixed> $filters column => value (eq) */
public function paginate(
?string $search = null,
?string $sortColumn = null,
?string $sortDirection = null,
int $page = 1,
int $perPage = 25,
array $filters = [],
): LengthAwarePaginator {
$query = [
'select' => $this->select(),
'limit' => max(1, min($perPage, 100)),
'offset' => ($page - 1) * $perPage,
'order' => sprintf(
'%s.%s.nullslast',
in_array($sortColumn, $this->sortableColumns(), true) ? $sortColumn : $this->defaultSortColumn(),
strtolower($sortDirection ?? '') === 'asc' ? 'asc' : $this->defaultSortDirection(),
),
];
foreach ($filters as $column => $value) {
if ($value !== null && $value !== '') {
$query[$column] = 'eq.'.$value;
}
}
if (filled($search) && $this->searchableColumns() !== []) {
$terms = str_replace(['(', ')', ','], ' ', $search);
$clauses = array_map(
fn (string $column): string => sprintf('%s.ilike.*%s*', $column, trim($terms)),
$this->searchableColumns(),
);
$query['or'] = '('.implode(',', $clauses).')';
}
[$client, $asUser] = $this->scoped();
$response = $client->send('GET', '/api/database/records/'.$this->table(), query: $query, asUser: $asUser, headers: ['Prefer' => 'count=exact']);
$rows = $response->json() ?? [];
$total = $this->parseTotal($response->header('Content-Range')) ?? count($rows);
return new LengthAwarePaginator(
items: $rows,
total: $total,
perPage: $perPage,
currentPage: $page,
);
}
public function find(string $id): ?array
{
[$client, $asUser] = $this->scoped();
return $client->getRecord($this->table(), $id, asUser: $asUser);
}
/** @param array<string, mixed> $filters */
public function findBy(array $filters): ?array
{
[$client, $asUser] = $this->scoped();
$params = ['limit' => 1];
foreach ($filters as $column => $value) {
$params[$column] = 'eq.'.$value;
}
return $client->listRecords($this->table(), $params, asUser: $asUser)[0] ?? null;
}
public function create(array $data): void
{
[$client, $asUser] = $this->scoped();
$client->insertRecord($this->table(), [$data], asUser: $asUser);
}
public function update(string $id, array $data): void
{
[$client, $asUser] = $this->scoped();
$client->updateRecords($this->table(), ['id' => $id], $data, asUser: $asUser);
}
public function delete(string $id): void
{
[$client, $asUser] = $this->scoped();
$client->deleteRecords($this->table(), ['id' => $id], asUser: $asUser);
}
/**
* Pakai JWT user saat ada sesi (RLS aktif); jatuh ke admin key untuk konteks tanpa sesi.
*
* @return array{0: InsForgeClient, 1: bool}
*/
protected function scoped(): array
{
$token = session(InsForgeAuthService::SESSION_ACCESS_TOKEN);
return [
$token ? $this->client->withToken($token) : $this->client,
filled($token),
];
}
private function parseTotal(?string $contentRange): ?int
{
if (! filled($contentRange)) {
return null;
}
$total = explode('/', $contentRange)[1] ?? '';
return ctype_digit($total) ? (int) $total : null;
}
}