ООП-конструктор админки для Битрикс

Чем серьёзнее мы относимся к своим проектам, тем больше нам хочется, чтобы задачи решались лучшим из возможных способов. Например, хотим мы предоставить клиенту качественную админку в адекватные сроки. Лично мне в такие моменты сразу вспоминается Django: создал модель – получи админку. Или виджеты в Yii. Или чудная комбинация из хуков и классов в Drupal 7. Или Sonata в Symfony, про которую я, правда, только лишь слышал. А что делать, если нам достался Битрикс?

Админка по битриксовому «фен-шую»


К сожалению, Битрикс, несмотря на попытки разработчиков как-то исправить положение, во многих своих аспектах остаётся системой архаичной: процедурные куски кода в несколько сотен строк, копипаст, возведённый до уровня мануала, классы, от которых невозможно нормально наследоваться – всё это по сей день остаётся реальностью для тех, кому приходится работать с этой системой. И, уверен, пройдёт это не скоро.

Что должен делать разработчик, если ему нужно создать административный интерфейс для какой-либо кастомной таблицы в БД? Согласно мануалу, нам нужно скопировать «рыбу» с кодом в 417 строк – для страницы списка элементов и 365 строк – для страницы редактирования элемента. Ну или написать всё самим, если мы счастливые обладатели феноменальной памяти. Что ж, 2016 год на дворе – хорошее начало!

Но ведь у нас ещё ничего не работает! После того, как мы совершили акт копипаста, нам нужно внимательно вычитать 782 строки кода, удалить всё лишнее и дописать своё. А именно:

  1. Написать валидацию данных фильтров.
  2. Указать список колонок для фильтрации выборки.
  3. Написать обработку действий над отдельным элементом и над группой элементов списка.
  4. Сделать саму выборку. Причём обычно никто не парится, делают просто SELECT * FROM … — в «рыбе» от битрикса никак не предлагается ограничивать список выбираемых полей только теми, которые необходимы.
  5. Указать список колонок для вывода в списке.
  6. В процессе вывода списка для каждой колонки вывести определённый элемент управления.
  7. Вывести футер таблицы.
  8. Вывести фильтр над таблицей.

Это для страницы списка. Я специально указывал пункты не в том порядке, в котором подсказывает логика, и в котором они выводятся на результирующей странице, а в том порядке, в котором этот код встречается в «рыбе» из мануала.

Теперь, что нам нужно сделать, если мы решили, скажем, добавить в список ещё одно поле? Или даже просто переименовать какое-то существующее? Мы должны в 7-и местах прописать это новое поле или изменить существующее, ни разу не ошибившись! Ситуация осложняется тем, что вместе с php-кодом в этом же файле идёт и html, притом совершенно не в том порядке, в котором он выводится на странице, нечитабельный ни вашей любимой IDE, ни человеческим глазом, потому что многие теги генерируются где-то в недрах. В этом всём очень сложно ориентироваться. Особенно когда страница совсем уже не простая и на ней содержится ещё и JS-код, как правило писанный инлайном.

Что мы получаем в итоге? Баги. Сложность поддержки. Неоправданно высокие временные затраты даже при изменении какой-либо мелочи. Для страницы редактирования элемента ситуация такая же. Искренне не понимаю, как столько лет можно было жевать такой кактус?!

Как всё могло бы быть


Как ни странно, API для админки у Битрикса спроектирован неплохо. После вышеописанных ужасов в это трудно поверить, однако это действительно так. Потому что проблема не в самом API, а в том, как его дальше стали использовать. Создаётся такое впечатление, что разработчик(и) API имели насчёт него какие-то планы на будущее, либо просто некие смутные прозрения, но не сделали простого и логичного следующего шага: создания набора MVC-классов. Вероятно, причиной тому – отсутствие до недавнего времени единого интерфейса работы с БД.

После просмотра великого множества самописных админок становится ясно, что, вне зависимости от сложности и особенностей задачи, процесс построения админки включает в себя одни и те же шаги, которые я описал выше. А значит, и код везде один и тот же, остаётся только менять входные данные. Можно выделить следующие сущности пока не привязываясь к коду:

  1. Конфиг интерфейса: список полей, который будет использоваться для формирования фильтров, колонок таблицы списка или набора инпутов на странице редактирования.
  2. Класс-представление для вывода интерфейса. На входе он должен получать конфиг, «под капотом» у него будет вся та логика, которую мы видим в «рыбе» от битрикса, на выходе он выдаст отрисованную страничку.
  3. Виджет. Содержит в себе логику работы отдельного поля админки. В списке с его помощью отрисовываются ячейки таблицы, на странице редактирования – поля элемента.

