Поддержка
Медиа

Медиа-библиотека

Контракт BrowsableDriver, API-эндпоинты и интеграция с хранилищами

Обзор

Медиа-библиотека позволяет просматривать все загруженные файлы в одном месте — с поиском, папками и фильтрацией по типу.

Медиа-библиотека доступна только если драйвер реализует интерфейс BrowsableDriver. Поля File и Image автоматически определяют это и отдают флаг browsable: true на фронтенд.

Контракт BrowsableDriver

Расширяет MediaDriver четырьмя методами для работы с библиотекой:

packages/core/src/Contracts/BrowsableDriver.php
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;
}
browse
→ LengthAwarePaginator
Пагинированный список медиа. Поддерживает текстовый поиск и фильтрацию по папке. Реализация решает сама, по каким полям искать (имя, тип, метаданные).
folders
→ Collection
Список папок. Если передан $parent — возвращает дочерние папки. Если null — корневые. Структура папки: id, name, parent.
createFolder
→ array
Создание папки. Возвращает массив с данными созданной папки.
moveToFolder
→ void
Перемещение медиа в папку. Если $folder = null — перемещение в корень.

API-эндпоинты

Все эндпоинты доступны только для авторизованных пользователей. Если драйвер не реализует BrowsableDriver, возвращается 501 Not Implemented.

Просмотр медиа

GET /api/frame/media
ПараметрТипПо умолчаниюОписание
searchstringnullПоисковый запрос
folderstringnullID папки для фильтрации
per_pageint24Количество на странице
typestringnullТип файлов: image, file

Список папок

GET /api/frame/media/folders
ПараметрТипПо умолчаниюОписание
parentstringnullID родительской папки

Создание папки

POST /api/frame/media/folders
ПолеТипОбязательноеОписание
namestringДаНазвание папки (макс. 255 символов)
parentstringНетID родительской папки

Перемещение медиа

PATCH /api/frame/media/{media}/move
ПолеТипОбязательноеОписание
folderstringНетID папки назначения (null = корень)

Реализация

Пример: драйвер на Spatie Media Library

app/Drivers/SpatieMediaDriver.php
namespace App\Drivers;

use Frame\Core\Contracts\BrowsableDriver;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Collection;
use Spatie\MediaLibrary\MediaCollections\Models\Media;

class SpatieMediaDriver implements BrowsableDriver
{
    public function browse(
        ?string $search = null,
        ?string $folder = null,
        ?string $type = null,
        int $perPage = 24,
    ): LengthAwarePaginator {
        return Media::query()
            ->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%"))
            ->when($folder, fn ($q) => $q->where('collection_name', $folder))
            ->latest()
            ->paginate($perPage);
    }

    public function folders(?string $parent = null): Collection
    {
        // Коллекции Spatie как папки
        return Media::query()
            ->select('collection_name')
            ->distinct()
            ->pluck('collection_name')
            ->map(fn (string $name) => [
                'id' => $name,
                'name' => $name,
                'parent' => null,
            ]);
    }

    public function createFolder(string $name, ?string $parent = null): array
    {
        // Коллекции в Spatie создаются при загрузке,
        // но можно хранить список в конфиге или таблице
        return ['id' => $name, 'name' => $name, 'parent' => $parent];
    }

    public function moveToFolder(int|string $id, ?string $folder = null): void
    {
        Media::findOrFail($id)->update([
            'collection_name' => $folder ?? 'default',
        ]);
    }

    // ... остальные методы MediaDriver
}

Пример: драйвер с файловой системой и БД

app/Drivers/DatabaseMediaDriver.php
namespace App\Drivers;

use App\Models\MediaFile;
use App\Models\MediaFolder;
use Frame\Core\Contracts\BrowsableDriver;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;

class DatabaseMediaDriver implements BrowsableDriver
{
    public function browse(
        ?string $search = null,
        ?string $folder = null,
        ?string $type = null,
        int $perPage = 24,
    ): LengthAwarePaginator {
        return MediaFile::query()
            ->when($search, fn ($q) => $q->where('name', 'like', "%{$search}%"))
            ->when($folder, fn ($q) => $q->where('folder_id', $folder))
            ->latest()
            ->paginate($perPage);
    }

    public function folders(?string $parent = null): Collection
    {
        return MediaFolder::query()
            ->where('parent_id', $parent)
            ->get()
            ->map(fn (MediaFolder $f) => [
                'id' => $f->id,
                'name' => $f->name,
                'parent' => $f->parent_id,
            ]);
    }

    public function createFolder(string $name, ?string $parent = null): array
    {
        $folder = MediaFolder::create([
            'name' => $name,
            'parent_id' => $parent,
        ]);

        return ['id' => $folder->id, 'name' => $folder->name, 'parent' => $folder->parent_id];
    }

    public function moveToFolder(int|string $id, ?string $folder = null): void
    {
        MediaFile::findOrFail($id)->update(['folder_id' => $folder]);
    }

    // ... остальные методы MediaDriver
}
Frame не создаёт таблицы для медиа — это ответственность драйвера. Каждый драйвер сам решает, где и как хранить файлы, папки и метаданные.

Фильтрация по типу

Поля Image и File передают свой тип при запросе к медиа-библиотеке. Это позволяет драйверу фильтровать результаты:

  • Поле Image запрашивает только изображения
  • Поле File показывает все типы файлов

Реализация фильтрации — на стороне драйвера в методе browse().

Интеграция с полями

Поля File и Image автоматически определяют, поддерживает ли текущий драйвер медиа-библиотеку, и отдают на фронтенд флаг browsable:

{
  "type": "image",
  "field": "avatar",
  "browsable": true,
  "max": 1,
  "multiple": false
}

Если browsable: true, UI показывает кнопку "Выбрать из библиотеки", которая открывает модальное окно с содержимым медиа-библиотеки.