Где могут располагаться контроллеры?
Для того, чтобы, увидеть ответ, необходимо приобрести одну из следующих подписок:
Купить подписку можно на главной странице сайта
О проекте
Мы публикуем ответы на тесты, необходимые для получения сертификатов по 1С — Битрикс. Данные сертификаты требуются для получения компетенции и партнерства.
Разделы
Ответы
- Контент — менеджер
- Администратор.Базовый
- Администратор.Модули
- Bitrix Framework
- Пользователь Сервиса Битрикс24
Битрикс, HMVC и немного бреда…
Здрасте! Наверняка многие знают, что такое CMS Битрикс, что она из себя представляет и какие «замечательные» код и архитектурные решения представляют его разработчики. В данном посте я хотел бы предложить новое видение на разработку компонентов и модулей системы.
HMVC
Прежде чем объяснять, что это за 4 буквы, напомню, что значат последние три:
Собственно MVC — это шаблон проектирования, который разделяет систему на три части:
- Модель — бизнес-логика;
- Контроллер — логика управления/взаимодействия;
- Представление — UI.
Вроде бы все хорошо, MVC прям почти панацея (это такая вещь, которая решает все проблемы), но если система достаточно большая, то она неизбежно усложняется и как следствие (или причина) возникают проблемы масштабирования. Тут на помощь приходит добрый друг: H — Hierarchical.

Основная идея данного шаблона в том, что вся система делиться на отдельные, независимые триады (MVC’шки), которые общаются между собой через контроллеры. Таким образом бизнес-логика по прежнему изолирована от внешнего мира, и по сути система самопроизвольно дробиться на мелкие части, а это куда удобнее и проще спагетти-зависимостей, которые неизбежно возникнут в большой системе.
Общение триад происходит путем запросов к контроллерам. Можно выделить следующие виды запросов:
- внешний запрос — создание запроса к другому серверу (божественное распределение);
- внутренний запрос — создание запроса к текущему серверу;
- вызов функции — просто вызов действия контроллера из кода, без дополнительного запроса к серверу.
Битрикс и его тараканы
Битрикс — это крайне разрекламированная и крайне недружелюбная для разработчика система управления. Но на самом деле речь не об этом, поэтому перейдем к делу.
По заявлению авторов, данная система реализует шаблон MVC. Выглядит это следующим образом:

Эмм… серьезно? Модель находиться в другой структурной части системы, отдельно от контроллера и представления. Отдельно, Карл.
На самом деле это довольно удачный ход, т.к. вся система основана на компонентах и каждый из них может дергать любую модель, любого модуля, а если быть точным конкретную модель — инфоблоки. Но как по мне с точки зрения MVC это неверно, контроллеры перегружаются логикой (по крайней мере если смотреть на реализацию стандартных компонентов).
Битрикс. Модели
Инфоблоки — это очень удобное и гибкое решение для хранения информации, тут не поспоришь и игнорировать данный инструмент — глупо. Для наглядности, структуру системы в общем случае можно изобразить так:

Глядя на эту картинку можно сделать вывод, что решение отделить модель чертовски замечательное. Но в данном случае много логики в контроллерах (компонентах), а модели (API) это всего лишь domain model, и никакой логики не содержат.
Битрикс. Контроллеры
Битрикс заверяет нас, что компоненты выполняют роль контроллеров, но я не соглашусь с этим.
Компоненты — это виджеты: они получают на вход данные и каким-либо образом их преобразуют.
Комплексные компоненты — это контроллеры (точнее фронт контроллеры): они принимают на вход запрос пользователя, парсят его и инициируют запрошенное действие. Схематически это можно выразить так:

