PHPackages                             brezgalov/yii2-api-helpers - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. brezgalov/yii2-api-helpers

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

brezgalov/yii2-api-helpers
==========================

api utils making you code better

v2.1.4(3y ago)3171MITPHPPHP &gt;=7.2.0

Since Aug 6Pushed 3y ago1 watchersCompare

[ Source](https://github.com/Brezgalov/yii2-api-helpers)[ Packagist](https://packagist.org/packages/brezgalov/yii2-api-helpers)[ RSS](/packages/brezgalov-yii2-api-helpers/feed)WikiDiscussions master Synced yesterday

READMEChangelogDependencies (1)Versions (14)Used By (0)

О пакете
--------

[](#о-пакете)

Этот пакет - результат работы по стандартизации, ускорению разработки команды и увеличению качества кода.

Он ориентирован на разработку в первую очередь серверных API, однако, работа с web формами так же поддерживается. *(Апи часто требует "админку" для существования в виде продукта)*

Ключевые особенности
--------------------

[](#ключевые-особенности)

### Стандартизация подключения логики.

[](#стандартизация-подключения-логики)

Никакой логики в контроллере, обеспечиваем подключение "Controller -&gt; Action -&gt; Service"

### Отдельный интерфейс для пользовательского ввода.

[](#отдельный-интерфейс-для-пользовательского-ввода)

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

### Отсутствие необходимости контроля за транзакциями.

[](#отсутствие-необходимости-контроля-за-транзакциями)

Переворачиваем игру: теперь транзакции есть везде и "стажер" не забудет их применить, а отказ от использования транзакций представлен в явном виде

### Последовательное выполнение action при одновременном вызове с одного и того же IP.

[](#последовательное-выполнение-action-при-одновременном-вызове-с-одного-и-того-же-ip)

Избегаем случаев, когда много одновременных запросов путают друг другу "карты"

### Использование отложенных событий.

[](#использование-отложенных-событий)

Списываем деньги с карты пользователя, только если action гарантированно выполнился без ошибок

### Разделение форматирования ответа и логики.

[](#разделение-форматирования-ответа-и-логики)

Один сервис, много вариантов форматирования в разных action

### Разделение логики и отображения для web-форм.

[](#разделение-логики-и-отображения-для-web-форм)

Вывод html - отдельный вариант форматирования. Для передачи данных на страницу используем "интерфейс DTO".

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

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

```
composer require brezgalov/yii2-api-helpers --prefer-dist

```

Подключение сервиса
-------------------

[](#подключение-сервиса)

Используем метод **Controller::actions()** для подключения **action**:

```
public function actions()
{
    return [
        'list' => [
            'class' => ApiGetAction::class,
            'service' => MyExampleRepositoryService::class,
            'methodName' => MyExampleRepositoryService::METHOD_LIST,
        ],

        'cities' => [
            'class' => ApiActiveGetAction::class,
            'service' => KladrCitiesSearch::class,
        ],

        'regions' => [
            'class' => ApiActiveGetAction::class,
            'service' => KladrRegionsSearch::class,
            'dataProviderSetup' => [
                'pagination' => false,
            ],
            'afterDataProviderInit' => function(ActiveDataProvider $dataProvider) {
                $dataProvider->sort->defaultOrder = ['name' => SORT_DESC];

                return $dataProvider;
            },
        ],
    ];
}

```

Код сервиса **MyExampleRepositoryService**:

```
class MyExampleRepositoryService extends Model
{
    const METHOD_LIST = 'listData';

    protected function getExampleData()
    {
        return [
            [
                'id' => 1,
                'name' => 'Barbara',
                'sex' => 'female',
            ],
            [
                'id' => 2,
                'name' => 'Mike',
                'sex' => 'male',
            ],
        ];
    }

    public function listData()
    {
        return $this->getExampleData();
    }
}

```

Для разработки API я предлагаю использовать такие **action**:

- **ApiGetAction** - для вывода любой информации
- **ApiPostAction** - для работы логики
- **ApiGetActiveAction** - для вывода списков с помощью **ActiveDataProvider**

> \_ApiGetAction и ApiPostAction отличаются набором поведений внутри. Cм. секцию "Поведения для action"

При подключении **ApiGetAction**/**ApiPostAction** **action** используется 2 параметра:

- **service** - Конфигурация класса сервиса
- **methodName** - Строка, обозначающая метод сервиса, который необходимо вызвать

Для упрощения реализации вывода списка объектов можно использовать **ApiGetActiveAction**. Основное отличие от **\\yii\\rest\\IndexAction** заключается в использовании **ApiGetAction**и механизма форматирования ответа. Благодаря этому возможна гибкая настройка **DataProvider**.

При подключении **ApiGetActiveAction** можно указать:

- **service** - Модель, возвращающая **ActiveQuery**
- **methodName** - Предполагается, что модель наследует **ISearch**, поэтому по-умолчанию указано "getQuery"
- **dataProviderSetup** - Массив настроек **DataProviderInterface**
- **afterDataProviderInit** - Callback для редактирования инстанцированного **DataProviderInterface**

Пользовательский ввод
---------------------

[](#пользовательский-ввод)

### Обрабатываем пользовательский ввод напрямую

[](#обрабатываем-пользовательский-ввод-напрямую)

Пользовательский ввод представлен в виде полей сервиса, которые заполняются через **IRegisterInputInterface**:

```
class MyExampleRepositoryService extends Model implements IRegisterInputInterface
{
    public $idFilter;

    public $nameFilter;

    public function registerInput(array $data = [])
    {
        $this->nameFilter = $data['filter_name'] ?? $this->nameFilter;
        $this->idFilter = $data['filter_ID'] ?? $this->idFilter;
    }

    ... // other code

```

Благодаря такому интерфейсу:

**!** Не нужно реализовывать/вызывать наполнение пользовательскими данными, когда это не требуется

**!** Интерфейс web API и сервиса могут различаться, это бывает полезно для рефакторинга

**!** Разрывает связь между rules и load для сервисов на основе Model, теперь проще указать правило валидации в rules для переменной которую не хотим заполнять данными запроса

### Используем Model::load для пользовательского ввода

[](#используем-modelload-для-пользовательского-ввода)

Так же мы можем поддерживать работу с web-формами или сервисами старой версии этой библиотеки, обернув метод load

```
class MyExampleRepositoryService extends Model implements IRegisterInputInterface
{
    public $id;

    public $name;

    public function rules()
    {
        return [
            [['id'], 'integer'],
            [['name'], 'string'],
        ];
    }

    public function registerInput(array $data = [])
    {
        $this->load($data, '');
    }

    ... // other code

```

Форматирование ответа
---------------------

[](#форматирование-ответа)

Для форматирования ответа сервиса необходимо использовать объект интерфейса **IFormatter**

```
interface IFormatter
{
    public function format($service, $result);
}

```

Метод **format($service, $result)** позволяет иметь доступ к состоянию сервиса через переменную **$service**, для формирования более комплексной логики форматирования.

По-умолчанию для форматирования ответа API используется объект класса **ModelResultFormatter**. Он отвечает за стандартную обработку ошибок.

Для примера реализуем Formatter скрывающий поле "sex" из набора данных:

```
class MyExampleFormatter extends ModelResultFormatter
{
    public function format($service, $result)
    {
        if (is_array($result)) {
            foreach ($result as &$item) {
                unset($item['sex']);
            }

            return $result;
        }

        return parent::format($service, $result);
    }
}

```

Подключим Formatter в контроллере:

```
public function actions()
{
    return [
        'index' => [
            'class' => ApiGetAction::class,
            'service' => MyExampleRepositoryService::class,
            'methodName' => MyExampleRepositoryService::METHOD_LIST,
            'formatter' => MyExampleFormatter::class
        ],
    ];
}

```

Валидация и обработка ошибок
----------------------------

[](#валидация-и-обработка-ошибок)

Обработка ошибок API происходит, когда метод сервиса возвращает false.

Вывести ошибку можно с применением объекта **Brezgalov\\ApiHelpers\\v2\\ErrorException** или использовать метод **Model::addError($attribute, $error)**

**ErrorException** позволяет самостоятельно выбрать формат ошибки и код ответа.

Использование ошибок класса **Model** приведет к стандартному отображению ошибок Yii2 с кодом ответа 422

Поведения для action
--------------------

[](#поведения-для-action)

Библиотека позволяет подключить поведения к **action**.

Стандартный набор поведений **action** задается через метод **BaseAction::getDefaultBehaviors()**

```
class ApiPostAction extends BaseAction
{
    const BEHAVIOR_KEY_TRANSACTION = 'transaction';
    const BEHAVIOR_KEY_MUTEX = 'mutex';
    const BEHAVIOR_KEY_DELAYED_EVENTS = 'delayedEvents';

    public $formatter = ModelResultFormatter::class;

    protected function getDefaultBehaviors()
    {
        return [
            static::BEHAVIOR_KEY_TRANSACTION => TransactionBehavior::class,
            static::BEHAVIOR_KEY_MUTEX  => MutexBehavior::class,
            static::BEHAVIOR_KEY_DELAYED_EVENTS  => DelayedEventsBehavior::class,
        ];
    }
}

```

Поле **BaseAction::$behavior** позволяет управлять поведениями при подключении к контроллеру. Если по ключу поведения передать значение **false** - поведение не будет подключено к **action**

```
public function actions()
{
    return [
        'my-post-action' => [
            'class' => ApiPostAction::class,
            ...
            'behaviors' => [
                ApiPostAction::BEHAVIOR_KEY_TRANSACTION => false,
                MyCustomBehavior::class,
            ],
        ],
    ];
}

```

Работа с web-формами
--------------------

[](#работа-с-web-формами)

**RenderAction** отвечает за отрисовку страниц и обладает настройками для подключения/отключения **layout**, **заголовка страницы**, указания **view** и **режима отрисовки** (по-умолчанию / отрисовка файла / ajax-отрисовка)

```
class RenderAction extends BaseAction
{
    /**
    * @var bool
    */
    public $layout = true;

    /**
     * @var string
     */
    public $title;

    /**
     * @var string
     */
    public $view;

    /**
     * @var string
     */
    public $mode = ViewResultFormatter::RENDER_MODE_DEFAULT;

    /**
     * @var ViewContextInterface|string|array
     */
    public $viewContext;

    /**
     * @var IFormatter
     */
    public $formatter = ViewResultFormatter::class;

    ...
}

```

Пример реализации **ViewContext**

```
class ViewContext implements ViewContextInterface
{
    /**
     * @return string the view path that may be prefixed to a relative view name.
     */
    public function getViewPath()
    {
        return __DIR__;
    }
}

```

Для передачи данных на **view** используется интерфейс **IRenderFormatterDTO**.

Можно использовать вариант реализации при котором сервис возвращает объект **DTO** наследующий этот интерфейс. Или, что проще, можно создать сервис \***Page**, который наследовать этот интерфейс и возвращать сам себя

Пример сервиса \***Page**:

```
class RightsTablePage extends Model implements IRenderFormatterDTO, IRegisterInputInterface
{
    const PAGE_PREPARE_METHOD = 'preparePageData';

    /**
     * @var RightsTableDto
     */
    protected $tableDto;

    /**
     * @var RightsTableFactory
     */
    public $rightsTableFactory;

    /**
     * RightsTablePage constructor.
     * @param array $config
     */
    public function __construct($config = [])
    {
        parent::__construct($config);

        if (empty($this->rightsTableFactory)) {
            $this->rightsTableFactory = new RightsTableFactory();
        }
    }

    /**
     * @return RightsTableDto[]
     */
    public function getViewParams()
    {
        return [
            'tableDto' => $this->tableDto,
            'tableErrors' => $this->submitRightsService->getErrorSummary(true),
        ];
    }

    /**
     * @param array $data
     * @return bool
     */
    public function registerInput(array $data = [])
    {
        $this->submitRightsService->registerInput($data);

        return true;
    }

    /**
     * @return $this
     */
    public function preparePageData()
    {
        $this->tableDto = $this->rightsTableFactory->buildTableDto();

        return $this;
    }
}

```

**::getViewParams()** - отвечает за передачу данных на **view**

**::registerInput()** - отвечает за регистрацию пользовательского ввода

**::preparePageData()** - отвечает за подготовку данных

Пример использования:

```
public function actions()
{
    return [
        'get-table' => [
            'class' => RenderAction::class,
            'service' => RightsTablePage::class,
            'methodName' => RightsTablePage::PAGE_PREPARE_METHOD,
            'title' => 'Таблица ролей и разрешений',
            'view' => 'RightsTable/View',
            'viewContext' => ViewContext::class,
        ],
    ];
}

```

Для обработки **submit'a** формы я предлагаю использовать **SubmitRenderAction**. Он работает аналогично **RenderAction**, это позволяет отрисовать форму снова с отображением ошибок, если такие произошли. Параметр **successRedirectRoute** позволяет указать маршрут для перехода при успешном **submit**.

Пример подключения **SubmitRenderAction**:

```
[
    'class' => SubmitRenderAction::class,
    'service' => RightsTablePage::class,
    'methodName' => RightsTablePage::SUBMIT_TABLE_METHOD,
    'title' => 'Таблица ролей и разрешений',
    'successRedirectRoute' => 'rights-table/index',
    'view' => 'RightsTable/View',
    'viewContext' => ViewContext::class,
],

```

Более подробно можно посмотреть пример использования **RenderAction** в репозитории [brezgalov/yii2-rights-manager](https://github.com/Brezgalov/yii2-rights-manager)

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity14

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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 ~27 days

Recently: every ~3 days

Total

13

Last Release

1410d ago

Major Versions

v1.0.7 → v2.02022-06-17

### Community

Maintainers

![](https://www.gravatar.com/avatar/ba5437e2f11c8cf8548822ad3c8e80eb9f9e84d266daf340e72681195aa7810d?d=identicon)[Brezgalov](/maintainers/Brezgalov)

---

Top Contributors

[![Brezgalov](https://avatars.githubusercontent.com/u/30072225?v=4)](https://github.com/Brezgalov "Brezgalov (100 commits)")

### Embed Badge

![Health badge](/badges/brezgalov-yii2-api-helpers/health.svg)

```
[![Health](https://phpackages.com/badges/brezgalov-yii2-api-helpers/health.svg)](https://phpackages.com/packages/brezgalov-yii2-api-helpers)
```

###  Alternatives

[dmstr/yii2-cookie-consent

Yii2 Cookie Consent Widget

1452.6k](/packages/dmstr-yii2-cookie-consent)

PHPackages © 2026

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