Блог Горошко Андрея 1C-Битрикс,PHP,Разработка Kontrollery: Руководство разработчика

Kontrollery: Руководство разработчика

Оптимизация производительности и безопасности — залог успешного проекта на 1С-Битрикс. Данный материал посвящен практическому применению «Kontrollery: Руководство разработчика». Мы подготовили подробный разбор принципов работы и развернутые примеры интеграции.

—
title: Контроллеры
—

В MVC-архитектуре контроллеры — это связующее звено между моделью и представлением. Контроллер:

  • обрабатывает входящие запросы от пользователя, например, нажатие кнопки или ввод данных,
  • взаимодействует с моделью для получения или изменения данных
  • передает результаты представлению для отображения.

Контроллеры состоят из методов, называемых действиями, которые выполняют основную логику и возвращают данные. Например, метод greetAction может возвращать приветственное сообщение.

Именование контроллеров и действий

PSR-4 в AJAX-контроллерах поддерживается с версии 20.600.87 главного модуля.

Контроллеры и их действия именуются по определенному шаблону. Имена параметров и контроллеров чувствительны к регистру, а имена действий — нет.

Примеры именования:

  • \Bitrix\Disk\Controller\Folder::getAction() преобразуется в bitrix:disk.Controller.Folder.get.
  • \Qsoft\Somedisk\Controller\SuperFolder::getAction() преобразуется в qsoft:somedisk.Controller.SuperFolder.get.

Здесь \Bitrix\Disk\Controller\Folder и \Qsoft\Somedisk\Controller\SuperFolder — это классы контроллеров, а getAction — это действие в этих классах.

Формат имени:

  • Имя действия формируется по шаблону: vendor:module.Controller.Action.
  • vendor: — это часть, которая указывает на поставщика или разработчика модуля. Например, bitrix: или qsoft:.
  • module — это название модуля, в котором находится контроллер.
  • Controller — это часть пространства имен и название контроллера.
  • Action — это конкретное действие, которое выполняется.

Особенности:

  • Если vendor: не указан, по умолчанию используется bitrix:.
  • Если задан defaultNamespace, его можно не указывать в имени действия, так как система автоматически его подставит.

Действия в контроллерах

Контроллеры состоят из действий, которые пользователь запрашивает для получения результата. В одном контроллере может быть одно или несколько действий.

Пример контроллера

Рассмотрим пример контроллера с действиями item.add и item.view в модуле example.

1. Создайте файл .settings.php в корне модуля.

   <?php
   //modules/vendor.example/.settings.php
   return [
   	'controllers' => [
   		'value' => [
   			'defaultNamespace' => '\\Vendor\\Example\\Controller',
   		],
   		'readonly' => true,
   	]
   ];

2. Создайте файл контроллера /modules/vendor.example/lib/controller/item.php.

   namespace Vendor\Example\Controller;
   use \Bitrix\Main\Error;
   
   class Item extends \Bitrix\Main\Engine\Controller
   {
       public function addAction(array $fields):? array
       {
           $item = Item::add($fields);
           if (!$item) {
               $this->addError(new Error('Could not create item.', {код_ошибки}));
               return null;
           }
           return $item->toArray();
       }
   
       public function viewAction($id):? array
       {
           $item = Item::getById($id);
           if (!$item) {
               $this->addError(new Error('Could not find item.', {код_ошибки}));
               return null;
           }
           return $item->toArray();
       }
   }

Действие addAction

1. Действие пытается создать Item из переданных $fields.

2. При ошибке возвращает null и добавляет ошибку в контроллер.

   {
   	"status": "error", 
   	"data": null,
   	"errors": [
   		{
   			"message": "Could not create item.",
   			"code": {код}
   		}
   	]
   }

3. При успехе возвращает массив с данными Item.

   {
   	"status": "success",
   	"data": {
   		"ID": 1,
   		"NAME": "Nobody",
   		//...поля элемента
   	},
   	"errors": null
   }

Действие viewAction

1. Действие пытается загрузить Item по $id. Параметр $id будет автоматически получен из $_POST['id'] или $_GET['id'].