Справедливости ради надо сказать, что отголоски этой концепции видны в исходном коде битрикса: в частности, «пользовательские типы», которые есть как для инфоблоков, так и для «Highload»-инфоблоков – не что иное, как «виджеты» в вышеописанной схеме.

Реализовав вышеописанные классы, мы могли бы существенно сократить «рыбу» от битрикса до чего-то подобного:

$fields = include(‘fields.conf.php’);
$adminListHelper = new MyHelper($fields);
$adminListHelper->buildList(array($by => $order));
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_admin_after.php");
$adminListHelper ->createFilterForm();
$adminListHelper ->show();
require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/epilog_admin.php");

В этом семистрочном куске кода проглядываются основные шаги по созданию админки, описанные в начале статьи. Но вместо того, чтобы копировать этот, пусть и короткий, сниппет каждый раз, лучше потрудиться ещё немного и сделать вот что:

  • вышеуказанный код прописать в специальном файле route.php, на который будут перенаправляться все запросы к административному интерфейсу, созданному через нашу надстройку над битриксовым API;
  • в файле с описанием конфига интерфейса выполнять регистрацию этого конфига в какой-либо глобальной переменной или статической переменной класса;
  • при обращении к страницам административного интерфейса использовать не прямые URL, а псевдонимы и функции, конструирующие правильный URL из этих псевдонимов;
  • в итоге, все запросы придут в route.php, который и разберётся, какой класс нужно создавать, какой конфиг интерфейса в него передавать, и как всё это выводить.

В итоге, код, необходимый для создания базовых страниц списка и редактирования, сокращается в разы, и, поскольку речь уже не идёт о сотнях строк, я могу привести его здесь:

Класс списка
class TableListHelper extends AdminListHelper
{
    static protected $model = 'MyModelTable';
    static public $module = 'my.module';
    static protected $viewName = 'table_list';
    static protected $editViewName = 'table_detail';
}


Класс страницы редактирования
class TableEditHelper extends AdminEditHelper
{
    static protected $model = 'MyModelTable';
    static public $module = 'my.module';
    static protected $listViewName = 'table_list';
    static protected $viewName = 'table_detail';
}


Настройки интерфейса
AdminBaseHelper::setInterfaceSettings(
    array(
        'FIELDS' => array(
            'ID' => array(
                'WIDGET' => new NumberWidget(),
                'TITLE' => 'ID',
                'TAB' => 'TAB_ONE'
            ),
            'STRING' => array(
                'WIDGET' => new StringWidget(),
                'TITLE' => 'STRING',
                'TAB' => 'TAB_ONE'
            ),
            'NUMBER' => array(
                'WIDGET' => new NumberWidget(),
                'TITLE' => 'NUMBER',
                'TAB' => 'TAB_ANOTHER'
            ),
            'TEXT' => array(
                'WIDGET' => new TextAreaWidget(),
                'TITLE' => 'TEXT',
                'TAB' => 'TAB_ANOTHER'
            )
        ),
        'TABS' => array(
            'TAB_ONE' => Loc::getMessage('TAB_ONE'),
            'TAB_ANOTHER' => Loc::getMessage('TAB_ANOTHER'),
        )
    ),
    array(
        '\TableEditHelper',
        '\TableListHelper'
    ),
    'my.module'
);


Файл menu.php
$menu = array(
    array(
        "parent_menu" => "global_menu_services",
        "section" => "table",
        "sort" => 140,
        "text" => Loc::getMessage('TABLE_MENU_TEXT'),
        "title" => Loc::getMessage('TABLE_MENU_TITLE'),
        "icon" => "table_menu_icon",
        "page_icon" => "table_page_icon",
        "items_id" => "menu_table",
        "url" => TableEditHelper::getListPageURL(),
        "more_url" => array(
            TableListHelper::getEditPageURL()
        ),
    ),
);

return $menu; 


