PHPackages                             besnovatyj/yii2-cms-images - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. besnovatyj/yii2-cms-images

ActiveYii2-extension[File &amp; Storage](/categories/file-storage)

besnovatyj/yii2-cms-images
==========================

Переиспользуемый пакет управления изображениями для Yii2 CMS модулей

v1.1.1(2w ago)0215MITPHP &gt;=8.4

Since Jul 6Compare

[ Source](https://github.com/besnovatyj/yii2-cms-images)[ Packagist](https://packagist.org/packages/besnovatyj/yii2-cms-images)[ RSS](/packages/besnovatyj-yii2-cms-images/feed)WikiDiscussions Synced 1w ago

READMEChangelogDependencies (6)Versions (11)Used By (5)

yii2-cms-images
===============

[](#yii2-cms-images)

Пакет для управления изображениями в модулях Yii2 CMS. Предоставляет базовый AR-класс, standalone Yii2 actions и AJAX-виджет загрузки — чтобы любой модуль получил полноценное управление изображениями с минимальным кодом.

---

Возможности
-----------

[](#возможности)

- **`BaseImage`** — абстрактный ActiveRecord с настроенным `UploadBehavior` (оригиналы + миниатюры по профилям)
- **5 standalone actions** — загрузка, удаление, список, сортировка, главное изображение
- **AJAX-виджет** — drag-and-drop загрузка, сортировка, превью, параллельные загрузки
- **Pessimistic lock** — защита от race condition при параллельной загрузке (через `ImageOwnerInterface`)
- **Автоматическое управление main\_image\_id** — первое изображение становится главным автоматически

---

Установка
---------

[](#установка)

composer.json вашего модуля:

```
"require": {
  "besnovatyj/yii2-cms-images": "1.0.0"
}
```

---

Быстрый старт
-------------

[](#быстрый-старт)

### 1. Создать класс изображения

[](#1-создать-класс-изображения)

```
// src/entities/ArticleImage.php
namespace Besnovatyj\Article\entities;

use Besnovatyj\Images\base\BaseImage;

class ArticleImage extends BaseImage
{
    protected static function getParentAttribute(): string
    {
        return 'article_id'; // FK-атрибут в таблице изображений
    }

    protected static function getStorageName(): string
    {
        return 'Article'; // поддиректория в @static/origin/ и @static/cache/
    }

    protected static function getThumbProfiles(): array
    {
        return [
            'admin' => ['width' => 70,  'height' => 100],
            'thumb' => ['width' => 640, 'height' => 480],
        ];
    }

    public static function tableName(): string
    {
        return '{{%article_images}}';
    }
}
```

`BaseImage` автоматически настраивает пути к файлам:

```
@static/origin/Article/{article_id}/{image_id}.{ext}
@static/cache/Article/{article_id}/{profile}_{image_id}.{ext}

```

### 2. Создать ImageOwner-адаптер

[](#2-создать-imageowner-адаптер)

```
// src/image/ArticleImageOwner.php
namespace Besnovatyj\Article\image;

use Besnovatyj\Article\entities\Article;
use Besnovatyj\Article\repositories\ArticleRepository;
use Besnovatyj\Images\contracts\ImageOwnerInterface;
use Besnovatyj\Images\contracts\NullImageOwnerTrait;

class ArticleImageOwner implements ImageOwnerInterface
{
    use NullImageOwnerTrait; // lockOwner/refreshOwner — no-op (если не нужен pessimistic lock)

    public function __construct(
        private readonly Article           $article,
        private readonly ArticleRepository $repository,
    ) {}

    public function getOwnerId(): int          { return $this->article->id; }
    public function getOwnedImages(): array    { return $this->article->images; }
    public function getMainImageId(): ?int     { return $this->article->main_image_id ?: null; }
    public function setMainImageId(?int $id): void { $this->article->setMainImage($id); }
    public function saveOwner(): void          { $this->repository->save($this->article); }
}
```

### 3. Подключить actions в контроллер

[](#3-подключить-actions-в-контроллер)

```
// src/controllers/backend/ArticleController.php
use Besnovatyj\Article\entities\ArticleImage;
use Besnovatyj\Article\image\ArticleImageOwner;
use Besnovatyj\Images\helpers\ImageActionsMap;

class ArticleController extends Controller
{
    public function actions(): array
    {
        return ImageActionsMap::get(
            ArticleImage::class,
            fn(int $id) => new ArticleImageOwner($this->repo->get($id), $this->repo),
        );
    }

    public function behaviors(): array
    {
        return [
            'verbs' => [
                'class'   => VerbFilter::class,
                'actions' => [
                    'add-image'      => ['POST'],
                    'delete-image'   => ['POST'],
                    'get-images'     => ['POST'],
                    'set-main-image' => ['POST'],
                    'set-new-sort'   => ['POST'],
                ],
            ],
        ];
    }
    // ...
}
```

### 4. Добавить виджет в view

[](#4-добавить-виджет-в-view)

```
// views/backend/article/view.php
use Besnovatyj\Images\widgets\upload\Widget;
use yii\helpers\Url;

```

---

Pessimistic lock (для модулей с параллельной загрузкой)
-------------------------------------------------------

[](#pessimistic-lock-для-модулей-с-параллельной-загрузкой)

Если несколько файлов могут загружаться одновременно и у родительской сущности есть `main_image_id`, нужен pessimistic lock. Иначе несколько запросов одновременно увидят `main_image_id = null` и попытаются его установить → FK constraint violation.

Пример (см. `GalleryImageOwner` в `yii2-cms-gallery`):

```
class GalleryImageOwner implements ImageOwnerInterface
{
    // НЕ используем NullImageOwnerTrait — реализуем lock самостоятельно

    public function lockOwner(): void
    {
        $this->gallery->lock(); // PessimisticLockBehavior — SELECT FOR UPDATE
    }

    public function refreshOwner(): void
    {
        $this->gallery->refresh(); // получаем актуальные данные после lock
    }
    // ...
}
```

`UploadImageAction` всегда вызывает `lockOwner()` → `refreshOwner()` внутри транзакции. Для модулей без lock эти методы — no-op через `NullImageOwnerTrait`.

---

Структура пакета
----------------

[](#структура-пакета)

```
src/
  base/
    BaseImage.php                   # Абстрактный AR с UploadBehavior
  contracts/
    ImageOwnerInterface.php         # Контракт адаптера владельца
    NullImageOwnerTrait.php         # No-op реализации lock/refresh
  forms/
    UploadImageForm.php             # Форма загрузки (formName = 'AddImageForm')
  actions/
    ActionTrait.php                 # isAjax(), errorResponse()
    UploadImageAction.php           # POST add-image
    DeleteImageAction.php           # POST delete-image
    GetImagesAction.php             # POST get-images
    SetMainImageAction.php          # POST set-main-image
    SetNewSortAction.php            # POST set-new-sort
  helpers/
    ImageActionsMap.php             # ::get() — все 5 actions одним вызовом
  widgets/
    upload/
      Widget.php                    # Yii2 виджет (yii\base\Widget)
      assets/
        Assets.php                  # AssetBundle
        media/js/                   # TypeScript источники + собранный dist/index.js

```

---

API Actions
-----------

[](#api-actions)

Все actions принимают запросы с заголовком `X-Requested-With' === 'XMLHttpRequest`.

ActionPOST-параметрыОписание`add-image``AddImageForm[id]`, `AddImageForm[file]`Загрузить изображение`delete-image``DeleteImageForm[id]`, `DeleteImageForm[imageId]`Удалить изображение`get-images``GetImagesForm[id]`Получить список изображений`set-main-image``SetMainImageForm[id]`, `SetMainImageForm[imageId]`Установить главное`set-new-sort``SetNewSortForm[id]`, `SetNewSortForm[sortOrder]` (JSON)Обновить порядокФормат ответа:

```
{
    "status": "success"
}
{
    "status": "error",
    "message": "...",
    "data": {
        "message": "..."
    }
}
```

`get-images` при успехе возвращает:

```
{
    "status": "success",
    "data": {
        "1": {
            "kind": "server",
            "id": 1,
            "sort": 0,
            "fileName": "photo.jpg",
            "previewUrl": "...",
            "srcUrl": "...",
            "isMain": true
        }
    }
}
```

---

BaseImage: параметры конфигурации
---------------------------------

[](#baseimage-параметры-конфигурации)

МетодВозвращаетОписание`getParentAttribute()``string`Имя FK-атрибута (`gallery_id`, `person_id`)`getStorageName()``string`Поддиректория хранилища (`Gallery`, `Person`)`getThumbProfiles()``array`Профили миниатюр для UploadBehavior`tableName()``string`Имя таблицы БДПубличные методы:

```
BaseImage::make(int $parentId, UploadedFile $file): static  // фабричный метод
BaseImage::getParentAttributeName(): string                 // для Actions
$image->setSort(int $sort): void
$image->isIdEqualTo(int $id): bool
$image->getParentId(): int
```

---

ImageActionsMap: параметры
--------------------------

[](#imageactionsmap-параметры)

```
ImageActionsMap::get(
    string $imageClass,        // FQCN потомка BaseImage
    callable $ownerResolver,   // fn(int $id): ImageOwnerInterface
    string $previewProfile,    // профиль миниатюры для get-images (по умолчанию 'thumb')
): array
```

---

Пример использования в gallery (с pessimistic lock)
---------------------------------------------------

[](#пример-использования-в-gallery-с-pessimistic-lock)

`yii2-cms-gallery` — эталонная реализация с pessimistic lock:

- `src/entities/gallery/Image.php` — extends BaseImage
- `src/image/GalleryImageOwner.php` — реализует lock через PessimisticLockBehavior
- `src/controllers/backend/GalleryController.php` — подключает через ImageActionsMap

---

Требования
----------

[](#требования)

- PHP &gt;= 8.4
- yiisoft/yii2 ~2.0.0
- yiisoft/yii2-bootstrap5 ~2.0.0
- `besnovatyj/yii2-cms-upload` — `BaseImage` конфигурирует `Besnovatyj\Upload\heap\UploadBehavior`(оригиналы + миниатюры через `ThumbnailProfile`)

###  Health Score

46

—

FairBetter than 92% of packages

Maintenance97

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~3 days

Total

10

Last Release

16d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4dab004c4cab25d241bbf453f63cb48e6226d00bd84f50315e735c257c4a1b40?d=identicon)[besnovatyj](/maintainers/besnovatyj)

---

Tags

imagescmsuploadyii2

### Embed Badge

![Health badge](/badges/besnovatyj-yii2-cms-images/health.svg)

```
[![Health](https://phpackages.com/badges/besnovatyj-yii2-cms-images/health.svg)](https://phpackages.com/packages/besnovatyj-yii2-cms-images)
```

###  Alternatives

[craftcms/cms

Craft CMS

3.6k3.7M3.4k](/packages/craftcms-cms)[skeeks/cms

SkeekS CMS — control panel and tools based on php framework Yii2

13926.0k66](/packages/skeeks-cms)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
