Skip to content

Файлы и медиа

Все загруженные файлы (изображения, документы, видео, архивы) хранятся через модель Templite\Cms\Models\File. Изображения автоматически оптимизируются, генерируются ресайзы под именованные размеры и альтернативные форматы (WebP, AVIF).

В админке управление файлами разделено на два раздела:

  • Медиа (/cms/media) — файлы, загруженные через поля блоков и настроек.
  • Менеджер файлов (/cms/file-manager) — прямой доступ к публичным файлам с папочной структурой.

Модель File

КолонкаНазначение
nameИмя файла
pathПуть к оригиналу на диске (относительно disk)
diskИмя Laravel filesystem disk (public, s3, и т.п.)
sizeРазмер в байтах
mimeMIME-тип
typeКатегория: image, video, document, archive
parent_idДля дочерних файлов (если используется)
folder_idFK на FileFolder — папка в файловом менеджере
altAlt-текст (для изображений)
titleTitle-атрибут
sizesJSON-объект с ресайзами и форматами (см. ниже)
metaJSON: width, height, dominant_color и др.

Структура sizes

json
{
  "thumb": {
    "width": 150,
    "height": 150,
    "original": "files/2026/abc-thumb.jpg",
    "webp":     "files/2026/abc-thumb.webp",
    "avif":     "files/2026/abc-thumb.avif"
  },
  "wide": {
    "width": 1920,
    "height": 1080,
    "original": "files/2026/abc-wide.jpg",
    "webp":     "files/2026/abc-wide.webp"
  }
}

Каждый именованный размер содержит ширину/высоту и пути к файлам в разных форматах.

Структура meta

json
{
  "width": 1920,
  "height": 1080,
  "dominant_color": "#a1b2c3",
  ...
}

meta используется встроенным компонентом <x-cms::image> для подстановки width/height (защита от CLS) и background-color плейсхолдера.


API модели

Получение URL

php
$file->url;                              // URL оригинала
$file->url('thumb');                     // URL ресайза 'thumb' (оригинальный формат)
$file->url('thumb', 'webp');             // URL ресайза 'thumb' в WebP
$file->url('wide', 'avif');              // URL в AVIF

Если запрошенный размер/формат отсутствует — возвращается оригинал.

Проверка наличия формата

php
$file->hasFormat('thumb', 'webp');       // bool

Generated srcset

php
$file->srcset();                          // 'url 150w, url 600w, url 1200w'
$file->srcset('webp');                    // WebP-варианты с srcset

Тип файла

php
$file->isImage();    // bool
$file->isVideo();    // bool
$file->isDocument(); // bool

Удаление с файлами

php
$file->deleteWithFiles();

Удаляет все ресайзы, форматы, дочерние файлы и саму запись из БД.


Использование в Blade

Компонент <x-cms::image>

Реальная сигнатура:

blade
@props(['file', 'size' => null, 'class' => '', 'loading' => 'lazy'])
PropТипДефолтНазначение
:fileFileМодель файла
sizestring | nullnullИмя размера (thumb, wide, и т.п.)
classstring''CSS-классы для <img>
loading'lazy' | 'eager''lazy'Атрибут <img loading>

Не preset

В старых версиях документации был preset="..." — это неверно. Правильное имя prop'а — size.

Что делает компонент:

  • Рендерит <picture> с <source> для AVIF и WebP (если форматы сгенерированы), fallback <img> с оригиналом.
  • Подставляет width/height из $file->meta (защита от CLS).
  • Использует $file->meta['dominant_color'] как background плейсхолдера.
  • Подставляет alt из $file->alt и title из $file->title.
  • Если $file пустой — ничего не рендерит (нет @else).
blade
<x-cms::image :file="$fields['hero']" size="wide" />
<x-cms::image :file="$fields['avatar']" size="thumb" loading="eager" class="rounded" />

Ручной рендер

blade
@if ($fields['hero'])
    <picture>
        @if ($fields['hero']->hasFormat('wide', 'webp'))
            <source type="image/webp" srcset="{{ $fields['hero']->url('wide', 'webp') }}">
        @endif
        <img
            src="{{ $fields['hero']->url('wide') }}"
            alt="{{ $fields['hero']->alt ?? '' }}"
        >
    </picture>
@endif

Документы

blade
@if ($fields['document'])
    <a href="{{ $fields['document']->url }}" download>
        Скачать {{ $fields['document']->name }} ({{ number_format($fields['document']->size / 1024, 0) }} КБ)
    </a>
@endif

Видео

blade
@if ($fields['promo'])
    <video src="{{ $fields['promo']->url }}" controls></video>
@endif

Загрузка и обработка