Битрикс. HMVC
С представлением битрикс об MVC мы познакомились, теперь рассмотрим варианты общения контроллеров (компонентов) между собой.
Как советует нам документация: общаться компонентам можно либо через глобальные переменные, либо через события. На самом деле это советы по кооперации модулей, но строго говоря на компоненты они тоже применимы, вот только не совсем подходят:
- глобальные переменные — неявная передача данных;
- события — вообще мимо кассы.
IncludeComponent('comp1','',[& $params]); $APPLICATION->IncludeComponent('comp2','',[$params]); // либо если явно указывать что есть что $APPLICATION->IncludeComponent('comp1','',['request' => $req, 'response' => & $res]); $APPLICATION->IncludeComponent('comp2','',[$res]); ?>
Все прозрачно и понятно куда, что и когда поступает.
Вроде бы все, но нет. Каждый отдельный компонент (не комплексный) должен представлять собой отдельную триаду MVC, а этого строго говоря нет. На помощь приходит service layer:

Благодаря такой структуре каждый компонент (не комплексный) имеет свою модель (service layer), с необходимой бизнес-логикой, которая в свою очередь обращается к API (domain model) для работы с базой.
Немного бреда
Конечно можно было бы закончить статью пунктом выше, но эмоции рвутся наружу поэтому я не могу молчать. Меня очень забавляет тот факт, что все битриксоиды, не зависимо от их уровня профессионализма кричат в один голос «используйте все из коробки, если этого в коробке нет, не используйте битрикс вообще!». Спрашивается «Зачем тогда маркетплейс?», ну да ладно.
Ребят, серьезно? Вы разработчики или кто? Я понимаю что битрикс это некий конструктор (угадайте чье это мнение) в котором МНОГО что есть, но секундочку: много — это НЕ ВСЕ. И мне несказанно грустно когда битриксоиды говорят используйте стандартные модули и на их основе делайте что-то.
Я не призываю переписывать все модули и лепить кучу велосипедов (хотя маркетплейс ими изобилует). Для прокачки скилла конечно стоит написать пару своих велосипедов, но из стандартных модулей можно взять только API и не более, потому как в жизни я не видел хуже говнокода, но это Битрикс, с этим ничего не поделать.
Что необходимо для работы контроллера в Bitrix?