Никаких сотен строк копипаста и копирования файлов в /bitrix/admin – а в результате получаем вполне рабочую админку для таблицы с четырьмя колонками: страницу списка, страницу редактирования и ссылки на них в меню системы. С поддержкой CRUD-операций «из коробки». С нормальным роутингом (в данном примере – /bitrix/admin/route.php?module=my.module&view=tab_list откроет страницу списка. Можно доработать это до «ЧПУ», если есть желание или необходимость). Дальше только переопределяем методы базовых классов, чтобы кастомизировать её поведение под свои задачи. Выглядит заманчиво, не правда ли?

Будущее уже здесь!


А теперь о приятном: описанное выше – уже не просто концепт, а реальный, работающий, уже побывавший в продакшене многих проектов, модуль, которым мне и хотелось бы поделиться:

github.com/DigitalWand/digitalwand.admin_helper

Код становится на несколько порядков лаконичнее, шаблонный копипаст сводится к минимуму, уступая место массивам с конфигурацией, чего в битриксе в принципе не было:

Объём кода с использованием модуля и без него
Сравнение основано на:


Модуль может работать как с полностью кастомными таблицами, так и с таблицами, созданными через битриксовый функционал «Highload»-инфоблоков, при этом вместо «виджетов» есть возможность использовать классы «пользовательских свойств». Таким образом, весь функционал, доступный в админке «Highload»-инфолоков, доступен и нам, только теперь мы можем без труда кастомизировать его под свои нужды.

Также должен предупредить читателей, что в данной статье был умышленно использован «старый стиль» работы с модулем из его первой версии, дабы нагляднее продемонстрировать внутренний механизм его работы. В последних версиях в классах-хэлперах достаточно указать только модель – всё остальное модуль определит сам.

Ещё из полезных материалов имеются:
  • Небольшая презентация, призванная убедить сторонников «олдскула» и «битрикс-вэя» перейти на новые «рельсы».
  • Схема, в общих чертах иллюстрирующая архитектуру модуля: Архитектура модуля


Хочется завершить статью словами благодарности авторам Qt Framework, который вдохновил на стремление к прекрасному и в вебе, пожеланием успеха тем, кто сейчас активно развивает этот модуль, а также надеждой, что когда-нибудь писать под Битрикс станет не только выгодно, но и приятно.
Share post

Similar posts

