Initial commit: Pireon Admin — headless admin panel and public API
Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer collective, backed by the Pireon OAuth identity server. The public site is a separate, server-rendered application consuming /api/public/v1. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
71
app/Support/ImagePayload.php
Normal file
71
app/Support/ImagePayload.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
use App\Models\Media;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* The shape every image takes in the API.
|
||||
*
|
||||
* A bare path is not enough for a server-rendered frontend: without width and
|
||||
* height the page reflows as images arrive, without a `srcset` every visitor
|
||||
* downloads the full-size original, and without alt text the page is not
|
||||
* accessible. All of that is known here and nowhere else, so all of it is
|
||||
* sent rather than left for the frontend to guess.
|
||||
*
|
||||
* Degrades on purpose: an image whose derived versions are not ready yet still
|
||||
* comes back with a URL, so the page renders a plain `<img>` instead of a gap.
|
||||
*/
|
||||
final class ImagePayload
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
public static function make(?string $path, ?Media $media, ?string $alt = null): ?array
|
||||
{
|
||||
if (blank($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'url' => $media?->url() ?? self::url($path),
|
||||
|
||||
// Empty rather than absent: an image with no description is
|
||||
// decorative, and `alt=""` is how you say that.
|
||||
'alt' => (string) ($alt ?? ''),
|
||||
|
||||
'width' => $media?->width,
|
||||
'height' => $media?->height,
|
||||
'aspect_ratio' => $media?->aspectRatio(),
|
||||
'mime' => $media?->mime,
|
||||
|
||||
// Paint something in the right colours while the real image loads.
|
||||
'placeholder' => $media?->placeholder,
|
||||
|
||||
// Keyed by format so the frontend can build a <picture> with AVIF
|
||||
// first and WebP behind it. Only widths that actually exist are
|
||||
// listed: offering one that does not would put a broken image on
|
||||
// the page.
|
||||
'srcset' => $media?->srcset() ?: null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* URL for a path with no media row yet — an upload processed a moment ago,
|
||||
* or one whose row was pruned.
|
||||
*/
|
||||
protected static function url(string $path): string
|
||||
{
|
||||
$url = Storage::disk((string) config('media.disk', 'public'))->url($path);
|
||||
|
||||
if (Str::startsWith($url, ['http://', 'https://'])) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return rtrim((string) config('app.url'), '/').'/'.ltrim($url, '/');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user