Здрасте!
Очередная суицидальная статья от меня на тему Битрикс, надеюсь в этот раз хабраобщество будет более снисходительно, т.к. здесь все по факту, с кодом, схемками, никакого холивара и все по-честному.
В статье я рассмотрю альтернативу BitrixFramework, которая призвана облегчить жизнь разработчика и как-нибудь повлиять на развитие CMS Битрикс в нужном направлении.
Акция для хейтеров: если напишите комментарий с нормальной критикой и по теме + к карме лично отправлю ;-). Вот вам Вольфыча для затравки, все интересное внутри…
Все что написано ниже лично мое мнение и может быть спокойно закидано тапками, но я уверен, что я прав:
Наверняка многие знакомы с этим персонажем (из Marvel, не из Dota), которого «невозможно остановить». Слегка пафосное название, на самом деле отражает суть данного проекта: абсолютно безразлично как развивается Битрикс, какие новшества он вводит и что он делает, все равно библиотека будет жить и процветать.
Bitrix нацелен на пользователей. Juggernaut нацелен на разработчиков.
Потому что это надо! Все на самом деле очень логично:
Битрикс нужно было с версии 14 просто закончить поддержку старого ядра и сделать основной упор на новом, но нет, «заботятся о клиентах». Бред. Это тоже самое если бы Yii2 поддерживал и обратно совмещал Yii1.
Раз Битрикс никакие подвижки не делает, то их будет делать сообщество (вместо того чтобы ныть, писать в сервис «Идея», и как-то выкручивать используя стандартные компоненты).
Поругали Bitrix, теперь можно приступить и к обзору Juggernaut. Далее начнется обзор составляющих частей библиотеки и краткое описание их использования.
Компоненты – это кирпичи из которых строиться сайт на Битрикс. Компоненты условно разделены на 2 категории: виджеты и роутеры (в нотации Битрикс: «обычный» и «комплексный»).
Виджет – это компонент, который тупо делает одну элементарную задачу (выводит форму, список, информацию). Виджет получает на вход данные и каким-либо образом их преобразует. Больше делать он ничего не должен. Виджеты не управляют маршрутизацией, но могут ее использовать.
Порядок выполнения компонента по умолчанию:
По порядку:
Чаще всего достаточно переопределить метод initResult и накидать шаблон компонента.
Ниже представлен пример класса компонента (class.php), который выводит список элементов инфоблока. На вход он получает массив параметров ($params), которые используются для фильтра и сортировки данных.
Задача роутера – это сбор виджетов воедино. Роутер — представляет из себя контроллер, который на основе запроса пользователя (REQUEST_URI), вызывает соответствующее действие. Действие может быть либо страницей с информацией (в том числе виджетами), либо содержать какую-либо логику.
Порядок выполнения компонента по умолчанию:
По порядку:
Ниже представлен пример компонента, которые реализует каталог:
По данному вопросу много говорить не буду, потому что и так ясно что это очень нужная вещь, просто опишу все работает.
Как реализовано в Juggernaut:
В папке «lib» вы должны соблюдать следующую структуру: имена файлов классов, идентичны именам пространства имен, не включая расширение и верхнего пространства имен. Например, классу «Iblock\Property\Table» будет соответствовать файл «…/modules/Iblock/lib/Property/Table.php».
Вызывать «includeModule» больше не нужно, т.к. при необходимости все классы подгрузятся автоматически из нужных директорий.
Если директория модуля отличается от названия пространства имен, или в любой другой ситуации, можно вручную задать соответствие пространства имен и директории:
У Битрикс тоже реализована автозагрузка, но формирует она путь несколько иначе:
Класс «Olof\Catalog\Tools\File» транслируется как «/Olof.Catalog/lib/Tools/File.php».
Если Вам нужен класс «Olof\Catalog» — то извините, руками указывайте его наличие (см.ниже). Директория модуля у Вас должна быть именно с разделителем «.» иначе гуляйте лесом. При чем директория «olof.catalog.iblock» — является некорректной.
Господа из Битрикс на самом деле сделали нормальную штуку: позаботились об указании вендора в имени модуля, но я считаю это лишнее условие именования директории.
Автозагрузка неявно реагирует на классы вида «ElementTable» удаляя постфикс, транслируя их в файлы «element.php». Собственно, из-за этого, вы не можете создать класс с именем Table.
Также загрузить классы из модулей, которые в данный момент не подключены (includeModule) – нельзя.
Рассмотрим пример работы Битриксового варианта: имеем модуль «olof.iblock» и соответствующий файл include.php:
Слишком много неявностей и условий на мой взгляд. Да и никто не знает, какую глупость Битрикс завтра придумают. А придумать им стоит указание директории для префикса пространства имен (как в PSR-4) и тогда будет круто. А пока есть Juggernaut ;-)
Для удобства работы с сущностями, а в частности с инфоблоками, реализован шаблон ActiveRecord. На данный момент AR базируется (по факту является надстройкой) на битриксовых DataMapper’ах, в дальнейшем планируется полный перенос на независимый ORM / DAO.
Ниже представлен пример работы с инфоблоками через AR, охвачены практически все, имеющиеся на данный момент, методы.
Методы: getPrimary, getRow, getRowByField, getList, getListByField — идентичны для всех ActiveRecord.
Функционал AR на данный момент достаточно беден (например, нет перекрестного поиска по таблицам), но т. к. они являются оберткой над стандартными функциями, в методах «getList» и «getRow» можно использовать Битриксовые плюшки. После создания / заимствования нормального DAO, этот момент будет допилен.
Сильной стороной Битрикс, и я думаю многие согласятся, является его пользовательский интерфейс a.k.a. «Эрмитаж». Он очень удобен и гибок.
Ниже представлен пример работы с Эрмитажем:
Так похвалил и так мало написал)) На самом деле этого достаточно для взаимодействия с пользователем. Очень много нужно реализовать касаемо административного интерфейса, но это уже не Эрмитаж, и это все в планах.
В Битрикс на сколько я знаю (а в данном вопросе, скрывать не буду, я особо не ковырялся), с безопасностью сайта (именно в коде) вообще грустно (только защита от SI). В будущем данный раздел будет содержать в себе инструменты для защиты от различных атак и вредоносных действий (XSS, генерация случайных данных, различные крипто-функции, валидация форм, работа с паролями, …). На данный момент реализован только инструментарий для защиты от CSRF:
После каждой проверки (удачно или неудачной) – токен из сессии удаляется, таким образом проверить токен можно только один раз.
Маршрутизация в Битрикс, не сказал бы что на высоте, поэтому и эта область затронута в Juggernaut. Данный класс позволяет динамически создавать и использовать URL маршруты (используется в компонентах-роутерах).
Рассмотрим пример парсинга и генерирования URL:
В дальнейшем планируется также подвязаться и к urlRewrite.php.
Данный класс является просто оберткой над функциями D7, с более удобным использованием.
Планы на ближайшее будущее:
Много чего задумано, много чего не сделано. Библиотека развивается по мере моей необходимости, поэтому очень зависит от текущих заказов (которое очень часто однотипны) и свободного времени.
Как я уже сказал вначале, проект будет развиваться несмотря ни на что, от количества ее авторов и заинтересованных лиц зависит лишь скорость развития. Так что выбор только за вами:
Помочь может каждый желающий филантроп (а иначе никак), для этого нужно:
Проект лежит на GitHub, так что править, добавлять, комментировать и спрашивать может любой желающий.
Спасибо за внимание! Конструктивная критика очень даже приветствуется :-)
P.S. комментарии типа «да на фиг Битрикс» огромная просьба не писать. Я в курсе какое у людей отношение к этой системе, и данный проект как раз направлен на ее облагораживание. Поэтому если вы считаете что «лучше и проще сделать проект на любом фреймворке» — то я это знаю и очень рад за вас, поэтому оставьте свое мнение при себе. Спасибо!
Репозиторий: github.com/irpsv/juggernaut.bitrix_release
Маркетплейс: скоро будет
Очередная суицидальная статья от меня на тему Битрикс, надеюсь в этот раз хабраобщество будет более снисходительно, т.к. здесь все по факту, с кодом, схемками, никакого холивара и все по-честному.
В статье я рассмотрю альтернативу BitrixFramework, которая призвана облегчить жизнь разработчика и как-нибудь повлиять на развитие CMS Битрикс в нужном направлении.
Акция для хейтеров: если напишите комментарий с нормальной критикой и по теме + к карме лично отправлю ;-). Вот вам Вольфыча для затравки, все интересное внутри…
Для начала расставим все точки над ‘i’
Все что написано ниже лично мое мнение и может быть спокойно закидано тапками, но я уверен, что я прав:
- «У Битрикс миллион строк говнокода» — да, бесспорно. Вся проблема заключается в том, что Битрикс поддерживает обратную совместимость (якобы обновив версию 12.0 до 16.5 все будет нормально работать). Зачем они это делают я не знаю (мне кажется никто не знает). Если же говорить про исходники стандартных компонентов (2К строк кода для вывода элементов инфоблока – в порядке вещей), то здесь ребята решили облегчить работу конечным юзерам и предусмотрели все что можно было предусмотреть (и то не факт), при чем все полностью это очень редко когда нужно. Ну и в догонку, на недавнем семинаре разработчик Битрикс сообщил свое отношение к PSR: «Ну что этот PSR, читал я его, собрались какие-то ребята и написали какую-то фигню» (не точная цитата). Так что код будет пахнуть всегда.
- «У Битрикс ужасная структура» — не совсем. Битрикс основан на файлах, и понимание MVC отличается от общепринятого, а для многих «не MVC» = «ужас-ужас какая структура». Так что это весьма спорный и спорный вопрос. И с Битриксовой структурой можно жить.
- «BitrixFramework никогда не будет развиваться» — это уже мое мнение и вот почему: с каждым релизом Битрикс дорабатывает только модуль «Магазин», делают какие-то правки, но все они направлены на магазин. На остальное им откровенно наплевать. Развитие BF начнется, когда они откажутся от обратной совместимости и начнут заниматься не только модулем «Магазин».
Знакомьтесь, Juggernaut!
Наверняка многие знакомы с этим персонажем (из Marvel, не из Dota), которого «невозможно остановить». Слегка пафосное название, на самом деле отражает суть данного проекта: абсолютно безразлично как развивается Битрикс, какие новшества он вводит и что он делает, все равно библиотека будет жить и процветать.
Bitrix нацелен на пользователей. Juggernaut нацелен на разработчиков.
Зачем это надо?
Потому что это надо! Все на самом деле очень логично:
- Битрикс разрабатывают новое ядро (good), но документировать вообще не хотят (bad);
- Битрикс разрабатывают новый функционал (good), но только для магазина (bad);
- Битрикс разрабатывают новые компоненты (good), но от их кода кровь из глаз (bad);
- Битрикс запатентовали «новую» технологию «Композитный сайт» (bad).
Битрикс нужно было с версии 14 просто закончить поддержку старого ядра и сделать основной упор на новом, но нет, «заботятся о клиентах». Бред. Это тоже самое если бы Yii2 поддерживал и обратно совмещал Yii1.
Раз Битрикс никакие подвижки не делает, то их будет делать сообщество (вместо того чтобы ныть, писать в сервис «Идея», и как-то выкручивать используя стандартные компоненты).
Поругали Bitrix, теперь можно приступить и к обзору Juggernaut. Далее начнется обзор составляющих частей библиотеки и краткое описание их использования.
Компоненты
Компоненты – это кирпичи из которых строиться сайт на Битрикс. Компоненты условно разделены на 2 категории: виджеты и роутеры (в нотации Битрикс: «обычный» и «комплексный»).
Виджет
Виджет – это компонент, который тупо делает одну элементарную задачу (выводит форму, список, информацию). Виджет получает на вход данные и каким-либо образом их преобразует. Больше делать он ничего не должен. Виджеты не управляют маршрутизацией, но могут ее использовать.
Порядок выполнения компонента по умолчанию:
По порядку:
- init — инициализирует начальные данные. Преобразует входные параметры ($arParams) в свойства класса;
- onBefore — проводит проверку возможности проведения действия;
- isCachedTemplate — флаг, определяющий есть ли кешированная копия. Если есть — выводит данные кеша, если нет — формирует их (в коде это выглядит несколько иначе, на схеме указано так для простоты);
- initResult — формирует данные для представления ($arResult);
- run — функция непосредственного исполнения виджета. В ней определяется что необходимо сделать с данными ($arResult);
- onBeforeRender — проводит проверку возможности вывода шаблона и выполняет какие либо преобразования (аналог result_modifier.php, хотя можно и им пользоваться);
- render — непосредственный вывод шаблона компонента;
- onAfter — выполнение действия после отработки виджета (аналог component_epilog.php).
Чаще всего достаточно переопределить метод initResult и накидать шаблон компонента.
Ниже представлен пример класса компонента (class.php), который выводит список элементов инфоблока. На вход он получает массив параметров ($params), которые используются для фильтра и сортировки данных.
Код
<?php
namespace Widget\Iblock\Element\List_;
use Jugger\Db\Orm\Ib\IblockElement;
use Jugger\Component\WidgetComponent;
class Component extends WidgetComponent
{
/*
* при выполнения метода 'init',
* все переменные из $arParams присваиваются существующим свойствам класса компонента,
* в данном случае: $this->params = $arParams['params']
*/
public $params = [];
/*
* по умолчанию, кеширование компонентов отключено
* в данном методе, мы его включаем
*/
protected function init() {
parent::init();
$this->isCachingTemplate = true;
}
/*
* инициализируются элементы для отображения
*/
protected function initResult() {
$this->arResult['elements'] = IblockElement::getList($this->params);
}
}
Роутер
Задача роутера – это сбор виджетов воедино. Роутер — представляет из себя контроллер, который на основе запроса пользователя (REQUEST_URI), вызывает соответствующее действие. Действие может быть либо страницей с информацией (в том числе виджетами), либо содержать какую-либо логику.
Порядок выполнения компонента по умолчанию:
По порядку:
- init — инициализирует начальные данные. Преобразует входные параметры ($arParams) в свойства класса;
- initUrlManager — заполняет UrlManager данные маршрутов (aliases). Это действие необходимо для выполнения маршрутизации по действием и дальнейшей генерацией URL адресов;
- parseRequest — производится разбор запроса UrlManager и определяется какое действие запрошено пользователем;
- existBeforeAction — проверка наличия персонального обработчика onBefore. Если есть действие 'index' и есть метод 'onBeforeIndex', то будет вызван именно он, иначе будет вызван общий 'onBefore';
- onBefore — проводит проверку возможности проведения действия;
- run — функция непосредственного исполнения компонентв. В ней определяется что необходимо сделать с данными ($arResult);
- existMethodAction — проверка на наличие обработчика действия. Если запрошено действие 'index' и есть метод 'actionIndex', то будет вызван этот метод, иначе роутер попытается вывести представление с именем 'index';
- onBeforeRender — проводит проверку возможности вывода шаблона и выполняет какие либо преобразования (в параметрах передается имя действия, поэтому можно настроить персональную проверку);
- render — непосредственный вывод шаблона компонента;
- onAfter — выполнение действия после отработки виджета (аналог component_epilog.php). Работает аналогично с методом 'onBefore': если для действия 'index' существует метод 'onAfterIndex', то будет вызван он, иначе общий 'onAfter'.
Ниже представлен пример компонента, которые реализует каталог:
- список элементов,
- список разделов
- детальная карточка элемента.
Код
<?php
namespace Widget\Iblock\Element\Catalog;
use Jugger\Db\Orm\Ib\IblockElement;
use Jugger\Db\Orm\Ib\IblockSection;
use Jugger\Component\RouteComponent;
class Component extends RouteComponent
{
/*
* ID инфоблока, который отображается
*/
public $iblockId;
/*
* Маршруты действий, в которые будут транслироваться адреса и по которым будер производиться маршрутизация
* по умолчанию, маршруты беруться из параметров компонента из свойства 'aliases'
*/
protected function getAliases() {
return [
"sectionList" => "index.php",
"elementList" => "#SECTION_CODE#/",
"elementView" => "#SECTION_CODE#/#ELEMENT_CODE#/"
];
}
/*
* Если инфоблок не указан, то выходим
*/
protected function onBefore($action) {
if (!$this->iblockId) {
throw new \Exception("Не указан 'iblockId' ". get_called_class());
}
return parent::onBefore($action);
}
/*
* Получаем раздел по его символьному коду
*/
protected function getSection($sectionCode) {
return IblockSection::getRow([
"filter" => [
"IBLOCK_ID" => $this->iblockId,
"CODE" => $sectionCode
],
]);
}
/**
* Список разделов инфоблока
*/
public function actionSectionList() {
$sectionList = IblockSection::getListByField(
"=IBLOCK_ID",
$this->iblockId,
[
"order" => ["SORT" => "ASC"]
]
);
$this->arResult['sectionList'] = $sectionList;
$this->render('list');
}
/**
* Список элементов указанного раздела
* Параметр $sectionCode содержит данные из URL
*/
public function actionElementList($sectionCode) {
$section = $this->getSection($sectionCode);
if (!$section) {
$this->error404();
}
//
$this->arResult['section'] = $section;
$this->arResult['elementList'] = $section->getElements();
$this->render('section');
}
/**
* Отображение карточки товара
* Параметры передаются в том же порядке, в каком они указаны в методе 'aliases'
*/
public function actionElementView($sectionCode, $elementCode) {
$section = $this->getSection($sectionCode);
if (!$section) {
$this->error404();
}
//
$element = IblockElement::getRow([
"filter" => [
"IBLOCK_ID" => $this->iblockId,
"IBLOCK_SECTION_ID" => $section->ID,
"CODE" => $elementCode,
],
]);
if (!$element) {
$this->error404();
}
$this->arResult['element'] = $element;
$this->arResult['section'] = $section;
$this->render('view');
}
}
Автозагрузка классов
По данному вопросу много говорить не буду, потому что и так ясно что это очень нужная вещь, просто опишу все работает.
Как реализовано в Juggernaut:
В папке «lib» вы должны соблюдать следующую структуру: имена файлов классов, идентичны именам пространства имен, не включая расширение и верхнего пространства имен. Например, классу «Iblock\Property\Table» будет соответствовать файл «…/modules/Iblock/lib/Property/Table.php».
Вызывать «includeModule» больше не нужно, т.к. при необходимости все классы подгрузятся автоматически из нужных директорий.
Если директория модуля отличается от названия пространства имен, или в любой другой ситуации, можно вручную задать соответствие пространства имен и директории:
// класс "Jugger\D7\Iblock" доступен по адресу "./lib/D7/Iblock.php" – по умолчанию так и работает
\Jugger\Psr\Psr4\Autoloader::addNamespace('Jugger', __DIR__.'/lib');
// класс "Jugger\D7\Iblock" доступен по адресу "./classes/Iblock.php"
\Jugger\Psr\Psr4\Autoloader::addNamespace('Jugger\D7', __DIR__.'/classes');
У Битрикс тоже реализована автозагрузка, но формирует она путь несколько иначе:
Класс «Olof\Catalog\Tools\File» транслируется как «/Olof.Catalog/lib/Tools/File.php».
Если Вам нужен класс «Olof\Catalog» — то извините, руками указывайте его наличие (см.ниже). Директория модуля у Вас должна быть именно с разделителем «.» иначе гуляйте лесом. При чем директория «olof.catalog.iblock» — является некорректной.
Господа из Битрикс на самом деле сделали нормальную штуку: позаботились об указании вендора в имени модуля, но я считаю это лишнее условие именования директории.
Автозагрузка неявно реагирует на классы вида «ElementTable» удаляя постфикс, транслируя их в файлы «element.php». Собственно, из-за этого, вы не можете создать класс с именем Table.
Также загрузить классы из модулей, которые в данный момент не подключены (includeModule) – нельзя.
Рассмотрим пример работы Битриксового варианта: имеем модуль «olof.iblock» и соответствующий файл include.php:
namespace Olof\Iblock;
use Bitrix\Main\Loader;
// подключаем модуль
Loader::includeModule("Olof.Iblock");
// переопределяем стандартное поведение для класса Api
Loader::registerAutoLoadClasses("Olof.Iblock", [
"\Olof\Classes\Api" => ". /modules/Olof.Iblock/classes/api.php",
]);
// Примеры доступа к классам:
// Olof\Iblock\Element -> ./modules/Olof.Iblock/lib/Element.php
// Olof\Classes\Api -> ./modules/Olof.Iblock/classes/api.php
// Olof\Classes\Help -> ./modules/Olof.Classes/lib/Help.php
Слишком много неявностей и условий на мой взгляд. Да и никто не знает, какую глупость Битрикс завтра придумают. А придумать им стоит указание директории для префикса пространства имен (как в PSR-4) и тогда будет круто. А пока есть Juggernaut ;-)
ActiveRecord
Для удобства работы с сущностями, а в частности с инфоблоками, реализован шаблон ActiveRecord. На данный момент AR базируется (по факту является надстройкой) на битриксовых DataMapper’ах, в дальнейшем планируется полный перенос на независимый ORM / DAO.
Ниже представлен пример работы с инфоблоками через AR, охвачены практически все, имеющиеся на данный момент, методы.
Код
use Jugger\Db\Orm\Ib\Iblock;
use Jugger\Db\Orm\Ib\IblockSection;
use Jugger\Db\Orm\Ib\IblockElement;
/*
* Получаем инфоблок
*/
$iblock = Iblock::getByPrimary(1);
$iblock = Iblock::getRowByField("=ID", 1);
$iblock = Iblock::getRow([
"filter" => [
"=ID" => 1,
],
]);
$iblock = new Iblock($iblock);
/**
* Доступ к полям таблицы, возможен как к свойствам класса
*/
$iblock->NAME;
$iblock->IBLOCK_TYPE_ID;
/**
* Дочерние элементы и разделы
*/
$iblock->getElements();
$iblock->getSections();
/*
* Получить разделы инфоблока
*/
$sectionList = $iblock->getSections();
$sectionList = $iblock->getSections([
"order" => [
"NAME" => "ASC",
],
]);
$sectionList = IblockSection::getListByField("=IBLOCK_ID", $iblock->ID);
$sectionList = IblockSection::getList([
"filter" => [
"=IBLOCK_ID" => $iblock->ID,
],
]);
/*
* Получить дочерние разделы
*/
$section = new IblockSection($sectionList->fetch());
$section->getChilds(); // получить детей 1-ого уровня вложенности
$section->getChilds(2); // получить детей до 2-ого уровня вложенности (вернется массив, в порядке вложенности потомков)
$section->getChilds(0); // получить всех детей
$section->getIblock(); // родительский инфоблок
/*
* Работа с элементами
*/
$elementList = $section->getElements();
while($element = $elementList->fetch()) {
/*
* Массив преобразуется в AR без дополнительного запроса к базе
*/
$element = new IblockElement($element);
$element->getProperties(); // свойства элемента
/*
* Работа со свойствами
*/
$elementProperty = $element->getProperty(1);
/*
* значение свойства (оба вызова равносильны)
*/
$value = $elementProperty->VALUE;
$value = $elementProperty->getValue();
/*
* при получении значения любым из способов выше,
* значение автоматически приводится к типу свойства одним из методов ниже
*/
$elementProperty->getValueRaw(); // значение без преобразования
$elementProperty->getValueEnum(); // IblockPropertyEnum - значение элемента списка (L)
$elementProperty->getValueFile(); // CFile::GetFileArray
$elementProperty->getValueHtml(); // (string) HTML код
$elementProperty->getValueElement(); // IblockElement - связный элемент (E)
$elementProperty->getValueSection(); // IblockSection - связный раздел (G)
$elementProperty->getValueNumber(); // (float) или (int) в зависимости от значения
/*
* Получить объект свойства
*/
$property = $elementProperty->getMeta();
$property->NAME;
$property->HINT;
}
Методы: getPrimary, getRow, getRowByField, getList, getListByField — идентичны для всех ActiveRecord.
Функционал AR на данный момент достаточно беден (например, нет перекрестного поиска по таблицам), но т. к. они являются оберткой над стандартными функциями, в методах «getList» и «getRow» можно использовать Битриксовые плюшки. После создания / заимствования нормального DAO, этот момент будет допилен.
Hermitage
Сильной стороной Битрикс, и я думаю многие согласятся, является его пользовательский интерфейс a.k.a. «Эрмитаж». Он очень удобен и гибок.
Ниже представлен пример работы с Эрмитажем:
Код
use Jugger\Db\Orm\Ib\IblockSection;
use Jugger\Db\Orm\Ib\IblockElement;
use Jugger\Ui\Hermitage;
use Jugger\Ui\Hermitage\Icon;
use Jugger\Context\UrlManager\Iblock;
/* @var $this CBitrixComponentTemplate */
/* @var $component CBitrixComponent */
/*
* Добавление кнопок "редактировать" и "удалить" для элементов и разделов
*/
$element = IblockSection::getByPrimary(1);
Hermitage::addButtonEditIblockElement($this, $element);
Hermitage::addButtonDeleteIblockElement($this, $element);
$section = IblockSection::getByPrimary(1);
Hermitage::addButtonEditIblockSection($this, $section);
Hermitage::addButtonDeleteIblockSection($this, $section);
/*
* Добавление кнопок в тулбар компонента
*/
Hermitage::addButton(
$component,
Iblock::getElementCreateUrl(1),
"Добавить элемент",
[
"ICON" => Icon::TOOLBAR_CREATE,
]
);
/*
* Добавление кнопок в верхнюю панель
*/
Hermitage::addPanelButton("#", "Надпись", [
"ICON" => Icon::PANEL_TRANSLATE,
]);
Так похвалил и так мало написал)) На самом деле этого достаточно для взаимодействия с пользователем. Очень много нужно реализовать касаемо административного интерфейса, но это уже не Эрмитаж, и это все в планах.
Безопасность
В Битрикс на сколько я знаю (а в данном вопросе, скрывать не буду, я особо не ковырялся), с безопасностью сайта (именно в коде) вообще грустно (только защита от SI). В будущем данный раздел будет содержать в себе инструменты для защиты от различных атак и вредоносных действий (XSS, генерация случайных данных, различные крипто-функции, валидация форм, работа с паролями, …). На данный момент реализован только инструментарий для защиты от CSRF:
use Jugger\Security\Csrf;
/*
* "автоматический" режим
*/
if (Csrf::validateTokenByPost()) {
// ok
}
else {
// error
}
echo Csrf::printInput();
/*
* "ручной" режим
*/
$nameField = "csrf";
$token = Bitrix\Main\Context::getCurrent()->getRequest()->getPost($nameField);
if (Csrf::validateToken($token)) {
// ok
}
else {
// error
}
$token = Csrf::createToken();
echo "<input type='hidden' name='{$nameField}' value='{$token}'>";
После каждой проверки (удачно или неудачной) – токен из сессии удаляется, таким образом проверить токен можно только один раз.
UrlManager
Маршрутизация в Битрикс, не сказал бы что на высоте, поэтому и эта область затронута в Juggernaut. Данный класс позволяет динамически создавать и использовать URL маршруты (используется в компонентах-роутерах).
Рассмотрим пример парсинга и генерирования URL:
Код
use Jugger\Context\UrlManager;
/*
* Установка базового URL и маршрутов
*/
UrlManager::setBaseUrl("/catalog");
UrlManager::addAlias("sectionList", "index.php");
UrlManager::addAlias("elementList", "#SECTION_CODE#/");
UrlManager::addAlias("elementView", "#SECTION_CODE#/#ELEMENT_CODE#/");
/*
* Получаем запрашиваемый 'alias' на основе запроса
* Например, для запроса '/catalog/section1/element1/' будет получен маршрут 'elementView'
*/
$alias = UrlManager::parseRequest();
/*
* Аналог используя старые фукнции (хотя по сути UrlManager тоже их использует, просто это сокрыто в недрах)
*/
$folder404 = "/catalog";
$arUrlTemplates = [
"sectionList" => "index.php",
"elementList" => "#SECTION_CODE#/",
"elementView" => "#SECTION_CODE#/#ELEMENT_CODE#/",
];
$arVariables = []; // UrlManager::$params
CComponentEngine::parseComponentPath($folder404, $arUrlTemplates, $arVariables);
/*
* Добавляем параметры маршрутов в систему
*/
UrlManager::addParams([
"param1" => "value1",
"param2" => "value2",
]);
/*
* Получаем сгенерированый URL.
* Параметры указанные в данном методе будут использоваться локально.
* После выполнения метода параметр "ELEMENT_CODE" - не будет доступен
*/
UrlManager::addParam("SECTION_CODE", "section1");
$url = UrlManager::build("elementView", [
"ELEMENT_CODE" => "element1",
]);
// $url: /catalog/section1/element1/
В дальнейшем планируется также подвязаться и к urlRewrite.php.
События
Данный класс является просто оберткой над функциями D7, с более удобным использованием.
use Jugger\Helper\Event;
/*
* добавление обработчика
*/
Event::on("имя события", function(){
// обработчик
});
Event::on("имя события", "\ClassName::MethodName", "moduleName");
/*
* удаление обработчиков
*/
Event::off("имя события");
Event::off("имя события", 3); // удалить 4-ий по счету (с нуля) обработчик
/*
* Вызов события
*/
Event::trigger("имя события");
/*
* Вызов события с указанием сендера (вызывателя)
*/
Event::trigger("имя события", $this);
Что дальше?
Планы на ближайшее будущее:
- нормальный QueryBuilder
- нормальное кеширование
- нормальный AssetsManager
- набор компонентов для создания и работы с административным интерфейсом
- нормальная маршрутизация (завязанная на HttpException)
Заключение
Много чего задумано, много чего не сделано. Библиотека развивается по мере моей необходимости, поэтому очень зависит от текущих заказов (которое очень часто однотипны) и свободного времени.
Как я уже сказал вначале, проект будет развиваться несмотря ни на что, от количества ее авторов и заинтересованных лиц зависит лишь скорость развития. Так что выбор только за вами:
- ныть, ждать и подстраиваться под Bitrix (а развитие BitrixFramework явно не в приоритете);
- взять все в свои руки и помочь в развитии Juggernaut.
Помочь может каждый желающий филантроп (а иначе никак), для этого нужно:
- поделиться идеей
- рефакторить то что есть
- сделать что-нибудь своими ручками
Проект лежит на GitHub, так что править, добавлять, комментировать и спрашивать может любой желающий.
Спасибо за внимание! Конструктивная критика очень даже приветствуется :-)
P.S. комментарии типа «да на фиг Битрикс» огромная просьба не писать. Я в курсе какое у людей отношение к этой системе, и данный проект как раз направлен на ее облагораживание. Поэтому если вы считаете что «лучше и проще сделать проект на любом фреймворке» — то я это знаю и очень рад за вас, поэтому оставьте свое мнение при себе. Спасибо!
Репозиторий: github.com/irpsv/juggernaut.bitrix_release
Маркетплейс: скоро будет