2. При ошибке возвращает null с соответствующей ошибкой.

   {
   	"status": "error",
   	"data": null,
   	"errors": [
   		{
   			"message": "Could not find value for parameter {id}",
   			"code": 0
   		}
   	]
   }

Как обратиться к действию контроллера

Для вызова действий используйте соглашение по именованию:

  • Item::addAction -> vendor:example.Item.add
  • Item::viewAction -> vendor:example.Item.view

Пример вызова через BX.ajax.runAction:

BX.ajax.runAction('vendor:example.Item.add', {
	data: {
		fields: {
			ID: 1,
			NAME: "test"
		} 
	}
}).then(function (response) {
	console.log(response);
	/**
	{
		"status": "success", 
		"data": {
			"ID": 1,
			"NAME": "test"
		}, 
		"errors": []
	}
	**/			
}, function (response) {
	//все ответы, у которых status !== 'success'
	console.log(response);
	/**
	{
		"status": "error", 
		"errors": [...]
	}
	**/				
});

Можно получить ссылку на действие и выполнить HTTP-запрос самостоятельно.

/** @var \Bitrix\Main\Web\Uri $uri **/
$uri = \Bitrix\Main\Engine\UrlManager::getInstance()->create('vendor:example.Item.view', ['id' => 1]);
echo $uri;
// /bitrix/services/main/ajax.php?action=vendor:example.Item.view&id=1
// выполняем GET-запрос

Как создавать контроллеры и действия

Контроллеры:

  • должны быть унаследованы от \Bitrix\Main\Engine\Controller или его потомков.
  • могут располагаться в модуле или внутри компонента в файле ajax.php.

Требования к действиям:

  • методы должны быть public и оканчиваться на Action,
   namespace Vendor\Example\Controller;
   class Item extends \Bitrix\Main\Engine\Controller
   {
   	public function addAction(array $fields)
   	{
   		//...
   	}
   }
  • могут возвращать:
  • данные для JSON-ответа,
  • объект \Bitrix\Main\HttpResponse или его наследников,
  • объекты, которые реализуют интерфейсы: \JsonSerializable, \Bitrix\Main\Type\Contract\Arrayable и \Bitrix\Main\Type\Contract\Jsonable.

Как создавать классы-действия

Классы-действия наследуются от \Bitrix\Main\Engine\Action и позволяют повторно использовать логику в нескольких контроллерах.

class Test extends \Bitrix\Main\Engine\Controller
{
    public function configureActions()
    {
        return [
            'testoPresto' => [
                'class' => \TestAction::class,
                'configure' => ['who' => 'Me'],
            ],
        ];
    }
}

class TestAction extends \Bitrix\Main\Engine\Action
{
    protected $who;

    public function configure($params)
    {
        parent::configure($params);
        $this->who = $params['who'] ?: 'nobody';
    }

    public function run($objectId = null)
    {
        return "Test action! Know object {$objectId}? Mr. {$this->who}";
    }
}

Жизненный цикл контроллера

1. Создание контроллера

2. Инициализация Controller::init()

3. Создание объекта действия. Если не удалось, выбрасывается исключение

4. Подготовка параметров Controller::prepareParams

5. Метод Controller::processBeforeAction(Action $action)

6. Событие onBeforeAction

7. Выполнение действия

8. Событие onAfterAction

9. Метод Controller::processAfterAction(Action $action, $result)

10. Формирование ответа

11. Метод Controller::finalizeResponse($response)

12. Вывод $response пользователю

Несколько пространств имен

В .settings.php можно указать несколько namespaces, помимо defaultNamespace. Это необходимо, когда контроллеры расположены рядом со своими бизнес-сущностями. Например, в модуле Диск есть интеграция с облаками.

return [
     'controllers' => [
         'value' => [
             'namespaces' => [
                 '\\Bitrix\\Disk\\CloudIntegration\\Controller' => 'cloud',
             ],
             'defaultNamespace' => '\\Bitrix\\Disk\\Controller',
         ],
         'readonly' => true,
     ]
];

В результате доступны для вызова контроллеры, которые расположены в обоих пространствах имен. Пространства поддерживают вызов через полное имя действия и через сокращенную запись.