Comments 19

    +5
    Это все хорошо, но на дворе и правда 2016 год, а 1С-Битрикс — коммерческий продукт, с вышедшим в 2013 году новым ядром D7, для которого за 3 года не сподобились сделать нормальную документацию. Я много лет работал с Битриксом, но последнее время все это уж очень похоже на некрофилию, так что я решил распрощаться с ним, чему несказанно рад.
      0
      С документацией там всегда была беда, и часто не только со свежими модулями.
      0
      По сравнению с тем же open source opencart архитектура битрикс просто ужасна…
      Не вижу никакой пользы от битрикс после opencart или magento
        0
        opencart ужасен в той же мере степени, что и битрикс (если не в большей). А вездесущий VQmod — тому доказательство.
          –2
          Серьезно?!
          vQmod в opencart 2.x нету — это раз.
          И vQmod — это не часть архитектуры совершенно (по идее его можно прилепить к любой cms). Не путайте. К тому же он исключен из совместимых дополнений opencart
          Судя по этому, я смотрю вы поверхностно знакомы с opencart. Поэтому не вам судить архитектуру opencart, при поверхностных знаниях её

          Два — модификаторы отличный инструмент. И очень плохо, что другие cms его не используют. Да — костыль, но хороший костыль, очень сильно развивающий инфраструктуру и сообщество, соответственно количество модулей, тем, популярность.
          А вот как раз архитектура opencart очень хорошая. Можно даже без модификаторов её использовать и «подменять» все. Там есть своего рода система «прослушки» всех методов, которой профессионалы пользуются.
          К тому же opencart из популярных open source систем самый безопасный, там безопасность заложена в архитектуре
          Так что не сравнивайте х с пальцем
            0
            > И vQmod — это не часть архитектуры совершенно

            Именно. И появился он в опенкарте не от хорошей жизни. Вовсе не от того, что там так все удобно и классно расширяется.
            Только задумайтесь! Для расширения системы, необходимо вклинится и заменить код.

            Что там? Во 2.х ветке всё прекрасно? Хаха!
            Ну что там, с чего начнем? С полного несоответствия ни одному psr, или с запросов к бд обернутых в лупы?
            Ну право же — это просто смешно.
              –2
              А кто сказал opencart плохо " расширяется?
              Я смотрю вы вообще не знаете архитектуру opencart! Квалификации нет совершенно
              Так что віучите сначала мат. часть а потом минусуйте

              Все можно «перехватить» и «прослушать», изменить без модификаторов. Всё для этого в архитектуре заложено. Я не пользуюсь модификаторами и спокойно, как ві пишите" вклиниваюсь в код без них, не изменяя ни одной строчки кода модификаторами.

              Модификаторы, я написал для кого, для программистов с малой квалификацией, которые и используют vQmod.
              Вы читайте внимательно и изучите детально архитектуру.

              Так что ваши инсинуации просто смешны.
                +1
                Окей, Вы правы, а я нет. Там всё по строго по psr, и запросы к бд в циклах не производятся, и вообще всё огонь. А этот кусок гуано ниразу не шесть экранов в ширину, и вообще из другого репозитория, который не имеет никакого отношения к opencart. Еще раз простите, я просто не подумал, что Вы настолько опытны. Хотел «красануться», понимете ли, и спорол чушь. Но не тут-то было — явились Вы (отец модульного подхода, и учитель Фаулера, судя по тексту) поставили меня на место. Тысячи извинений.
                  0
                  Уважаемый, не путайте «драйвер» формирования запросов и архитектуру. В этом у многих заблуждения. Они путают культуру «местного» кода с архитектурой.
                  Еще надо разобраться что удобнее при формировании запросов, такой метод, или методами добавления. Я могу очень много ни лицеприятного о «методах» формирования запросов сказать, когда их используют очень агрессивно.
                  Я если честно, конечно же изменил бы здесь драйвер формирования запросов, добавил бы в драйвер дополнительные методы добавления пользовательских вариантов. Но не усложнял бы сильно.
                  Идеального ПО априори не бывает. И на солнце есть пятна.
                  Так что еще раз убедился в поверхностных знаниях у вас opencart
                  Еще раз убедился, что на хабре поверхностные знания считаются достижениями «гуру»
        +1
        Искреннее вам сочувствие от django-адептов!
          0
          Сам перешел на django, нет слов описать на сколько стало легче дышать.
        • UFO just landed and posted this here
            0
            Это так и выросло из «негативной обратной связи». Навсегда запомню этот день: 2013 год, обычный летний день, и ни что не предвещало беды, как вдруг…

            Двое молодчиков написали каждый по своему модулю методом слепого копипаста. В результате появилось два класса-близнеца для работы с базой. Метод Add был скопирован у третьих лиц, а поэтому не добавлял элементы, если обязательные поля были, внимание, заполнены: функцию валидации тоже криво откуда-то скопировали. Но Add всё равно никто не пользовался, а добавляли новые записи методом Edit. На мой вопрос «почему» ответ был «а не знаю, я откуда-то скопировал».

            После того, как эти люди были устранены с проекта, я ещё порядком начитался кода, написанного «в лучших традициях», а потом не выдержал. D7 тогда ещё не было, поэтому сначала пришлось написать простенькую ORM, чтобы генератор админки вообще стал возможен.

            Так что не уверен, надо ли быть каким-то специальным человеком, чтобы заметить, что король-то голый…
            0
            А вот интересно, как битрикс относиться к PHP7?
              +1
              Недавно на своей конференции сказали, что на тестовой машине им удалось его запустить на php7.
              За 1-2 месяца смогут привести его к виду, работающему на php7. Главный косяк битрикса (по заявлению самого битрикса)в php7 это класс
              \Bitrix\Main\Text\String
              
              , из-за которрого возникает фатал.
                0
                Главный косяки битрикса — это сам битрикс.
                  0
                  Это то да, но то, что я перечислил, по заявлению самого битрикса.
                  0
                  Ну если всего один класс и, судя по названию, это какие-то вспомогательные методы работы со строками — его-то можно осилить и поправить не ломая API!
                    0
                    При чём тут класс или апи. Я про всю ущербность данного продукта.

              Only users with full accounts can post comments. Log in, please.