Что происходит при загрузке изображения

  1. Файл загружается через UI или REST API в раздел медиа.
  2. Сохраняется на диск (по умолчанию — public, через Laravel Storage).
  3. Очередь images (env CMS_IMAGE_QUEUE, по умолчанию images) выполняет обработку:
    • Intervention Image 3 — ресайз в именованные размеры.
    • Spatie Image Optimizer — сжатие через jpegoptim/pngquant/gifsicle.
    • Генерация WebP и AVIF (если установлены cwebp и avifenc в системе).
  4. Все пути ресайзов и форматов записываются в sizes JSON.
  5. meta заполняется размерами и dominant color.

Очередь работает в контейнере templite-queue (или прода через queue:work). Если очередь не запущена — изображения остаются без ресайзов до обработки.

Размеры по умолчанию

Из config/cms.php:

php
'default_image_sizes' => [
    'thumb'  => ['width' => 150,  'height' => 150,  'fit' => 'crop'],
    'small'  => ['width' => 300,  'height' => null, 'fit' => 'contain'],
    'medium' => ['width' => 600,  'height' => null, 'fit' => 'contain'],
    'large'  => ['width' => 1200, 'height' => null, 'fit' => 'contain'],
],

Дополнительные размеры для скриншотов блоков и страниц:

php
'block_screenshot_sizes' => [
    'thumb'  => ['width' => 300, 'height' => 200, 'fit' => 'cover'],
    'medium' => ['width' => 600, 'height' => null, 'fit' => 'contain'],
],

'page_screenshot_sizes' => [
    'thumb'  => ['width' => 400, 'height' => 225, 'fit' => 'cover'],
    'medium' => ['width' => 960, 'height' => null, 'fit' => 'contain'],
],

Для конкретного поля типа img размеры можно переопределить в JSON data поля.

Форматы

php
'default_image_formats' => ['original', 'webp'],
'default_image_quality' => 85,

AVIF включается, если установлен системный avifenc. Без него генерируется только оригинал + WebP.

Пересоздание ресайзов

После изменения настроек размеров или добавления новых форматов — пересоздать ресайзы для всех изображений:

bash
docker exec templite-app php artisan cms:resize-images

Поддерживаемые форматы

Из config/cms.php:

php
'allowed_file_types' => [
    'image'    => ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'],
    'video'    => ['mp4', 'webm', 'avi'],
    'document' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'csv'],
    'archive'  => ['zip', 'rar', '7z', 'tar', 'gz'],
],

Максимальный размер загрузки — CMS_MAX_UPLOAD_SIZE в .env (по умолчанию 50 МБ).


Безопасность

Блокировка опасных расширений

php
'blocked_file_extensions' => [
    'php', 'php3', 'php4', 'php5', 'php7', 'php8', 'phtml', 'phar',
    'sh', 'bash', 'csh', 'ksh', 'zsh',
    'exe', 'com', 'bat', 'cmd', 'msi', 'scr', 'pif',
    'pl', 'cgi', 'py', 'rb', 'jsp', 'asp', 'aspx',
    'htaccess', 'htpasswd',
],

Эти расширения блокируются независимо от MIME-типа. Загрузка .php файла с заголовком Content-Type: image/jpeg всё равно отклоняется.

Санитизация SVG

php
'sanitize_svg' => env('CMS_SANITIZE_SVG', true),

По умолчанию включена. При загрузке .svg файла из него удаляются скрипты и event-handler'ы (предотвращение XSS).


Файловая структура

В админке файлы организованы в папки через модель FileFolder (поле folder_id в File). Папки используются для группировки в файловом менеджере, на путях хранения это не отражается (path — это реальный путь к файлу на диске).


Подводные камни

Подводные камни

  • Prop в <x-cms::image>size, не preset. Старые версии документации могут содержать неверный prop.
  • Возможен временной разрыв между загрузкой и обработкой. Очередь images обрабатывает ресайзы асинхронно. Если очередь не запущена — изображение видно как оригинал без ресайзов. Запуск worker'а — php artisan queue:work --queue=images (в production это делает контейнер templite-queue).
  • WebP/AVIF требуют системные утилиты: cwebp (libwebp) и avifenc (libavif). В Docker-образе Templite они уже установлены. Для bare-metal установок нужно поставить вручную.
  • SVG санитизируется, что может сломать SVG-анимации или интерактивные элементы. Отключить санитизацию глобально — CMS_SANITIZE_SVG=false в .env.
  • File::deleteWithFiles() удаляет все ресайзы и оригинал с диска необратимо. Просто $file->delete() удаляет только запись в БД, оставляя файлы на диске сиротами.
  • Глобальные настройки и поля шаблонов хранят img/file как ID, не модель. Для рендера через <x-cms::image> нужно загрузить модель: File::find($global['header_logo']).
  • Размер meta['width'] / meta['height'] — реальные размеры оригинала, не ресайза. Если выводите ресайз — width/height из sizes['<size>'].width будут точнее.

Связанные разделы

Распространяется под лицензией MIT.