Эквивалентные вызовы:

  • disk.CloudIntegration.Controller.GoogleFile.get
  • disk.cloud.GoogleFile.get
  • disk.Controller.File.get
  • disk.File.get

Вызов контроллера из компонента

Используйте BX.ajax.runAction с подписанными параметрами:

BX.ajax.runAction('socialnetwork.api.user.stresslevel.get', {
     signedParameters: this.signedParameters,
     data: {
         c: myComponentName,
         fields: {
             //..
         }
     }
});

Внутри кода действия используйте метод Controller::getUnsignedParameters().

<?php
	//...
	public function getAction(array $fields)
	{
		//внутри распакованный, проверенный массив параметров
		$parameters = $this->getUnsignedParameters();
        
		return $parameters['level'] * 100;
	}

Отладка и ответы контроллера

Для удобной отладки ошибок включайте debug => true в .settings.php. Это позволит увидеть трейс ошибок и исключений.

Если нужно отдать файл, воспользуйтесь классами \Bitrix\Main\Engine\Response\File и \Bitrix\Main\Engine\Response\BFile.

class Controller extends Engine\Controller
{
    public function downloadAction($orderId)
    {
        // Найдите прикрепленный fileId по $orderId
        return \Bitrix\Main\Engine\Response\BFile::createByFileId($fileId);
    }
    public function downloadGeneratedTemplateAction()
    {
        // Генерация файла ... $generatedPath
        return new \Bitrix\Main\Engine\Response\File(
            $generatedPath,
            'Test.pdf',
            \Bitrix\Main\Web\MimeType::getByFileExtension('pdf')
        );
    }
    public function showImageAction($orderId)
    {
        // Найдите прикрепленный imageId по $orderId
        return \Bitrix\Main\Engine\Response\BFile::createByFileId($imageId)
            ->showInline(true);
    }
}

Для отдачи отресайзенного изображения используйте \Bitrix\Main\Engine\Response\ResizedImage.

Никогда не позволяйте пользователю запрашивать произвольные размеры для ресайза. Всегда подписывайте параметры или явно указывайте размеры в коде.

class Controller extends Engine\Controller
{
    public function showAvatarAction($userId)
    {
        // Найдите прикрепленный imageId по $userId
        return \Bitrix\Main\Engine\Response\ResizedImage::createByImageId($imageId, 100, 100);
    }
}

Постраничная навигация

Постраничная навигация позволяет управлять отображением данных, разбивая их на страницы. В Bitrix это реализуется с помощью \Bitrix\Main\UI\PageNavigation.

Использование PageNavigation

Для организации постраничной навигации в AJAX-действии необходимо использовать \Bitrix\Main\UI\PageNavigation. Это позволяет задавать лимит и смещение для выборки данных.

use \Bitrix\Main\Engine\Response;
use \Bitrix\Main\UI\PageNavigation;

public function listChildrenAction(Folder $folder, PageNavigation $pageNavigation)
{
	$children = $folder->getChildren([
		'limit' => $pageNavigation->getLimit(),
		'offset' => $pageNavigation->getOffset(),
	]);

	return new Response\DataType\Page('files', $children, function() use ($folder) {
		return $folder->countChildren();
	});
}

Передача номера страницы

Для передачи номера страницы в JavaScript API используйте параметр navigation. Это позволяет указать, какую страницу данных необходимо загрузить.

BX.ajax.runAction('vendor:someController.listChildren', {
data: {
    folderId: 12 
},
navigation: {
    page: 3
}
});

Оптимизация подсчета записей

В Response\DataType\Page($id, $items, $totalCount) параметр $totalCount может быть числом или \Closure. Это позволяет отложить вычисление общего количества записей, что повышает производительность.

Внедрение зависимостей

Внедрение зависимостей позволяет автоматически передавать объекты и параметры в методы действий контроллеров.

Автоматическое извлечение параметров

Скалярные параметры, такие как $userId, $newName, $groups, автоматически извлекаются из REQUEST. Если параметр не найден и не имеет значения по умолчанию, действие не будет запущено, и сервер отправит сообщение об ошибке.

public function renameUserAction($userId, $newName = 'guest', array $groups = array(2))
{
	$user = User::getById($userId);
	$user->rename($newName);

	return $user;
}

