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>
55 lines
1.8 KiB
PHP
55 lines
1.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Models\Concerns;
|
|
|
|
use App\Models\Media;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
|
/**
|
|
* Keeps the media table in step with the image paths a record holds.
|
|
*
|
|
* The panel still uploads a file and stores its path, exactly as before; this
|
|
* is what turns that path into something the frontend can render properly.
|
|
* Models declare which of their attributes are images and expose an ordinary
|
|
* `belongsTo` relation for each, so a listing can eager-load them instead of
|
|
* asking once per row.
|
|
*/
|
|
trait HasImages
|
|
{
|
|
/**
|
|
* Attributes holding the path of an uploaded file.
|
|
*
|
|
* @return array<int, string>
|
|
*/
|
|
abstract public function imageAttributes(): array;
|
|
|
|
public static function bootHasImages(): void
|
|
{
|
|
static::saved(function (Model $model): void {
|
|
foreach ($model->imageAttributes() as $attribute) {
|
|
// Only on the way in or on a change: an unrelated save must
|
|
// not queue work for an image nobody touched.
|
|
if ($model->wasChanged($attribute) || $model->wasRecentlyCreated) {
|
|
Media::for($model->getAttribute($attribute));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Relation definition shared by every image attribute.
|
|
*
|
|
* The join is on the stored path rather than on an id, because the file is
|
|
* the identity: two records pointing at one upload share its dimensions
|
|
* and its derived versions instead of each deriving them again.
|
|
*/
|
|
protected function imageRelation(string $attribute): BelongsTo
|
|
{
|
|
return $this->belongsTo(Media::class, $attribute, 'path')
|
|
->where('media.disk', (string) config('media.disk', 'public'));
|
|
}
|
|
}
|