` instead of a gap. */ final class ImagePayload { /** * @return array|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 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, '/'); } }