Внедрение стандартных объектов

По умолчанию можно внедрять объекты, такие как \Bitrix\Main\Engine\CurrentUser, \Bitrix\Main\UI\PageNavigation, и \CRestServer. Имя параметра может быть произвольным, связывание происходит по классу.

Внедрение пользовательских типов

Для внедрения своих типов объектов используйте метод getPrimaryAutoWiredParameter, который возвращает объект ExactParameter. Это позволяет автоматически создавать объекты на основе переданных данных.

class Folder extends Controller
{
	public function getPrimaryAutoWiredParameter()
	{
	    return new ExactParameter(
	        Folder::class,
	        'folder',
	        function($className, $id) {
	            return Folder::loadById($id);
	        }
	    );
	}

	public function renameAction(Folder $folder);

	public function downloadAction(Folder $folder);

	public function deleteAction(Folder $folder);
}

Вызов в JavaScript

При вызове действий с внедрением зависимостей в JavaScript необходимо передавать идентификаторы объектов, которые будут использоваться для создания экземпляров.

BX.ajax.runAction('folder.rename', {
data: {
    id: 1 
}
});

Множественное внедрение

Если требуется описать несколько параметров для создания, используйте метод getAutoWiredParameters. Это позволяет гибко настраивать внедрение нескольких объектов в одно действие.

Часто встречающиеся ошибки

Не найден обязательный параметр

Решение: убедиться, что все обязательные параметры передаются в запросе. Проверьте, что параметры указаны в $_POST или $_GET, и их имена соответствуют ожидаемым.

Неверный тип параметра

Решение: проверить, что передаваемые параметры соответствуют ожидаемым типам. Используйте type-hinting для автоматической проверки типов.

Не удалось создать объект

Решение: убедиться, что функция создания объекта возвращает корректный экземпляр. Проверьте, что все необходимые данные для создания объекта передаются в запросе.

Проблемы с подписанными параметрами

Решение: убедиться, что подписанные параметры корректно передаются и проверяются. Используйте метод getUnsignedParameters() для работы с ними внутри контроллера.

💡 Экспертное мнение и рекомендации

Практические рекомендации:

  • При написании кастомных компонентов для «Kontrollery: Руководство разработчика» всегда разделяйте бизнес-логику и представление (шаблон).
  • Используйте отладку SQL-запросов через конструкцию $wpdb->last_query или стандартный трекер Битрикса, чтобы контролировать нагрузку.

Похожая задача в бизнесе?

Помогу автоматизировать процессы и внедрить ИИ

Меня зовут Андрей Горошко. Я Full-stack разработчик и эксперт по 1С-Битрикс. Настраиваю CRM Битрикс24, разрабатываю защищенные API и создаю умных ИИ-агентов для автоматизации рутины.

Related Post

Оптимизация шрифтов (WOFF2, font-display: swap, Preload) и устранение скачков CLSОптимизация шрифтов (WOFF2, font-display: swap, Preload) и устранение скачков CLS

Высокая скорость загрузки сайта и безупречная серверная безопасность — обязательные требования для успешного коммерческого проекта на 1С-Битрикс. Услуга «Оптимизация шрифтов (WOFF2, font-display: swap, Preload) и устранение скачков CLS» обеспечивает устранение

Удалить значение свойства sale.propertyvalue.deleteУдалить значение свойства sale.propertyvalue.delete

Каждый PHP-разработчик, работающий с экосистемой Bitrix Framework, должен глубоко понимать работу её ключевых компонентов. В этой статье мы проведем подробный аудит темы «Удалить значение свойства sale.propertyvalue.delete», проанализируем лучшие практики и

Настройка модуля «Задачи и Проекты» в Битрикс24: шаблоны, чек-листы, дедлайныНастройка модуля «Задачи и Проекты» в Битрикс24: шаблоны, чек-листы, дедлайны

Профессиональная настройка и внедрение Битрикс24 CRM позволяет систематизировать работу отдела продаж, исключить потерю лидов и автоматизировать ключевые бизнес-процессы компании. Услуга «Настройка модуля «Задачи и Проекты» в Битрикс24: шаблоны, чек-листы, дедлайны»