Есть несколько способов чтобы «Битрикс» (а на самом деле php) видел ваш класс:
1. Ванильный php: класс должен быть подключен к выполнению явно через подключение файла в которым он описан
2. Вандальный/Коробочный Б24: Класс должен располагаться где-угодно, но быть заргистрирован через автолоадер (здесь не важно что вы используете: composer, битриксовый или свое на базе spl_autoloader) в init.php
3. Через модули.
Если мы говорим о классическом bitrix framework то правильнее делать модуль.
Причем в модуле есть 2 варианта его инициализации:
1. Через «автолоадер» или прямое подключение в include.php ()
2. Через `psr`-подобный загрузчик в папке lib (https://dev.1c-bitrix.ru/learning/course/index.php. )
Иначе говоря:
— Либо вы размещаете где хотите и подключаете его сами
— Либо если вы работаете с модулем то положить его по psr4 в папочку `lib` вашего модуля.
Как работает автозагрузчик?
Если вы читали абзац выше, то поняли, что единого центразиованного загрузчика тут нет.
Есть:
1. Глобальный автозагрузчик из модулей (который загружает по psr4 из активных подключенных модулей)
2. Локальный автозагрузчик модуля (для подключенного модуля)
3. Кастомные загрузчики (тут кто во что горазд).
Соответственно, если мы говорим про 1 и 2 типы загрузчиков, что сначала необходимо подключить модуль (`\CModule::IncludeModule` или `\Bitrix\Main\Loader`), а потом уже вызывать классы.
Если вы попытаетесь вызывать классы, а до вас никто не подключил модуль, то вы получите фатал.
Везде ли он работает?
Последний абзац выше показывает что нет.
Есть ли проблемы с версиями? Отличия ядер битрикса или ещё какой черной магии?
Описания в зависимости от версии особо нет.
Что касается проблем, то примерно до версии 18 psr-подобного загрузчика не было, потом он начал появляться и совершенствоваться.
Если ориентироваться на последнюю версию Битрикс24, то проблемы если и есть, то только локального характера связанные с модульной структурой.
Здесь могу посоветовать только проштудировать документацию и потыкаться самому. Универсальной формулы нет — документация тут скудная.
Как настроить defaultNamespace и namespaces в .settings.php, чтобы всё заработало?
Здесь нужно намного больше информации, например: как модуль используете, что сделали, что ожидаете и что работает не так как ожидали.
Пока могу лишь ограничиться ответом «настроить согласно документации» (ха-ха, злая шутка, особо документации нет — нужно больше вводных данных см выше)
Есть ли где-то полностью рабочий пример реализации своего класса, с описанием того где файлы и почему они там?
Нет, такого нет.
В Битриксе это можно получить только после изучения всех курсов/доков/экзаменов и с точки зрения практического опыта.
Можно посмотреть уже готовые модули (что-то подобное можно найти на github, например https://github.com/alex-nzr/bitrix-example-boilerp. или https://github.com/zahalski/awz.ydelivery/tree/mas. )/
Можно посмотреть как это делать без модулей https://bx24devbook.website.yandexcloud.net/index.html (тут скорее больше описания чем конкретные классы).
Готового гайда как у симфони здесь увы нет.
Какая именно должна быть ссылка для проверки роута?
Что вы имеете ввиду? Проверки чем? Ссылка на что? Какого роута?
Ответ написан 16 янв.
Нравится 2 1 комментарий

Дмитрий Баскаков @dmtrbskkv Автор вопроса
Касательно роута и .settings.php:
Текущая моя задача — это сделать ссылку redirect_url для кнопки авторизации. На данном redirect_url(роуте) будет функционал создания пользователя, поэтому нужно не просто случайный файл внутри модуля создать, а полноценный роут
Нашел пока только способ с ajax.php к которому через action подхватывается нужный контроллер, указанный в .settings.php
Резюмируя. Нужно настроить .settings.php так, чтобы я могу создать свою ссылку и обработать данные приходящие на нее через свой контроллер
Ответы на вопрос 1
Писал уже Вам в другом вопросе, Вы зашли в новый фреймворк и вместо того, чтобы изучить последовательно доку, посмотреть курсы, ядро, идете сюда и задаёте вопросы, ответы на которые уже есть в вышеперечисленных источниках.
Почему битрикс должен видеть мой класс и где он должен находиться?
Потому, что автодолоадер классов.
Как работает автозагрузчик? Везде ли он работает?
Отает в доке и ядре.
Есть ли проблемы с версиями? Отличия ядер битрикса или ещё какой черной магии?
Вы серьезно думаете, что тут вам будут рассказывать про отличия ядер? А самому посмотреть список обновлений ядра на офф сайте?
Как настроить defaultNamespace и namespaces в .settings.php, чтобы всё заработало?
В первой ссылке ответ на 2й странице «Контроллер», или дальше 1й не читали?
Есть ли где-то полностью рабочий пример реализации своего класса, с описанием того где файлы и почему они там?
Есть. В примерах доки и практически любом битриксовом модуле.
Какая именно должна быть ссылка для проверки роута?
В первой ссылке ответ на 2й странице «Контроллер»(а да, вы же дальше 1й стараницы не дошли), там даже метод генерации ссылки описан.
Может вместо «Кодим и декодим, а иногда кино смотрим» посмотреть доку с самого начала, и ролики с того же ютуба, есть и тех потоки битрикса, на которых они сами объясняют и куча других материалов, от той же интерволги.
Ответ написан 16 янв.
Нравится 1 4 комментария

Дмитрий Баскаков @dmtrbskkv Автор вопроса
Ещё можно потратить полжизни на изучение документации по различным версиям битрикса и не менее различным гайдам. Актуальность которых каждый раз под вопросом
Почему-то с ларой или симфонией я находил нужную информацию быстро и без каких-либо особых проблем. Может быть доки плохо организованы, а комьюните так мало, что единственный способ понять что да как в нем — это метод «тыка» и постоянных вопросов?
Но даже метод «тыка» тут не работает как надо, ибо большинство модулей написано по-своему. Выискивать в них общие концепции дело не очень благодарное при живом, хоть и малочисленном, комьюнити
Касательно ваших ответов:
Потому, что автодолоадер классов.
А я думал черная магия. Вроде я в вопросе спросил как именно он работает. И почему-то я уверен что очень просто, но информацию об этом «просто» сложно найти. Но спасибо за очень информативный ответ
Отает в доке и ядре.
Правильно, а форумы и вопросы вообще лишнее. Ведь у нас все есть на ужасно неудобном форуме и в ужасно организованной доке
Вы серьезно думаете, что тут вам будут рассказывать про отличия ядер? А самому посмотреть список обновлений ядра на офф сайте?
Понимаю, сам еле понимал доку, но где-то внутри документации написано про глобальное отличие какого-то «нового» и «старого» ядра. Видимо вам стоит пойти по своему же совету, раз вы так любите документацию и прочитать что там за ядра есть
В первой ссылке ответ на 2й странице «Контроллер», или дальше 1й не читали?
Иногда прежде чем советовать, стоит перепроверить. Один из моих вопросов касается расположения файлов. И как-то я не вижу в разделе «Контроллеры/Контроллер» имена файлов. На всякий случай уточню: .settings.php — я знаю куда класть. Мне бы чуть более важные файлы, типа контроллеров. Если я не ошибаюсь, то их надо в lib и далее куда угодно. Однако вопросы как раз в том какие ограничения на это «куда угодно» и почему оно должно заработать. В конце концов, я попросил пример рабочего решения, где это можно было бы увидеть. Не обязательно мне тут все расписывать
Есть. В примерах доки и практически любом битриксовом модуле.
Люблю людей, которые не перепроверяют информацию и просто так её вкидывают. Чисто ян топлес. А если серьезно, то именно по этим гайдам я пробовал всё сделать, и не заработало, ибо не хватает информации как и что сделать. Напоминаю: есть примеры хороших доков по типу лары и симфонии. В них можно понять что да как, в битриксе нет. Поэтому даже вы ссылаетесь на форум битрикса, ибо дока не всегда помогает
Резюмируя. Прежде чем советовать, можно подумать а есть ли смысл советовать. Может ваш ответ будет бессмысленным вбросом с нулевой полезной нагрузкой? Даже если вы обладаете знаниями в битриксе, в чем я немного сомневаюсь, то не надо думать, что и другие знают всё то же. Сколько лично вы потратили на обучение битриксу? Делали ли вы это самостоятельно? Мне кажется, что вы не сразу поняли что да как и не штурмовали документацию несколько дней подряд. А если штурмовали, то сочувствую, но уважаю за проявленную смелость, упорство и удачу
Дмитрий Баскаков, мой ответ направлен на то, что нужно хоть немного прилагать усилия и изучать ту область, в которую ты входишь и хочешь что-то в ней сделать. В противном случае, вы еще больше пополняете проекты костылями и велосипедами.
Если не получается делать по примерам доки или видите, что дока не актуальна, поищите еще ответ в интернете, ну серьезно, по битриксу вагон всего. Если и в этом случае не получилось, идите в исходники кода и смотрите как устроено, если не получается, значит вам не хватает знаний в php и дело уже не в битриксе. Очень странно, вы указываете, что не было проблем с ларой и симфони, но не можете разобраться с автолоадером классов битрикса, который условно реализован в одном классе..
Ещё можно потратить полжизни на изучение документации по различным версиям битрикса и не менее различным гайдам
Не хотите тратить свое время — платите деньги за время специалистов.
Не нравится продукт, его дока и реализация, но бизнес требует что-то на этом продукте — платите деньги специалистам, они вам сделают. Все просто.

Дмитрий Баскаков @dmtrbskkv Автор вопроса
Эдуард, Перед тем как задать вопрос на форуме, я гуглю, читаю документацию и пробую различные варианты. В остальном плюс-минус согласен. Только добавлю что битрикс является для меня одним из инструментов и не представляет высокой ценности сам по себе. Поэтому и написал что нет желания тратить на него сильно много времени. Хотя потратил уже неприлично много времени
А про класссы самого битрикса: я не могу разобрать их. Так получилось, что единственный проект, который мне доступен для тестов находится на бою. И мне даже банальные var_dumpы страшно создавать внутри модуля. Что уж говорить о изучении внутренних классов битрикса
user_41144 @user_41144
Дмитрий Баскаков, Боюсь что Эдуард прав что если надо чтобы помогли — платите денюшки. Да, у битры плохая дока, да, они сами пишут в доке что «если вы опытный вы можете залезть в наше ядро и все сами посмотреть». Битриксойды тратят на это время и потом у них не остается желания и сил расписывать гайды как это сделать. Это все не очень хорошо, я сам постоянно мучаюсь с этим но это реальность — либо сами разбирайтесь либо платите опытным программистам. Ну и самое важное что я понял за годы работы — нельзя просто взять что то на форуме найти и бац — все работает (иногда можно). Придется потратить часы, дни чтобы с самого начала, с самой базы все изучить и да, уйдет много времени. Это конечно не пол жизни на документацию но это неизбежно. Представь что тебе щас объяснили что делать, скинули код. Ты на следующей же задаче откиснешь, например, когда надо будет в смарт-процессах добавить элемент. Ну и да, все знают что прежде чем вопрошать надо показать где у тебя не получается а не просто «скиньте мне как должно быть»
Где могут располагаться контроллеры битрикс
Контроллеры — это часть MVC архитектуры, которая отвечает за обработку запроса и генерирование ответа.
Действия
Контроллеры состоят из действий, которые являются основной сутью и их в конечном итоге запрашивает пользователь для получения результата. В одном контроллере может быть одно или несколько действий. Для примера сделаем контроллер с двумя действиями item.add и item.view в модуле example.
Первый шаг — создать в корне модуля файл .settings.php .
[ 'value' => [ 'defaultNamespace' => '\\Vendor\\Example\\Controller', ], 'readonly' => true, ] ];
Далее создаётся сам файл контроллера (смотри подробное описание доступных методов):
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(); > >
В действии add (определенным методом Item::addAction ) сначала идёт попытка создания некого Item по переданным $fields .
Примечание. Массив $fields получается путем автоматического связывания параметров метода и $_REQUEST . Подробнее о принципах в уроке Внедрение зависимостей.
Если не удалось выполнить создание по каким-то причинам, то возвращаем null и наполняем ошибками сам контроллер. В этом случае ядро сгенерирует ответ:
Иначе добавляем Item и возвращаем из действия его представление в виде массива $item->toArray() . Таким образом ядро сгенерирует ответ:
< "status": "success", "data": < "ID": 1, "NAME": "Nobody", //. поля элемента >, "errors": null >
В целом, действие может вернуть не просто скаляры, но и объекты.
В действии view (определенным методом Item::viewAction ) сначала идёт попытка загрузки некого объекта Item по переданному параметру $id . Важно заметить, что $id будет автоматически получен из $_POST[‘id’] или $_GET[‘id’] .
Если данный параметр не найден, то ядро сгенерирует ответ с ошибкой:
< "status": "error", "data": null, "errors": [ < "message": "Could not find value for parameter ", "code": 0 > ] >
Как обратиться к действию контроллера?
Для вызова конкретного аякс-действия нужно знать и пользоваться соглашением по именованию. В нашем случае: Item::addAction -> vendor:example.Item.add Item::viewAction -> vendor:example.Item.view .
vendor:example.Item.add, 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) < //. >>
Возвращаемое значение действия представляет собой данные ответа, которые будут высланы клиенту.
Если действие возвращает \Bitrix\Main\HttpResponse или его наследников, то данный объект и будет отправлен клиенту. Если действие возвращает некие данные, то они должны приводиться к скаляру или объекту, который после будет превращен в JSON и на основе него будет сформирован \Bitrix\Main\Engine\Response\AjaxJson .
В целом действие может вернуть не просто скаляры, но и объекты, которые реализуют следующие интерфейсы:
- \JsonSerializable
- \Bitrix\Main\Type\Contract\Arrayable
- \Bitrix\Main\Type\Contract\Jsonable
Либо конкретные наследники \Bitrix\Main\HttpResponse:
Создание классов-действий
Есть возможность создавать классы-действия, которые унаследованы от \Bitrix\Main\Engine\Action . Подобная возможность может потребоваться, когда необходимо повторно использовать логику в нескольких контроллерах. Например, если реализуется одинаковый протокол обмена в разных модулях (стандартный поиск, выполнение пошаговых действий с прогрессом и тому подобное.). Для использования нужно описать в карте конфигурации контроллера метод configureActions:
class Test extends \Bitrix\Main\Engine\Controller < public function configureActions() < return [ 'testoPresto' =>[ 'class' => \TestAction::class, 'configure' => [ 'who' => 'Me', ], ], ]; > >
И вот сам TestAction:
who = $params['who']?: 'nobody'; > //основной метод работы. Параметры так же автоматически связываются, как и в методе //аякс-действии public function run($objectId = null) < return "Test action is here! Do you know object with id ? Mr. who>"; > >
Для вызова этого действия нужно обращаться к нему testoPresto, как описано в карте конфигурации. Класс-действие поддерживает пре- и пост-фильтры и по сути ничем не отличается от обычного метода-действия. Смысл метода run() аналогичен другим методам аякс-действиям.
Использование контроллеров внутри компонентов
Предпочтительно создавать и использовать классы контроллеров, которые располагаются в модулях, как указано в данной статье, так как позволяет лучше организовать повторное использование вспомогательного кода и бизнес-логики.
В простых случаях, если компонент самодостаточен и не используется активно с API-модуля, то можно использовать контроллеры внутри компонента.
Жизненный цикл контроллера
При обработке запроса Application создаёт контроллер на основе соглашения по именованию. Далее работу выполняет контроллер:
- Controller::init() будет вызван после того, как контроллер создан и сконфигурирован.
- Контроллер создает объект действия
- Если действие не удалось создать, выкидывается исключение.
- Параметры действия будут сопоставлены с данными из запроса
Несколько namespaces
Указание нескольких namespaces в модуле.
В .settings.php можно указать несколько namespaces, помимо defaultNamespace. Это может быть необходимо, когда контроллеры расположены рядом со своими бизнес-сущностями. Например, в некотором модуле «Диск» у нас есть интеграция с облаками.
[ 'value' => [ 'namespaces' => [ '\\Bitrix\\Disk\\CloudIntegration\\Controller' => 'cloud', //cloud - это альяс ], 'defaultNamespace' => '\\Bitrix\\Disk\\Controller', ], 'readonly' => true, ] ];
Теперь у нас доступны для вызова контроллеры, которые расположены в обоих namespaces. Оба из них поддерживают вызов через полное имя действия и через сокращенную запись, так как у нас есть альяс cloud.
disk.CloudIntegration.Controller.GoogleFile.get disk.cloud.GoogleFile.get disk.Controller.File.get disk.File.get
Вызов модульного контроллера с подписанными параметрами компонента
В случае, если вам нужно обратиться к контроллеру, реализованному в модуле, из контекста компонента, прокинув подписанные параметры, используйте следующий способ:
BX.ajax.runAction('socialnetwork.api.user.stresslevel.get', < signedParameters: this.signedParameters, // результат $this->getComponent()->getSignedParameters() data: < c: myComponentName, // например, 'bitrix:intranet.user.profile', параметры которого нам будут нужны fields: < //.. >> >);После этого внутри кода действия используйте:
getUnsignedParameters(); return $parameters['level'] * 100; >
Документация
