Перейти к содержанию

Ajax

Контроллер

Контроллер должен наследоваться от Bitrix\Main\Engine\Controller. Контроллер хранит коллекцию ошибок в свойстве errorCollection. Ошибка — объект класса \Bitrix\Main\Error.

Как добавлять ошибки:

  • $this->addError(\Bitrix\Main\Error $error) — принимает объект-ошибку
  • $this->addErrors(array $errors) — принимает массив ошибок. Сокращение для $this->errorCollection->add()
  • $this->errorCollection->add(array $errors) — принимает массив ошибок
  • $this->errorCollection->setError(\Bitrix\Main\Error $error, $offset = null) — принимает объект-ошибку. С помощью $offset можно заменить существующую ошибку в коллекции. Передача null добавляет ошибку в конец коллекции
  • Обработка \Throwable, \Exception, \Error:
    • $this->runProcessingException(\Exception $exception) — получает \Bitrix\Main\Error из \Exception и добавляет эту ошибку в коллекцию errorCollection
    • $this->runProcessingError(\Error $error) — получает \Bitrix\Main\Error из \Error и добавляет эту ошибку в коллекцию errorCollection
    • $this->runProcessingThrowable(\Throwable $t) — получает \Bitrix\Main\Error из \Throwable и добавляет эту ошибку в коллекцию errorCollection

Метод buildErrorFromException позволяет сформировать \Bitrix\Main\Error из \Exception

class MyController extends \Bitrix\Main\Engine\Controller
{
    public function myAction()
    {
        $this->addError(new \Bitrix\Main\Error('Ошибка через addError'));

        // сокращение для $this->errorCollection->add
        $this->addErrors([
            new \Bitrix\Main\Error('message'),
            new \Bitrix\Main\Error('message'),
        ]);

        $this->errorCollection->add([
            new \Bitrix\Main\Error('Первая ошибка'),
            new \Bitrix\Main\Error('Вторая ошибка'),
        ]);

        // с использованием setError()
        $this->errorCollection->setError(new \Bitrix\Main\Error('Первая ошибка'));
        $this->errorCollection->setError(new \Bitrix\Main\Error('Вторая ошибка'));

        try {
            // что-то происходит
        } catch (\Throwable $t) {
            $this->runProcessingThrowable($t);
        }
    }
}

Битрикс проверяет по наличию элементов в errorCollection и формирует объект-результат \Bitrix\Main\Engine\Response\AjaxJson. Если errorCollection пустой — вернётся success.

Response

Получить объект $response можно так:

$response = \Bitrix\Main\Context::getCurrent()::getCurrent()->getResponse();

Конфигурирование экшенов

Необходимо переопределить метод configureActions. Метод должен вернуть массив элементов. Ключ — название экшена без суффикса Action. Значение — массив.

use \Bitrix\Main\Engine\Controller;

class MyController extends Controller
{
    public function configureActions(): array
    {
        return [
            // для checkAction ключ имеет название check
            'check' => [
                'prefilters'   => [],
                '+prefilters'  => [],   // добавление префильтров к существующему набору
                'postfilters'  => [],
                '+postfilters' => [],
            ],
        ];
    }

    public function checkAction()
    {}
}

Префильтры и постфильтры содержат коллекцию ActionFilter, каждый из которых должен был наследником \Bitrix\Main\Engine\ActionFilter\Base::class.

Контроллер проходит по каждому объявленному фильтру, для каждого prefilters регистрирует событие:

$eventManager->addEventHandler(
    'main',
    'MyController::onBeforeAction',
    [$filter, 'onBeforeAction']
);

Для каждого postfilters

$eventManager->addEventHandler(
    'main',
    'MyController::onBeforeAction',
    [$filter, 'onAfterAction']
  );

Поэтому Фильтр всю свою логику должен реализовывать в методах onBeforeAction и onAfterAction.

Для ошибки фильтр должен вернуть \Bitrix\Main\EventResult. При инициализации EventResult должен обязательно быть передан $handler, который должен реализовывать интерфейс \Bitrix\Main\Errorable, и который заполнит errorCollection ошибками. Например, передать себя.

То есть должны сработать оба условия:

  • возвращается EventResult типа EventResult::ERROR
  • передан $handler с непустой errorCollection
