besnovatyj/yii2-cms-upload
Composer 安装命令:
composer require besnovatyj/yii2-cms-upload
包简介
Функционал загрузок для Yii2 CMS
关键字:
README 文档
README
Обзор
Переработанная система загрузки файлов и изображений для Yii2 ActiveRecord. Один behavior для файлов и изображений. Чёткое разделение ответственностей.
Структура
upload/
├── UploadBehavior.php — единый behavior, lifecycle-хуки модели
├── PathResolver.php — резолвинг шаблонов путей
├── NameGenerator.php — генерация и санитизация имён файлов
├── ThumbnailProfile.php — value object профиля превью
├── ThumbnailMode.php — enum режимов превью (crop/resize)
├── ThumbnailGenerator.php — генерация превью изображений
├── FileUploadException.php — исключение загрузки
├── UrlDownloader.php — скачивание файлов по URL
├── DownloadResult.php — value object результата скачивания
└── Storage/
├── StorageInterface.php — контракт хранилища
├── StorageException.php — исключение хранилища
└── LocalStorage.php — локальная файловая система
Интеграция в проект
1. Регистрация компонента хранилища
Через DIC, смотри \Besnovatyj\Upload\Bootstrap
2. Обновление модели Profile
Было:
use common\components\upload\behaviors\ImageUploadBehavior; public function behaviors(): array { return [ [ 'class' => ImageUploadBehavior::class, 'attribute' => 'photo', 'filePath' => '@static/origin/users/[[attribute_user_id]]/[[id]].[[extension]]', 'fileUrl' => '@staticHostName/origin/users/[[attribute_user_id]]/[[id]].[[extension]]', 'thumbPath' => '@static/cache/users/[[attribute_user_id]]/[[profile]]_[[id]].[[extension]]', 'thumbUrl' => '@staticHostName/cache/users/[[attribute_user_id]]/[[profile]]_[[id]].[[extension]]', 'thumbs' => [ 'admin' => ['width' => 100, 'height' => 70], 'thumb' => ['width' => 370, 'height' => 370], ], ], ...parent::behaviors(), ]; }
Стало:
use Besnovatyj\Upload\heap\UploadBehavior; use Besnovatyj\Upload\heap\ThumbnailProfile; public function behaviors(): array { return [ 'photoUpload' => [ 'class' => UploadBehavior::class, 'attribute' => 'photo', 'pathTemplate' => 'origin/users/{attr.user_id}/{pk}.{extension}', 'thumbnails' => [ new ThumbnailProfile('admin', width: 100, height: 70), new ThumbnailProfile('thumb', width: 370, height: 370), ], 'thumbPathTemplate' => 'cache/users/{attr.user_id}/{profile}_{pk}.{extension}', ], ...parent::behaviors(), ]; }
3. Обновление view-файлов и сервисов
Методы получения URL:
// Было: $model->getImageFileUrl('photo'); $model->getThumbFileUrl('photo', 'admin'); $model->getUploadedFileUrl('photo'); $model->getUploadedFilePath('photo'); // Стало: $model->getUploadUrl('photo'); $model->getThumbUrl('photo', 'admin'); $model->getUploadPath('photo'); $model->getThumbPath('photo', 'admin');
Все методы поддерживают fallback URL:
$model->getUploadUrl('photo', '/images/no-photo.png'); $model->getThumbUrl('photo', 'admin', '/images/no-photo-sm.png');
4. Удаление старых файлов
После миграции удалить:
app/common/components/upload/behaviors/FileUploadBehavior.phpapp/common/components/upload/behaviors/ImageUploadBehavior.phpapp/common/components/upload/behaviors/FileUploadException.phpapp/common/components/upload/behaviors/TODO.MDapp/common/components/upload/behaviors/(директория)app/common/components/upload/UploadFromUrl.phpapp/common/components/upload/UploadFileFromUrl.php
5. Поиск всех использований в проекте
Найти и обновить все файлы, ссылающиеся на старые классы:
grep -r "common\\\\components\\\\upload\\\\behaviors" app/ --include="*.php" grep -r "ImageUploadBehavior\|FileUploadBehavior" app/ --include="*.php" grep -r "getImageFileUrl\|getThumbFileUrl\|getUploadedFileUrl\|getUploadedFilePath" app/ --include="*.php"
Шаблоны путей
Пути — относительные, без Yii-алиасов. Хранилище само добавляет basePath и baseUrl.
Плейсхолдеры
| Плейсхолдер | Описание | Пример значения |
|---|---|---|
{pk} |
Primary key модели | 42 |
{model} |
Имя класса (lcfirst) | profile |
{attribute} |
Имя атрибута | photo |
{extension} |
Расширение файла | jpg |
{filename} |
Имя без расширения | my-photo |
{basename} |
Полное имя файла | my-photo.jpg |
{profile} |
Профиль превью | admin |
{attr.xxx} |
Значение атрибута модели | {attr.user_id} → 5 |
{md5.xxx} |
MD5 от атрибута модели | {md5.email} → ab12... |
Примеры
// Файл документа 'pathTemplate' => 'documents/{attr.user_id}/{pk}.{extension}' // → documents/5/42.pdf // Изображение с превью 'pathTemplate' => 'origin/gallery/{attr.gallery_id}/{pk}.{extension}' 'thumbPathTemplate' => 'cache/gallery/{attr.gallery_id}/{profile}_{pk}.{extension}' // → origin/gallery/10/42.jpg // → cache/gallery/10/admin_42.jpg
Примеры конфигураций
Простая загрузка файла (без превью)
[
'class' => UploadBehavior::class,
'attribute' => 'document',
'pathTemplate' => 'documents/{attr.user_id}/{pk}.{extension}',
]
Изображение с превью
use Besnovatyj\Upload\heap\ThumbnailProfile; use Besnovatyj\Upload\heap\ThumbnailMode; [ 'class' => UploadBehavior::class, 'attribute' => 'photo', 'pathTemplate' => 'origin/users/{attr.user_id}/{pk}.{extension}', 'generateName' => true, // уникальные имена 'thumbnails' => [ new ThumbnailProfile('small', width: 100, height: 100, mode: ThumbnailMode::Crop), new ThumbnailProfile('medium', width: 400, height: 300), new ThumbnailProfile('large', width: 800, height: 600, quality: 90), ], 'thumbPathTemplate' => 'cache/users/{attr.user_id}/{profile}_{pk}.{extension}', ]
Превью в webp/avif (параметр format)
Формат превью задаётся per-профильно и не зависит от формата оригинала:
'thumbnails' => [ // формат оригинала (как раньше) new ThumbnailProfile('admin', width: 100, height: 70), // современный формат отдачи: энкодер + {extension} в пути превью станут webp new ThumbnailProfile('thumb', width: 370, height: 370, format: 'webp'), new ThumbnailProfile('hero', width: 1600, height: 900, format: 'avif', quality: 60), ],
Принципы:
- Оригинал не перекодируется —
formatкасается только превью (оригинал остаётся источником для регенерации и скачивания). formatопределяет энкодер imagine/Imagick и подставляется в{extension}шаблонаthumbPathTemplate—getThumbUrl()автоматически вернёт URL с новым расширением (cache/users/5/thumb_1.webp).- Поддерживаемые значения зависят от сборки Imagick: проверить —
php -r "print_r(Imagick::queryFormats('WEBP')); print_r(Imagick::queryFormats('AVIF'));". null(дефолт) — прежнее поведение: формат оригинала.- Рекомендация:
webp— безопасный дефолт для превью (поддержка браузерами ~98%);avif— точечно для тяжёлых изображений (энкодинг заметно дороже, поддержка ~95%).
Кастомная генерация имени
[
'class' => UploadBehavior::class,
'attribute' => 'avatar',
'pathTemplate' => 'avatars/{pk}.{extension}',
'generateName' => fn(\yii\web\UploadedFile $file) => 'avatar_' . time(),
'deleteOnEmpty' => true, // удалять файл если атрибут очищен
]
Загрузка файла по URL
use Besnovatyj\Upload\heap\UrlDownloader; $downloader = new UrlDownloader(timeout: 60); $result = $downloader->download( url: 'https://example.com/photo.jpg', allowedExtensions: ['jpg', 'png', 'webp'], maxSize: 5 * 1024 * 1024, // 5 MB ); // Использование результата в модели $model->photo = /* создать UploadedFile из $result->tempPath */; $model->save(); $result->cleanup(); // удалить временный файл
Разница со старой системой
| Аспект | Было | Стало |
|---|---|---|
| Классы behavior | 2 (наследование) | 1 (final, без наследования) |
| Файловые операции | Разбросаны по behavior-ам | Изолированы в LocalStorage |
| Резолвинг путей | 30-строчный regex callback | PathResolver с strtr() |
| Конфигурация превью | Сырые массивы | ThumbnailProfile (typed VO) |
| Формат превью | Только формат оригинала | Per-профильный format (webp/avif/…) |
| Режим превью | Магическая строка 'thumbsType' |
ThumbnailMode enum |
| Драйвер изображений | Хардкод IMAGICK каждый вызов | Настраивается в конструкторе |
| Обработка ошибок превью | Нет (crash) | try/catch на каждый профиль |
| Скачивание по URL | 2 дублирующихся класса | 1 класс UrlDownloader |
| Пути в модели | @static/..., @staticHostName/... |
Относительные, storage добавляет базу |
| Конфиг в модели | filePath + fileUrl + thumbPath + thumbUrl | pathTemplate + thumbPathTemplate |
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-06