Драйверы
Обзор
Вся работа с файлами и изображениями в Frame выполняется через медиа-драйверы. Драйвер — это класс, реализующий контракт MediaDriver, который определяет как файлы загружаются, хранятся, привязываются к моделям и удаляются.
FileDriver, который не требует дополнительных зависимостей.Контракт MediaDriver
Каждый драйвер реализует пять методов:
UploadedFile, возвращает массив метаданных для фронтенда.conversion).Полный интерфейс:
namespace Frame\Core\Contracts;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
interface MediaDriver
{
public function upload(UploadedFile $file): array;
public function delete(int|string $id): void;
public function resolve(Model $model, string $field, array $options = []): ?array;
public function fill(Model $model, string $field, ?array $value): void;
public function export(Model $model, string $field): ?string;
}
Глобальный драйвер
Зарегистрируйте драйвер в сервис-провайдере — все поля File и Image будут его использовать:
use Frame\Core\Frame;
use App\Drivers\SpatieMediaDriver;
public function boot(): void
{
Frame::useMediaDriver(SpatieMediaDriver::class);
}
Драйвер на уровне поля
Метод driver() позволяет переопределить драйвер для конкретного поля:
use App\Drivers\CloudinaryDriver;
use App\Drivers\SpatieMediaDriver;
public function fields(): array
{
return [
Image::make('avatar')
->driver(new SpatieMediaDriver()),
File::make('documents')
->driver(new CloudinaryDriver()),
];
}
->json()), всегда используется встроенный FileDriver.FileDriver (встроенный)
FileDriver — драйвер по умолчанию:
- Загружает файлы через Laravel Storage (диск
public, директорияframe-uploads) - Хранит метаданные в JSON-колонке модели
- Не требует дополнительных пакетов
Настройка диска и директории через сервис-контейнер:
use Frame\Laravel\Drivers\FileDriver;
public function register(): void
{
$this->app->when(FileDriver::class)
->needs('$disk')
->give('s3');
$this->app->when(FileDriver::class)
->needs('$directory')
->give('uploads');
}
Создание своего драйвера
Создайте класс драйвера
namespace App\Drivers;
use Frame\Core\Contracts\MediaDriver;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
class SpatieMediaDriver implements MediaDriver
{
public function upload(UploadedFile $file): array
{
// Загрузка, оптимизация, thumbhash...
}
public function delete(int|string $id): void
{
// Удаление медиа
}
public function resolve(Model $model, string $field, array $options = []): ?array
{
$conversion = $options['conversion'] ?? '';
// Получение медиа из коллекции
}
public function fill(Model $model, string $field, ?array $value): void
{
// Привязка: reorder, copy, удаление неиспользуемых
}
public function export(Model $model, string $field): ?string
{
// Экспорт путей
}
}
Зарегистрируйте драйвер
use Frame\Core\Frame;
use App\Drivers\SpatieMediaDriver;
public function boot(): void
{
Frame::useMediaDriver(SpatieMediaDriver::class);
}
Структура ответа upload
Метод upload() должен возвращать массив с метаданными:
public function upload(UploadedFile $file): array
{
return [
'id' => 'unique-id',
'path' => 'https://.../',
'size' => 102400,
'name' => 'document.pdf',
'type' => 'file', // 'file' или 'image'
'preview' => null, // thumbhash, base64...
'extension' => 'pdf',
'collection' => 'default',
'conversions' => [],
'properties' => [],
'order' => 0,
];
}
Опции resolve
Массив $options в методе resolve() может содержать:
| Ключ | Тип | Описание |
|---|---|---|
conversion | string | Название конверсии (из Image::conversion()) |
type | string | Тип поля (file или image) для фильтрации |
Browsable-драйверы (медиа-библиотека)
Если драйвер умеет показывать ранее загруженные файлы (медиа-библиотека), он должен дополнительно реализовать Frame\Core\Contracts\BrowsableDriver. У поля File/Image автоматически появится кнопка «Select from library» — флаг browsable в ответе API вычисляется из resolveDriver() instanceof BrowsableDriver.
Контракт
namespace Frame\Core\Contracts;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
interface BrowsableDriver extends MediaDriver
{
public function browse(
?string $search = null,
?string $folder = null,
?string $type = null,
int $perPage = 24,
): LengthAwarePaginator;
public function folders(?string $parent = null): Collection;
public function createFolder(string $name, ?string $parent = null): array;
public function moveToFolder(int|string $id, ?string $folder = null): void;
}
Методы
search (подстрока в имени), folder (id текущей папки или null), type (image/file/null для всех), perPage. Элементы — массивы в формате upload-response.id, name, parent. Верните пустую коллекцию, если папки не поддерживаются.RuntimeException.null).Минимальный пример без папок
Если хранилище плоское (нет директорий), folders() возвращает пустую коллекцию, createFolder кидает, moveToFolder — no-op:
use Frame\Core\Contracts\BrowsableDriver;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use RuntimeException;
class MyBrowsableDriver implements BrowsableDriver
{
// ...все методы MediaDriver...
public function browse(
?string $search = null,
?string $folder = null,
?string $type = null,
int $perPage = 24,
): LengthAwarePaginator {
$query = Media::query()->latest('id');
if ($search) {
$query->where('filename', 'ilike', "%{$search}%");
}
if ($type === 'image') {
$query->where('mime_type', 'like', 'image/%');
} elseif ($type === 'file') {
$query->where('mime_type', 'not like', 'image/%');
}
return $query->paginate($perPage)
->through(fn (Media $m) => $this->toFrameArray($m));
}
public function folders(?string $parent = null): Collection
{
return collect();
}
public function createFolder(string $name, ?string $parent = null): array
{
throw new RuntimeException('Folders are not supported.');
}
public function moveToFolder(int|string $id, ?string $folder = null): void
{
// no-op
}
}
implements BrowsableDriver (не просто MediaDriver + методы). Flag browsable в сериализации поля завязан на instanceof.