Контроллеры
Контроллер обрабатывает запрос, вызывает бизнес-логику и возвращает ответ. В Bitrix Framework за контроллеры отвечает класс Bitrix\Main\Engine\Controller. Действия контроллера — это методы с суффиксом Action, например listAction, getAction, addAction.
Действия контроллера можно вызывать через HTTP-маршрут или AJAX-диспетчер BX.ajax.runAction(). Оба способа показаны на отдельных контроллерах: Web\Iblock обрабатывает HTTP-запросы, а Ajax\Iblock — AJAX-вызовы.
Подготовить модуль
Материал объединяет HTTP- и AJAX-примеры в одном учебном проекте. Выберите подходящий сценарий:
-
изучить оба способа вызова — выполните примеры по порядку,
-
использовать только HTTP-маршруты — создайте
Web\Iblockи пропустите AJAX-разделы, -
использовать только AJAX-вызовы — начните с раздела Настроить AJAX-контроллер.
Для примеров используется учебный модуль my.content. Установите его перед созданием контроллеров.
-
Скачайте архив с модулем.
-
Распакуйте папку
my.contentв/local/modules/проекта. -
Установите модуль на странице Marketplace > Установленные решения в административном разделе.
Команды статьи используют пространство имен из /local/modules/my.content/.settings.php. Для собственного модуля замените my.content, префикс My\Content, предметное имя Iblock и идентификаторы действий my:content.Iblock.*.
Если пространство имен для генераторов не настроено, настройте его или создайте классы вручную по указанным путям. Добавьте нужные секции в существующий .settings.php и не заменяйте файл целиком.
Создать контроллер через консоль
Перед запуском команды перейдите в каталог, в котором находится файл bitrix.php.
Подробнее о консольном инструменте и справке по его командам читайте в статье Консольные команды.
Команда make:controller создает класс контроллера и заданные действия:
php bitrix.php make:controller <controller-name> \
-m <module-id> \
—actions=<action-names> \
[-C <context>] \
—no-interaction
Квадратные скобки обозначают необязательную часть команды. При запуске команды вводить их не нужно.
Параметры команды:
-
<controller-name>— имя контроллера. -
-m <module-id>— идентификатор модуля, в котором команда создает контроллер. -
—actions=<action-names>— имена действий через запятую. Для каждого имени команда создает метод с суффиксомAction. -
-C <context>— контекст контроллера: подпространство имен и соответствующий подкаталог.Без этого параметра команда создает контроллер в корневом пространстве имен модуля. Например, для контроллера
iblockиз модуляmy.contentкоманда создает файл/local/modules/my.content/lib/Infrastructure/Controller/Iblock.php. -
—no-interaction— отключает дополнительные запросы команды.
Контроллер может находиться в любом каталоге внутри lib, если класс доступен автозагрузке Bitrix Framework. В примерах используются контексты Web и Ajax. Команда создает для них отдельные пространства имен и подкаталоги, чтобы разделить HTTP- и AJAX-действия.
Создайте первый контроллер в модуле my.content:
php bitrix.php make:controller iblock \
-m my.content \
—actions=get \
-C Web \
—no-interaction
Команда создаст файл /local/modules/my.content/lib/Infrastructure/Controller/Web/Iblock.php с действием getAction().
Замените содержимое Web/Iblock.php на пример ниже.
-
Метод
init()из сгенерированного шаблона можно удалить, если в нем нет своей логики. -
Метод
configureActions()не нужен, потому что действие настроено атрибутами. -
Метод
getAutoWiredParameters()оставьте пустым. Он понадобится для собственных классов в разделе Автоваринг.
Авторизация и ограничение HTTP-метода заданы атрибутами над getAction(). Не настраивайте это же действие одновременно через configureActions(), иначе контроллер вернет ошибку Invalid configuration of actions.
<?php
namespace My\Content\Infrastructure\Controller\Web;
use Bitrix\Iblock\IblockTable;
use Bitrix\Main\Engine\ActionFilter\HttpMethod;
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
final class Iblock extends Controller
{
public function getAutoWiredParameters(): array
{
return [];
}
#[\Bitrix\Main\Engine\ActionFilter\Attribute\Rule\Authentication]
#[\Bitrix\Main\Engine\ActionFilter\Attribute\Rule\HttpMethod([HttpMethod::METHOD_GET])]
public function getAction(int $id = 0): ?array
{
if (!Loader::includeModule('iblock'))
{
$this->addError(new Error('Iblock module is not installed', 'IBLOCK_MODULE_NOT_INSTALLED'));
return null;
}
$query =
IblockTable::query()
->setSelect(['ID', 'NAME', 'IBLOCK_TYPE_ID'])
->setLimit(1)
;
if ($id > 0)
{
$query->where('ID', $id);
}
$iblock = $query->fetch();
if (!$iblock)
{
$this->addError(new Error('Iblock not found', 'IBLOCK_NOT_FOUND'));
return null;
}
return $iblock;
}
}
В примере действие get возвращает данные инфоблока. Оно доступно только авторизованным пользователям через GET-запрос. Если $id равен 0, действие возвращает первый найденный инфоблок. Если $id больше 0, действие возвращает инфоблок с этим идентификатором.
Зарегистрировать контроллер
Создать класс контроллера недостаточно. Нужно зарегистрировать точку входа, через которую Bitrix Framework начнет обрабатывать действие.
Для контроллера доступны два варианта регистрации:
-
HTTP-маршрут в
/local/routes/web.php, -
AJAX-настройка пространства имен в
/local/modules/my.content/.settings.php.
Сначала зарегистрируйте созданный Web\Iblock как HTTP-обработчик. AJAX-контроллер будет добавлен в разделе ниже.
Настроить HTTP-маршрут
Создайте файл /local/routes/web.php и зарегистрируйте маршруты для HTTP-примеров статьи:
<?php
use Bitrix\Main\Routing\RoutingConfigurator;
use My\Content\Infrastructure\Controller\Web\Iblock;
return static function (RoutingConfigurator $routes)
{
$routes->get('/content/iblock/{id}/', [Iblock::class, 'get'])
->where('id', '[0-9]+');
$routes->get('/content/export/{type}/', [Iblock::class, 'export'])
->where('type', '[a-z0-9_-]+');
$routes->get('/content/photo/resize/', [Iblock::class, 'resizePhoto']);
$routes->get('/content/photo/', [Iblock::class, 'downloadPhoto']);
$routes->get('/content/render/view/', [Iblock::class, 'renderView']);
$routes->get('/content/render/component/{id}/', [Iblock::class, 'renderComponent']);
$routes->get('/content/render/extension/{id}/', [Iblock::class, 'renderExtension']);
$routes->get('/content/error/{id}/', [Iblock::class, 'error']);
$routes->get('/content/autowire/{id}/', [Iblock::class, 'autowire']);
$routes->get('/content/service/', [Iblock::class, 'service']);
};
Первый маршрут вызывает созданное действие getAction(). Остальные маршруты понадобятся для HTTP-примеров в разделах ниже.
Не вызывайте остальные маршруты, пока не добавите соответствующие методы в Web\Iblock.
Если файл маршрутов еще не подключен, добавьте секцию routing в /bitrix/.settings.php. Сохраните остальные настройки файла:
<?php
return [
// Другие настройки системы
'routing' => [
'value' => [
'config' => ['web.php'],
],
'readonly' => true,
],
];
Система будет искать пользовательские маршруты в файле /local/routes/web.php.
Подробнее о настройке веб-сервера и маршрутах читайте в статье Роутинг.
Чтобы получить успешный ответ, создайте хотя бы один инфоблок. Затем проверьте вызов через HTTP. Контроллер проверяет авторизацию, поэтому передайте логин и пароль пользователя через Basic Authentication:
curl —request GET \
—url 'http://localhost/content/iblock/0/' \
—user 'login:password'
Контроллер вернет JSON-ответ:
{"status":"success","data":{"ID":"1","NAME":"Новости","IBLOCK_TYPE_ID":"news"},"errors":[]}
При значении 0 контроллер вернет первый найденный инфоблок. Если на сайте нет инфоблоков, ответ будет содержать ошибку IBLOCK_NOT_FOUND. Значения полей зависят от инфоблока на сайте.
Настроить AJAX-контроллер
Не регистрируйте один контроллер одновременно для HTTP-маршрутов и AJAX-вызовов. У этих точек входа разные способы вызова. Возможность вызвать одно и то же действие двумя способами создает путаницу и затрудняет отладку. Отдельные контроллеры позволяют независимо настраивать действия и не смешивать HTTP- и AJAX-сценарии. Поэтому для AJAX-действий создайте отдельный Ajax\Iblock.
Создать класс AJAX-контроллера
Если в проекте нужны только AJAX-вызовы, создавать Web\Iblock и настраивать HTTP-маршруты не требуется. Создайте один AJAX-контроллер и зарегистрируйте его пространство имен в .settings.php.
Создайте контроллер Iblock с действием ping в контексте Ajax:
php bitrix.php make:controller iblock \
-m my.content \
—actions=ping \
-C Ajax \
—no-interaction
Команда создаст файл /local/modules/my.content/lib/Infrastructure/Controller/Ajax/Iblock.php. Замените его содержимое на пример:
<?php
namespace My\Content\Infrastructure\Controller\Ajax;
use Bitrix\Main\Engine\Controller;
final class Iblock extends Controller
{
public function pingAction(): array
{
return [
'transport' => 'ajax',
];
}
}
pingAction() — минимальное действие для проверки точки входа.
Все AJAX-действия в статье учебные. Перед использованием в рабочем проекте настройте проверку авторизации для действий с защищенными данными, а для изменяющих действий ограничьте допустимый HTTP-метод.
Подробнее о настройке читайте в статье Пре- и постфильтры.
Настроить AJAX-вызовы
AJAX-диспетчер находит контроллер по пространству имен из секции controllers и идентификатору действия. Добавьте или обновите секцию controllers в файле /local/modules/my.content/.settings.php. Сохраните остальные настройки файла:
<?php
return [
// Другие настройки модуля
'controllers' => [
'value' => [
'defaultNamespace' => '\\My\\Content\\Infrastructure\\Controller\\Ajax',
],
'readonly' => true,
],
];
После настройки вызовите pingAction() по идентификатору my:content.Iblock.ping. Откройте страницу сайта с подключенным JS-ядром Bitrix и выполните код в консоли браузера:
BX.ajax.runAction('my:content.Iblock.ping')
.then((response) => console.log(response));
Контроллер вернет JSON-ответ:
{"status":"success","data":{"transport":"ajax"},"errors":[]}
Способ вызова определяет точку входа, но не меняет устройство действия. Контроллер получает параметры, выполняет метод с суффиксом Action и формирует ответ.
В учебном проекте методы распределены между Web\Iblock и Ajax\Iblock. При добавлении собственных действий учитывайте точку входа и выбирайте совместимый с ней тип ответа.
Жизненный цикл контроллера
При вызове действия система выполняет шаги:
-
Создает экземпляр конкретного контроллера.
-
Вызывает
Controller::init(). -
Создает объект действия по имени
*Action. -
Выполняет
Controller::prepareParams(). -
Выполняет
Controller::processBeforeAction($action). -
Вызывает событие
onBeforeAction. Обработчик может отменить выполнение. -
Вызывает метод действия
actionNameAction(...). -
Вызывает событие
onAfterAction. -
Выполняет
Controller::processAfterAction($action, $result). -
Формирует ответ.
-
Выполняет
Controller::finalizeResponse($response). -
Отправляет ответ пользователю.
При необходимости переопределите методы init(), processBeforeAction(), processAfterAction() и finalizeResponse().
Реквесты
Аргументы методов с суффиксом Action формируются из запроса по имени и типу.
Добавьте методы из примеров кода в файл /local/modules/my.content/lib/Infrastructure/Controller/Ajax/Iblock.php.
final class Iblock extends Controller
{
// ...
public function getByCodeAction(string $code): array
{
return [
'code' => $code,
];
}
public function listAction(int $limit = 10, ?int $categoryId = null): array
{
return [
'limit' => $limit,
'categoryId' => $categoryId,
];
}
}
Вызовите действия из консоли браузера:
BX.ajax.runAction('my:content.Iblock.getByCode', {
data: { code: 'news' }
}).then((response) => console.log(response));
BX.ajax.runAction('my:content.Iblock.list', {
data: { limit: 20, categoryId: 5 }
}).then((response) => console.log(response));
В первом вызове code передает обязательный символьный код. Во втором вызове limit и categoryId передают необязательные параметры.
Первый вызов вернет переданный символьный код:
{"status":"success","data":{"code":"news"},"errors":[]}
Второй вызов вернет переданные значения ограничения и категории:
{"status":"success","data":{"limit":20,"categoryId":5},"errors":[]}
Правила:
-
если обязательный параметр отсутствует, система вернет ошибку
Could not find value for parameter, -
если у параметра есть значение по умолчанию, действие использует это значение,
-
если тип значения не совпадает с типом аргумента, система вернет ошибку
Invalid value to match with parameter.
Получить текущего пользователя
Чтобы получить текущего пользователя, добавьте аргумент Bitrix\Main\Engine\CurrentUser в сигнатуру метода-действия контроллера:
final class Iblock extends Controller
{
// ...
public function currentUserAction(\Bitrix\Main\Engine\CurrentUser $user): ?array
{
if (empty($user->getId()))
{
$this->addError(new \Bitrix\Main\Error('Need authenticated'));
return null;
}
return [
'userId' => $user->getId(),
];
}
}
Чтобы проверить действие, выполните код в консоли браузера на странице сайта:
BX.ajax.runAction('my:content.Iblock.currentUser')
.then((response) => console.log(response));
Контроллер вернет идентификатор текущего пользователя:
{"status":"success","data":{"userId":"1"},"errors":[]}
Если сессия не авторизована, ответ будет содержать ошибку Need authenticated.
Получить JSON-тело запроса
Чтобы получить данные из тела запроса с Content-Type: application/json, добавьте аргумент Bitrix\Main\Engine\JsonPayload.
final class Iblock extends Controller
{
// ...
public function jsonAction(\Bitrix\Main\Engine\JsonPayload $json): array
{
return [
'name' => $json->getDataList()->get('name'),
'type' => $json->getDataList()->get('type'),
];
}
}
Чтобы проверить действие, передайте JSON в параметре json:
BX.ajax.runAction('my:content.Iblock.json', {
json: {
name: 'Новости',
type: 'news'
}
}).then((response) => console.log(response));
Параметр json передает данные в теле запроса с Content-Type: application/json.
Контроллер вернет JSON-ответ:
{
"status": "success",
"data": {
"name": "Новости",
"type": "news"
},
"errors": []
}
Получить постраничную навигацию
Чтобы получить объект навигации, добавьте аргумент Bitrix\Main\UI\PageNavigation:
final class Iblock extends Controller
{
// ...
public function paginationAction(\Bitrix\Main\UI\PageNavigation $pagination): array
{
return [
'page' => $pagination->getCurrentPage(),
'size' => $pagination->getPageSize(),
'limit' => $pagination->getLimit(),
'offset' => $pagination->getOffset(),
];
}
}
Чтобы проверить действие, передайте навигацию в поле navigation:
BX.ajax.runAction('my:content.Iblock.pagination', {
navigation: {
page: 3,
size: 33
}
}).then((response) => console.log(response));
Bitrix Framework создаст объект PageNavigation. Контроллер вернет номер и размер страницы, лимит и смещение:
{"status":"success","data":{"page":3,"size":33,"limit":33,"offset":66},"errors":[]}
Размер страницы применяется, если значение входит в разрешенный диапазон от 1 до 50.
О классе PageNavigation читайте в статье Постраничная навигация.
DTO реквеста
DTO реквеста описывает входные данные, применяет валидацию и передает подготовленный объект в действие. Для подключения DTO к действию используется автоваринг.
В имени IblockCreateRequest часть Iblock обозначает предметную область. В собственном модуле замените ее и используйте тот же принцип для классов IblockData и IblockService из следующих разделов.
Создайте реквест командой make:request.
php bitrix.php make:request IblockCreate -m my.content —fields=name,type,description —no-interaction
Команда использует AJAX-пространство имен из .settings.php и создаст файл /local/modules/my.content/lib/Infrastructure/Controller/Ajax/Request/IblockCreateRequest.php.
Сгенерированное содержимое уже подходит для передачи данных в действие. Чтобы добавить валидацию, дополните файл атрибутами NotEmpty и Length, как в примере ниже.
Если создаете файл вручную, используйте тот же путь.
<?php
namespace My\Content\Infrastructure\Controller\Ajax\Request;
use Bitrix\Main\Validation\Rule\Length;
use Bitrix\Main\Validation\Rule\NotEmpty;
final class IblockCreateRequest
{
public function __construct(
#[NotEmpty]
public readonly ?string $name,
#[NotEmpty]
public readonly ?string $type,
#[Length(max: 10000)]
public readonly ?string $description,
)
{
}
public static function createFromRequest(\Bitrix\Main\Request $request): self
{
return new self(
$request->get('name'),
$request->get('type'),
$request->get('description'),
);
}
}
Зарегистрируйте DTO в контроллере через getAutoWiredParameters():
use My\Content\Infrastructure\Controller\Ajax\Request\IblockCreateRequest;
final class Iblock extends Controller
{
// ...
public function getAutoWiredParameters(): array
{
return [
new \Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter(
IblockCreateRequest::class,
fn () => IblockCreateRequest::createFromRequest($this->getRequest()),
),
];
}
public function createAction(IblockCreateRequest $request): array
{
return [
'name' => $request->name,
'type' => $request->type,
'description' => $request->description,
];
}
}
Валидация выполняется до вызова действия. При ошибках действие не запускается.
Подробнее о правилах автоваринга смотрите в разделе Автоваринг.
Чтобы проверить DTO, вызовите createAction() через AJAX:
BX.ajax.runAction('my:content.Iblock.create', {
data: {
name: 'Новости',
type: 'news',
description: 'Раздел новостей'
}
}).then((response) => console.log(response));
Контроллер вернет JSON-ответ:
{
"status": "success",
"data": {
"name": "Новости",
"type": "news",
"description": "Раздел новостей"
},
"errors": []
}
Респонсы
Контроллер поддерживает разные типы ответов:
-
Bitrix\Main\Engine\Response\AjaxJson, -
Bitrix\Main\HttpResponse, -
Bitrix\Main\Engine\Response\File, -
Bitrix\Main\Engine\Response\BFile, -
Bitrix\Main\Engine\Response\ResizedImage, -
Bitrix\Main\Engine\Response\Component, -
Bitrix\Main\Engine\Response\HtmlContent, -
Bitrix\Main\Engine\Response\Json, -
Bitrix\Main\Engine\Response\OpenDesktopApp, -
Bitrix\Main\Engine\Response\OpenMobileApp, -
Bitrix\Main\Engine\Response\Redirect, -
Bitrix\Main\Engine\Response\Zip\Archive.
Добавьте методы из примеров раздела в /local/modules/my.content/lib/Infrastructure/Controller/Web/Iblock.php. Маршруты для проверки уже добавлены в /local/routes/web.php.
Вернуть JSON
Этот тип используется по умолчанию. Явно создавайте Json, если нужен ответ без полей status и errors.
Вернуть файлы
Чтобы вернуть файлы, используйте классы Response\File и Response\BFile. Response\File возвращает файл с диска, а Response\BFile — файл из таблицы файлов.
В первом примере Bitrix\Main\IO\File получает файл с диска, а Response\File формирует ответ.
Маршрут /content/export/{type}/ передает символьный тип экспорта в параметр $type действия exportAction().
use Bitrix\Main\Application;
use Bitrix\Main\Engine\Response\File as FileResponse;
use Bitrix\Main\Error;
use Bitrix\Main\IO\File;
final class Iblock extends Controller
{
// ...
public function exportAction(string $type): ?FileResponse
{
$filePath = Application::getDocumentRoot() . '/upload/export/iblock-' . $type . '.csv';
$file = new File($filePath);
if (!$file->isExists())
{
$this->addError(new Error('Export file not found', 'EXPORT_FILE_NOT_FOUND'));
return null;
}
return new FileResponse($file->getPath(), $file->getName(), 'text/csv');
}
}
Чтобы проверить пример, создайте файл /upload/export/iblock-news.csv и откройте /content/export/news/. Контроллер вернет созданный файл.
Пример с Response\BFile:
use Bitrix\Main\Error;
use Bitrix\Main\Engine\Response\BFile;
use Bitrix\Main\UserTable;
use Bitrix\Main\Engine\CurrentUser;
final class Iblock extends Controller
{
// ...
#[\Bitrix\Main\Engine\ActionFilter\Attribute\Rule\Authentication]
public function downloadPhotoAction(CurrentUser $user): ?BFile
{
$user = UserTable::getList([
'select' => ['PERSONAL_PHOTO'],
'filter' => ['ID' => $user->getId()],
])->fetch();
if (!$user || empty($user['PERSONAL_PHOTO']))
{
$this->addError(new Error('User photo not found', 'USER_PHOTO_NOT_FOUND'));
return null;
}
return BFile::createByFileId((int)$user['PERSONAL_PHOTO']);
}
}
Для проверки добавьте фотографию текущему пользователю. Если фотографии нет, контроллер вернет ошибку USER_PHOTO_NOT_FOUND.
Откройте /content/photo/, чтобы получить фотографию текущего пользователя.
Вернуть изображение
Используйте Response\ResizedImage, чтобы вернуть изображение заданного размера:
use Bitrix\Main\Error;
use Bitrix\Main\Engine\Response\ResizedImage;
use Bitrix\Main\UserTable;
use Bitrix\Main\Engine\CurrentUser;
final class Iblock extends Controller
{
// ...
#[\Bitrix\Main\Engine\ActionFilter\Attribute\Rule\Authentication]
public function resizePhotoAction(CurrentUser $user): ?ResizedImage
{
$user = UserTable::getList([
'select' => ['PERSONAL_PHOTO'],
'filter' => ['ID' => $user->getId()],
])->fetch();
if (!$user || empty($user['PERSONAL_PHOTO']))
{
$this->addError(new Error('User photo not found', 'USER_PHOTO_NOT_FOUND'));
return null;
}
return ResizedImage::createByImageId((int)$user['PERSONAL_PHOTO'], 100, 100);
}
}
Откройте /content/photo/resize/, чтобы получить фотографию текущего пользователя размером 100 на 100 пикселей.
Не передавайте размеры изображения напрямую из запроса. Используйте фиксированные значения или подписанные параметры.
Использовать рендеринг
Рендеринг выводит HTML-страницы, компоненты или JS-расширения из контроллера.
Функционал рендеринга доступен с версии 25.700.0 главного модуля.
Добавьте методы из примеров раздела в файл /local/modules/my.content/lib/Infrastructure/Controller/Web/Iblock.php. Исключение — componentAction() из подраздела Отрисовать компонент для AJAX. Его нужно добавить в Ajax/Iblock.php.
Методы renderView() и renderComponent() возвращают HTML-ответ на основе HttpResponse и предназначены для HTTP-маршрутов. Не вызывайте действия с такими ответами через BX.ajax.runAction(): он ожидает JSON. Чтобы вернуть результат компонента через AJAX, используйте renderComponentAjax() — он формирует JSON-ответ с HTML и подключаемыми ресурсами.
Отрисовать представление
Метод renderView() возвращает HTML из файла представления. Переменные из второго аргумента доступны в шаблоне как обычные PHP-переменные.
final class Iblock extends Controller
{
// ...
public function renderViewAction(): \Bitrix\Main\Engine\Response\Render\View
{
return $this->renderView('content/index', [
'title' => 'Новости',
]);
}
}
Откройте URL в браузере:
http://localhost/content/render/view/
Контроллер вернет HTML-страницу с заголовком Новости.
Система ищет шаблон по пути /local/modules/my.content/views/content/index.php. Этот файл уже есть в учебном модуле:
<?php
/** @var string $title */
?>
<h1><?= htmlspecialcharsbx($title) ?></h1>
Чтобы отключить общий шаблон сайта, замените return $this->renderView(...) в методе renderViewAction() на вызов с withSiteTemplate: false.
return $this->renderView('content/index', [
'title' => 'Новости',
], withSiteTemplate: false);
Отрисовать компонент
Если страница состоит из одного компонента, используйте renderComponent(). Отдельное представление не требуется.
Компонент my.content:iblock.view есть в учебном модуле.
Параметры метода:
-
$name— символьное имя компонента, обязательный параметр, -
$template— название шаблона компонента, -
$params— ассоциативный массив параметров для передачи в компонент.
final class Iblock extends Controller
{
// ...
public function renderComponentAction(int $id): \Bitrix\Main\Engine\Response\Render\Component
{
return $this->renderComponent('my.content:iblock.view', '', [
'IBLOCK_ID' => $id,
]);
}
}
Откройте URL в браузере и замените 1 на ID существующего инфоблока:
http://localhost/content/render/component/1/
Контроллер вернет HTML-страницу с результатом работы компонента my.content:iblock.view.
Отрисовать компонент для AJAX
Метод renderComponentAjax() формирует ответ для обработки в браузере.
Добавьте componentAction() в /local/modules/my.content/lib/Infrastructure/Controller/Ajax/Iblock.php.
Первые три параметра совпадают с параметрами renderComponent(). Дополнительно метод принимает:
-
$additionalResponseParams— дополнительные данные, которые попадают в ответ, -
$dataKeys— список ключей из результата работы компонента, которые попадают в ответ.
final class Iblock extends Controller
{
// ...
public function componentAction(int $id): \Bitrix\Main\Engine\Response\Component
{
return $this->renderComponentAjax('my.content:iblock.view', '', [
'IBLOCK_ID' => $id,
]);
}
}
Чтобы проверить пример, вызовите действие из консоли браузера. Замените 1 на ID существующего инфоблока:
BX.ajax.runAction('my:content.Iblock.component', {
data: { id: 1 }
}).then((response) => console.log(response));
Пример структуры ответа:
{
status: 'success',
data: {
html: 'HTML код компонента',
assets: {
js: [],
css: [],
string: []
},
additionalParams: {},
componentResult: {}
},
errors: []
}
Отрисовать расширение
Для страниц, которые выводят только JS-расширение, используйте renderExtension():
final class Iblock extends Controller
{
// ...
public function renderExtensionAction(int $id): \Bitrix\Main\Engine\Response\Render\Extension
{
return $this->renderExtension('my.content.iblock.editor', [
'iblockId' => $id,
]);
}
}
Откройте URL в браузере:
http://localhost/content/render/extension/1/
Контроллер вернет страницу, на которой подключено JS-расширение my.content.iblock.editor. В конце страницы появится текст Iblock editor: 1.
Если открыть URL http://localhost/content/render/extension/2/, текст изменится на Iblock editor: 2. Так можно проверить, что значение из маршрута передано в метод контроллера и использовано в расширении.
Если страница пустая, проверьте, что при установке модуля расширение скопировалось в /local/js/my/content/iblock/editor/, в браузер загрузилась актуальная версия файла dist/editor.bundle.js и в консоли нет JavaScript-ошибок. В учебном расширении скрипт сам создает контейнер, если страница от renderExtension() открылась без body.
При сборке расширения запись файла скрипта в config.php формируется автоматически. Точку входа controllerEntrypoint укажите вручную:
<?php
return [
'js' => './dist/editor.bundle.js',
'rel' => [
'main.core',
],
'skip_core' => false,
'controllerEntrypoint' => 'MyContent.Iblock.Editor.render',
];
Расширение my.content.iblock.editor есть в учебном модуле.
Рендеринг расширений работает в браузере. Это не Server-Side Rendering. Используйте его для интерфейсов без требований к SEO.
Обработать ошибки
Чтобы вернуть ошибку в стандартном JSON-ответе, добавьте объект Bitrix\Main\Error через addError():
Добавьте метод в /local/modules/my.content/lib/Infrastructure/Controller/Web/Iblock.php.
final class Iblock extends Controller
{
// ...
public function errorAction(int $id): ?array
{
if ($id <= 0)
{
$this->addError(new \Bitrix\Main\Error('Iblock id is required', 'IBLOCK_ID_REQUIRED'));
return null;
}
return [
'success' => true,
];
}
}
Откройте /content/error/0/, чтобы получить ответ с ошибкой.
Ответ при ошибке:
{
"status": "error",
"data": null,
"errors": [
{
"message": "Iblock id is required",
"code": "IBLOCK_ID_REQUIRED"
}
]
}
Для отладки включите режим разработки в /bitrix/.settings.php:
'exception_handling' => [
'value' => [
'debug' => true,
],
'readonly' => false,
],
В режиме разработки система добавляет в ошибки стек вызовов. Не включайте этот режим на рабочем сайте.
Автоваринг
Автоваринг автоматически создает или получает объект и подставляет его в аргумент действия контроллера.
Добавьте методы из примеров раздела в /local/modules/my.content/lib/Infrastructure/Controller/Web/Iblock.php.
Bitrix Framework поддерживает встроенный автоваринг для CurrentUser, JsonPayload и PageNavigation. Для собственных классов добавьте правило в getAutoWiredParameters() или зарегистрируйте сервис в Bitrix\Main\DI\ServiceLocator.
Настроить автоваринг кастомных классов
Чтобы преобразовать входной параметр в объект, добавьте правило в getAutoWiredParameters().
Создайте файл /local/modules/my.content/lib/Value/IblockData.php. Класс My\Content\Value\IblockData хранит данные инфоблока:
<?php
namespace My\Content\Value;
final class IblockData
{
public function __construct(
public readonly int $id,
public readonly string $name,
public readonly string $type,
)
{
}
}
ExactParameter использует строгое имя аргумента. Замените пустой массив в getAutoWiredParameters() класса Web\Iblock на правило из примера:
use Bitrix\Iblock\IblockTable;
use Bitrix\Main\Engine\AutoWire\ExactParameter;
use Bitrix\Main\Loader;
use My\Content\Value\IblockData;
final class Iblock extends Controller
{
// ...
public function getAutoWiredParameters(): array
{
return [
new ExactParameter(
IblockData::class,
'iblock',
static function (string $className, int $id): ?IblockData
{
if (!Loader::includeModule('iblock'))
{
return null;
}
$row = IblockTable::getRow([
'select' => ['ID', 'NAME', 'IBLOCK_TYPE_ID'],
'filter' => ['=ID' => $id],
]);
if (!$row)
{
return null;
}
return new IblockData(
(int)$row['ID'],
(string)$row['NAME'],
(string)$row['IBLOCK_TYPE_ID'],
);
}
),
];
}
public function autowireAction(IblockData $iblock): array
{
return [
'id' => $iblock->id,
'name' => $iblock->name,
'type' => $iblock->type,
];
}
}
Откройте URL в браузере. Вместо 1 укажите ID существующего инфоблока:
http://localhost/content/autowire/1/
Контроллер вернет JSON-ответ:
{
"status": "success",
"data": {
"id": 1,
"name": "Новости",
"type": "news"
},
"errors": []
}
Если объект не создан, система вернет ошибку Could not construct parameter {iblock}. Для валидации параметров используйте Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter.
Подробнее о правилах валидации читайте в статье Валидация.
Использовать сервис-локатор
Если объект нельзя получить с помощью встроенного автоваринга или правила getAutoWiredParameters(), система пробует получить его из Bitrix\Main\DI\ServiceLocator.
Добавьте serviceAction() в /local/modules/my.content/lib/Infrastructure/Controller/Web/Iblock.php.
Создайте файл /local/modules/my.content/lib/Service/IblockService.php.
<?php
namespace My\Content\Service;
use Bitrix\Iblock\IblockTable;
use Bitrix\Main\Loader;
final class IblockService
{
public function getFirstIblock(): ?array
{
if (!Loader::includeModule('iblock'))
{
return null;
}
$row = IblockTable::getRow([
'select' => ['ID', 'NAME', 'IBLOCK_TYPE_ID'],
'order' => ['ID' => 'ASC'],
]);
if (!$row)
{
return null;
}
return [
'id' => (int)$row['ID'],
'name' => (string)$row['NAME'],
'type' => (string)$row['IBLOCK_TYPE_ID'],
];
}
}
Использование сервиса в контроллере:
use My\Content\Service\IblockService;
final class Iblock extends Controller
{
// ...
public function serviceAction(IblockService $service): ?array
{
$iblock = $service->getFirstIblock();
if (!$iblock)
{
$this->addError(new \Bitrix\Main\Error('Iblock not found', 'IBLOCK_NOT_FOUND'));
return null;
}
return $iblock;
}
}
Откройте URL в браузере:
http://localhost/content/service/
Контроллер вернет JSON-ответ с первым инфоблоком по возрастанию ID:
{
"status": "success",
"data": {
"id": 1,
"name": "Новости",
"type": "news"
},
"errors": []
}
Если класс не зарегистрирован в контейнере, система создаст его напрямую. Для управления жизненным циклом зарегистрируйте сервис в DI.
Подробнее читайте в статье Service Locator.
Проверить итоговую структуру
Схема ниже соответствует полному сценарию статьи. Для HTTP-сценария используйте ветку Web и файл /local/routes/web.php. Для AJAX-сценария используйте ветку Ajax и секцию controllers в .settings.php.
/local/
├── routes/
│ └── web.php
│ # HTTP-маршруты для Web\Iblock
└── modules/my.content/
├── .settings.php
│ # Пространство имен AJAX-контроллеров
└── lib/
├── Infrastructure/Controller/
│ ├── Web/
│ │ └── Iblock.php
│ │ # HTTP-действия и автоваринг IblockData
│ └── Ajax/
│ ├── Iblock.php
│ │ # AJAX-действия и автоваринг IblockCreateRequest
│ └── Request/
│ └── IblockCreateRequest.php
│ # DTO входных данных для createAction()
├── Service/
│ └── IblockService.php
│ # Получение данных инфоблока
└── Value/
└── IblockData.php
# Данные инфоблока для автоваринга
Методы распределены между контроллерами:
-
Web\Iblock—getAction(),exportAction(),downloadPhotoAction(),resizePhotoAction(),renderViewAction(),renderComponentAction(),renderExtensionAction(),errorAction(),autowireAction()иserviceAction(). -
Ajax\Iblock—pingAction(),getByCodeAction(),listAction(),currentUserAction(),jsonAction(),paginationAction(),createAction()иcomponentAction().
У каждого контроллера свой метод getAutoWiredParameters(). В Web\Iblock он создает IblockData, а в Ajax\Iblock — проверяет IblockCreateRequest. Не объединяйте правила двух контроллеров в одном методе.