I api responses misc methods

This commit is contained in:
Fabian Wagner 2026-04-28 08:06:04 +02:00
parent e2c663522c
commit aa51e0aa27
1 changed files with 137 additions and 0 deletions

View File

@ -114,6 +114,143 @@ class MiscService
return $payload;
}
/**
* Available content languages for the running app.
*
* Tries (in order):
* 1. config('languages.languages') Blax convention, list of {code, ...}
* 2. config('app.available_locales') plain array of codes
* 3. fall back to [app()->getLocale()]
*
* @return array<int, string>
*/
public static function availableLanguages(): array
{
$configured = config('languages.languages');
if (is_array($configured) && $configured) {
return collect($configured)
->map(fn($l) => is_array($l) ? ($l['code'] ?? $l['lang'] ?? null) : $l)
->filter()
->values()
->toArray();
}
$locales = config('app.available_locales');
if (is_array($locales) && $locales) {
return array_values($locales);
}
return [app()->getLocale()];
}
/**
* Standard meta block for an API response.
*
* Every api response in the workkit-shaped envelope carries this block
* so consumers always know:
* - which URL produced the payload (`url`)
* - which locale they got back (`locale`)
* - which other locales the same resource is available in (`languages`)
*
* Pagination keys (current_page, total, total_pages, etc.) are merged in
* by `apiPaginated()`; `apiItem()` / `apiCollection()` skip them.
*/
public static function apiMeta(array $extra = []): array
{
return array_merge([
'url' => optional(request())->fullUrl(),
'locale' => app()->getLocale(),
'languages' => self::availableLanguages(),
], $extra);
}
/**
* Paginated API envelope. Use for any list/index endpoint.
*
* Returns:
* {
* "data": [...resource collection...],
* "meta": {
* "url", "locale", "languages",
* "current_page", "per_page", "from", "to",
* "total", "total_pages", "has_more"
* }
* }
*/
public static function apiPaginated(
$paginated,
string $resource_class,
array $extraMeta = []
): array {
$arr = method_exists($paginated, 'toArray') ? $paginated->toArray() : [];
$current = $arr['current_page'] ?? 1;
$last = $arr['last_page'] ?? null;
$pagination = [
'current_page' => $current,
'per_page' => $arr['per_page'] ?? null,
'from' => $arr['from'] ?? null,
'to' => $arr['to'] ?? null,
'total' => $arr['total'] ?? null,
'total_pages' => $last,
'has_more' => ($last !== null) ? ($current < $last) : false,
];
return [
'data' => $resource_class::collection($paginated),
'meta' => self::apiMeta(array_merge($pagination, $extraMeta)),
];
}
/**
* Single-item API envelope. Use for any show endpoint.
*/
public static function apiItem(
$item,
?string $resource_class = null,
array $extraMeta = []
): array {
return [
'data' => $resource_class ? $resource_class::make($item) : $item,
'meta' => self::apiMeta($extraMeta),
];
}
/**
* Non-paginated collection envelope. Use only when pagination is
* impractical (tiny fixed list like an enum or a child collection of a
* parent show response). Most list endpoints should use apiPaginated().
*/
public static function apiCollection(
$items,
string $resource_class,
array $extraMeta = []
): array {
$count = is_countable($items) ? count($items) : null;
return [
'data' => $resource_class::collection($items),
'meta' => self::apiMeta(array_merge([
'total' => $count,
], $extraMeta)),
];
}
/**
* Plain envelope (data + meta) for arbitrary payloads login responses,
* action acknowledgements, etc. Always carries the standard apiMeta block.
*/
public static function apiResponse(
mixed $data = null,
array $extraMeta = []
): array {
return [
'data' => $data,
'meta' => self::apiMeta($extraMeta),
];
}
public static function bytesToHuman($bytes)
{
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];