// Реализация ActionFilter для Контроллера
class MyActionFilter extends \Bitrix\Main\Engine\ActionFilter\Base
{
    /**
     * Метод onBeforeAction будет вызван до срабатывания основного Action
     *
     * @param $event
     * @return \Bitrix\Main\EventResult
     */
    public function onBeforeAction($event)
    {
        // Добавление ошибки через свой метод, который принимает объект-ошибку
        $this->addError(new \Bitrix\Main\Error('Первая ошибка'));

        // либо добавление ошибок через errorCollection
        $this->errorCollection->add([
            new \Bitrix\Main\Error('Первая ошибка через errorCollection');
            new \Bitrix\Main\Error('Вторая ошибка через errorCollection');
        ]);
        $this->errorCollection->setError(new \Bitrix\Main\Error('Ещё одна ошибка через errorCollection'));

        // обязательно вернуть EventResult типа EventResult::ERROR
        return new \Bitrix\Main\EventResult(
            \Bitrix\Main\EventResult::ERROR,
            null,
            null,
            $this
        );
    }
}
// Пример контроллера, использующего MyActionFilter
class MyController extends \Bitrix\Main\Engine\Controller
{
    public function configureActions(): array
    {
        return [
            'test' => [
                'prefilters' => [
                    new MyActionFilter(),
                ]
            ]
        ];
    }

    public function testAction()
    {}
}

Дефолтные фильтры для своего контроллера

В контроллере есть метод getDefaultPreFilters, который возвращает набор предустановленных фильтров. В битриксе прописан такой дефолтный набор:

  • Bitrix\Main\Engine\ActionFilter\Authentication
  • Bitrix\Main\Engine\ActionFilter\HttpMethod
  • Bitrix\Main\Engine\ActionFilter\Csrf

Использование prefilters затирает набор дефолтных префильтров.

Использование +prefilters и -prefilters вместо переопределения списка позволяет добавлять/удалять новые к дефолтному списку.

Переопределив метод getDefaultPreFilters в своём контроллере, можно определить новый список дефолтных фильтров для всех Action своего контроллера при условии что в configureActions не будет использоваться prefilters, а только +prefilters и -prefilters.

Так например можно ввести Фильтр для контроллера, который на каждый Action будет проверять, что пользователь входит в определённую группу

class CheckUserGroupActionFilter extends \Bitrix\Main\Engine\ActionFilter\Base
{
    private array $userGroups;

    public function __construct(array $userGroups)
    {
        parent::__construct();

        $this->userGroups = $userGroups;
    }

    public function onBeforeAction($event): ?\Bitrix\Main\EventResult
    {
        $currentUserGroups = \Bitrix\Main\Engine\CurrentUser::get()->getUserGroups();
        if (empty(array_intersect($currentUserGroups, $this->userGroups))) {
            // если текущий пользователь не входит в разрешенные группы
            $this->addError(new \Bitrix\Main\Error('Access denied'));

            return new \Bitrix\Main\EventResult(
                \Bitrix\Main\EventResult::ERROR,
                null,
                null,
                $this
            );
        }

        return null;
    }
}

class MyController extends \Bitrix\Main\Engine\Controller
{
    public function getDefaultPreFilters(): array
    {
        // дополняем дефолтный набор фильтров своими фильтрами
        $defaultPreFilters = parent::getDefaultPreFilters();

        // на все Экшены контроллера навешивается проверка, что пользователь входит в эти группы
        $defaultPreFilters[] = new CheckUserGroupActionFilter([1, 2, 3]),

        return $defaultPreFilters;
    }

    public function configureAction(): array
    {
        return [
            // prefilters очистит набор предустановленных фильтров
            'first' => ['prefilters' => []],

            // +prefilters добавит указанные фильтры к предустановленному набору
            'second' => ['+prefilters' => [new class extends \Bitrix\Main\Engine\ActionFilter\Base {} ]]

            // -prefilters удаляет из набора фильтров те, которые относятся к переданным классам. Проверяется через is_a
            'third' => ['-prefilters' => [Bitrix\Main\Engine\ActionFilter\HttpMethod::class]]
        ];
    }

    public function firstAction(){}
    public function secondAction(){}
    public function thirdAction(){}

}
Хотите указать свой набор фильтров для контроллера — не используйте prefilters, иначе перезатрёте предустановленный набор.

Контроллеры модуля

Документация по контролеру

.settings.php в каталоге модуля
return [
    'controllers' => [
        'value' => [
            'namespaces' => [
                '\\Bitrix\\Disk\\CloudIntegration\\Controller' => 'cloud', //cloud - это алиас
            ],
            'defaultNamespace' => '\\Bitrix\\Disk\\Controller',
        ],
        'readonly' => true,
    ]
];