Так же, как и дома строятся из кирпичей, приложения на основе Bitrix Framework, в идеале, должны строиться из компонентов. И, казалось бы, что тут может быть сложного, но проработав более 2 лет с Битрикс, я редко когда встречал хорошую реализацию, которую было бы приятно использовать. В большинстве случаев, это «портянка» из полутра тысяч строк в одном файле, скролить который трудно, не говоря уже о том, чтобы его дорабатывать и расширять. В этой статье, я решил добавить свой вариант реализации архитектуры простого компонента, который я уже использую как минимум полгода во всех проектах.
Обратимся к официальной документации Битрикс и ознакомимся с определением компонента. К сожалению, данное определение не соответствует нашим реалиям. В большинстве случаев, это просто обертка над куском PHP кода, который «мозолит глаза» и точно не является «логически законченным функционалом». Вот его и прячут в дебрях файловой структуры Битры, заменяя более привычным вызовом компонента. Но как говориться, не будем о грустном.
Возьмем вариант, когда мы действительно пишем функционал, который будет являться «логической единицей в рамках приложения». Хорошим примером этого может быть сложная форма. У неё бывает множество состояний, например:
- инициализация (отображения формы для пользователя);
- сохранения данных (когда мы кликаем по кнопке submit);
- обработка AJAX запросов (например, динамическая подгрузка городов);
- и т. д. и т. п.
Все эти состояния, по сути - «действия», которые мало друг от друга зависят, и для их обработки не нужно использовать один метод, их удобно разделить по отдельным файлам. Так же при написании компонента, мы добавляем хелперы, которые логичнее вынести в свои отдельные классы и использовать их через какой-нибудь адаптер.
Собственно, я так и сделал. Ранее в статье «Добавление и обновление свойств в заказе с использованием Битрикс API», для полезных кусков кода я создал на github репозиторий.
У меня уже накопилось очень много полезного кода, который хотелось бы туда добавить, но руки никак не доходят.
Composer пакет falbar/bitrix
Чтобы получить «заветный» скелет компонента, потребуется установить пакет falbar/bitrix. В README.md, описано, что нужно сделать, но продублирую тут. Переходим в папку local
в своем проекте и запускаем команду:
composer require falbar/bitrix "1.*"
После чего потребуется подключить autoload
, для этого открываем файл php_interface/init.php
и добавляем строчку:
require_once($_SERVER['DOCUMENT_ROOT'] . '/local/vendor/autoload.php');
Теперь у вас появилась возможность пользоваться сниппетами из репозитория. Когда вы ставили falbar/bitrix, вы могли заметить, что установился еще пакет symfony/console. В последнее время очень часто пользуюсь им, так как это очень просто, да и symfony мне нравиться.
Создавать скелет компонента мы будем, через запрос в консоли. Перед этим ее стоит настроить. Переходим в раздел «Консольные команды» и следуем инструкциям. Создаем файл console
с содержимым:
#!/usr/bin/env php
<?php
set_time_limit(0);
ini_set('mbstring.func_overload', 2);
ini_set('memory_limit', '1024M');
ini_set('mbstring.internal_encoding', 'UTF-8');
$_SERVER['DOCUMENT_ROOT'] = realpath(__DIR__ . '/..');
define('BX_UTF', true);
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
define('BX_BUFFER_USED', true);
define('NO_AGENT_CHECK', true);
define('NO_AGENT_STATISTIC', true);
require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
use Symfony\Component\Console\Application;
try {
$oApplication = new Application();
$oApplication->add(new \Falbar\Bitrix\Console\CacheSizeCommand());
$oApplication->add(new \Falbar\Bitrix\Console\CacheClearCommand());
$oApplication->add(new \Falbar\Bitrix\Console\CreateComponentCommand());
$oApplication->run();
} catch (\Exception $oError) {
echo $oError->getMessage();
}
На этом мы закончили установку пакета и настройку консоли.
Для создания компонента нам останется запустить команду:
php console bitrix:create:component <namespace> <name>
У команды два обязательных параметра:
- namespace – пространство компонента (например custom);
- name – название компонента (например custom.component).
В результате мы увидим:
И сам компонент:
Теперь немного, о том, как это работает
Особой магии, к сожалению, нет. Файл class.php
– мы используем как точку входа в компонент и в нем храним основные свойства, методы, подключения модулей, обрабатываем входные параметры и т.д. Не стоит его бессмысленно расширять! Все действия, которые могут происходить с компонентом - выносим в отдельные классы, которые находятся в папке actions
, а хелперы в папку helpers
.
Например, нам нужно подключить какие-то модули (iblock, sale и т.д), для это их помещаем в свойство $arModules
:
На уровни компонента они будут доступны как в actions
, так и в helpers
. Если, что-то не подключиться мы сразу это увидим в исключении.
По умолчанию при создании компонента через консоль у нас два события – это init
и ajax
. Все события добавляем в метод prepareAction()
:
Если посмотреть класс Init
, который соответствует событию init
, мы увидим такую картину:
Соответственно, в методе do()
, помещаем логику, которую реализуем. Так как мы наследуемся от родительского класса, мы не теряем контекст. Для всех других событий реализуется такая же логика. Так мы значительно разгрузим файл class.php
, и с компонентом станет работать проще и удобнее.
Для того, чтобы использовать хелперы нам потребуется воспользоваться методом getHelper()
– это адаптер, который при вызове подключает нужный нам класс из папки helpers
, а в результате возвращает объект этого класса.
Так, используя его, мы сможем легко спрятать все методы, которые раньше валялись в class.php
и получить к ним удобный доступ. Достаточно редко, но приходиться реализовать проверку прав доступа к компоненту, для этого в class.php
– есть метод haveAccess()
.
Из написанного выше для того, кто знаком с Битрикс, нет ничего нового и сложного, но, как показала практика, почти во всех проектах компоненты пишутся в одном классе (что раньше и сам делал). Разрабатывая такой компонент, если он больше 500 строк, то теряется его контекст и понимание происходящего. Возвращаться к его доработке, и вносить фиксы через долгий промежуток времени становиться крайне проблематично и неудобно.
Если у кого-то есть интересные архитектурные реализации, было бы интересно их узнать, так как это далеко не финальная версия, и скелет я буду со временем доработать.