Будет рассмотрено создание компонента на основе modExtra 1, которую создал товарищ Безумкин на основе официальной заготовки, поставляемой от modx.com. В статьях Безумкина, на мой взгляд, много лишнего по типу "создание modx cloud", "работа с git" и т.д. Делаем на основе первой версии т.к. новая версия лично у меня всегда выдавала 500 на этапе запуска build.php. Решений пока найдено не было.
Устанавливаем заготовку
- Качаем modExtra 1.0.zip;
- Распаковываем в корень сайта;
- Необходимо переименовать компонент так как нам удобно. Открываем файл %stename.ru%/modExtra/rename_it.php?name=fscontests с параметром name и указанием желаемого имени. Переименуются все классы и все папки на то, что укажем в параметре.
- Далее мы можем собарть пакет, а также установить на сайт, чтобы вести разработку на основе уже работающего компонента. Делаем это запустив файл %sitename.ru%/fscontests/_build/build.transport.php. По умолчанию, пакет будет установлен на сайт сразу, а не просто собран. За это отвечает настройка в файле config.inc.php в папке _build. Константа PKG_AUTO_INSTALL true или false соответственно.
- После установки компонента, дропаем кеш и обновляем страницу админки ctrl+F5 в верхнем меню -> Приложения, тыкаем название нашего компонента и получаем что-то типа этого: Тут для тестов можем по создавать обекты и с помощью одноименного сниппета по выводить (который уже присутствует после установки)
Примечание! После развертки компонента (пункт 4), нужно удостовериться, что файл с основным классом лежит в корне папки model, а не еще ниже, например, model/fscontests/
Меняем интерфейс в админке
Честно признаюсь, я не силен в ExtJS, ориентируюсь в коде интуитивно, попутно залезая в документацию.
Под интерфейсом я понимаю - поля типа "название", "описание" и т.д., а также кнопки, вкладки и диалоговые окна. Но интерфейс - это одно, а чтобы это работало, нам нужно будет дописать/переписать процессоры для действий в компоненте.Об этом позже.
Основной принцип работы интерфейса и бэкенда - мы производим действие в админке (получаем ресурсы, нажимаем кнопки и т.д.), ExtJS тем временем отправляет ajax-запрос коннектору, который лежит в assets/components/fscontests/connector.js передавая попутно имя процессора через параметр action (передаются имена процессоров без class.php но с указанием вложенных репозиториев т.е. если процессор лежит в репозитории processors/mgr/item/disable.class.php то передавать будем таким образом - mgr/item/disable). Сами процессоры лежат в core/components/fscontests/processors.
- Открываем файл assets/components/fscontests/js/mgr/widgets/items.grid.js. Он отвечает за отрисовку ресурсов в компоненте. Находим функции getFields и getColumns и меняем списки колонок. Я добавил несколько полей и переименовал булевое поле.
- Далее если мы сохраним этот файл и обновим нашу страничку с компонентом ctrl+F5 то увидим, что разметка изменилась, но заголовки пустые. Нам необходимо их заполнить в словаре. Подобные записи fscontests_item_name, fscontests_item_city и т.д. - это записи словаря, мы их указали в интерфейсе, но не прописали в словарь. Заходим в core/components/fscontests/lexicon/ru/default.inc.php. При разработке компонента делаем это в файле, а не в управлении словарями т.к. во втором случае в файл ничего не сохранится, а нам это необходимо. По аналогии с уже существующими добавляем наши элементы. Также можем редактировать уже имеющиеся дефолтные под себя. Получаем: Дропаем кеш сайта, обновлям страничку и видим как заголовки отобразились.
- Настраиваем диалоговое окно. Открываем assets/components/fscontests/js/mgr/widgets/items.windows.js. Это файл, генерирующий диалоговые окна. По сути он отвечает за 2 диалоговых окна - при создании и при редактировании элемента. Для обоих случаев схема полей одинаковая. Настраиваем ее для create, а затем дублируем для update.
Настраиваем логику
По дефолту в процессорах организовано все так, что все делается автоматически, например core/components/fscontests/processors/mgr/item/getlist.class.php который отвечает за вывод элементов в компоненте - он выводит все, что есть в базе в таблице fscontests_items. Если нужна какая-то особая логика то можно переписать, но необходимо учитывать, что наш процессор наследует родительский в котором уже многое предусмотрено. Ищем на гитхабе.
Необходимо помнить, что процессор ВСЕГДА должен возвращать имя процессора
-
Поиск. После того как мы изменили поля нашего компонента, у нас отвалился поиск в компоненте. Произошло это из-за того, что поиск дефолтного компонента настроен на 2 поля - description, name, первый из которых мы вообще убрали. Чтобы его починить нужно изменить sql-запросы в методе prepareQueryBeforeCount, добавив в него те поля, по которым мы хотим организовать поиск. Файл - core/components/fscontests/processors/mgr/item/getlist.class.php.
Было:
public function prepareQueryBeforeCount(xPDOQuery $c) { $query = trim($this->getProperty('query')); if ($query) { $c->where([ 'name:LIKE' => "%{$query}%", 'OR:description:LIKE' => "%{$query}%" ]); } return $c; }
Стало:
public function prepareQueryBeforeCount(xPDOQuery $c) { $query = trim($this->getProperty('query')); if ($query) { $c->where([ 'page:LIKE' => "%{$query}%", 'OR:name:LIKE' => "%{$query}%", 'OR:phone:LIKE' => "%{$query}%", 'OR:email:LIKE' => "%{$query}%" ]); } return $c; }
- Схема БД. Это файл xml, который содержит в себе информацию о создаваемых таблицах БД и зависимостях элементов. При установке компонента, именно от этого файла будет зависеть создадутся ли корректно наши таблицы. Расположение - core/components/fscontests/model/schema/fscontests.mysql.schema.xm
В случае с нашим компонентом, делаем так:
<?xml version="1.0" encoding="UTF-8"?> <model package="fscontests" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" phpdoc-package="fscontests" version="1.1"> <object class="fscontestsItem" table="fscontests_items" extends="xPDOSimpleObject"> <field key="name" dbtype="varchar" precision="100" phptype="string" null="false" default=""/> <field key="city" dbtype="varchar" precision="50" phptype="string" null="true" default=""/> <field key="phone" dbtype="varchar" precision="50" phptype="string" null="true" default=""/> <field key="email" dbtype="varchar" precision="50" phptype="string" null="true" default=""/> <field key="done" dbtype="tinyint" precision="1" phptype="boolean" null="true" default="0"/> <index alias="name" name="name" primary="false" unique="false" type="BTREE"> <column key="name" length="" collation="A" null="false"/> </index> <index alias="active" name="active" primary="false" unique="false" type="BTREE"> <column key="active" length="" collation="A" null="false"/> </index> </object> </model>
- Карта данных. Карта данных необходима, чтобы мы могли используя xPDO обращаться к нашим таблицам. Принцип корректировки почти такой же как и для схемы БД. Расположение - core/components/fscontests/model/fscontests/mysql/fscontestsitem.map.inc.php.
Делаем следующее:
<?php $xpdo_meta_map['fscontestsItem']= array ( 'package' => 'fscontests', 'version' => '1.1', 'table' => 'fscontests_items', 'extends' => 'xPDOSimpleObject', 'tableMeta' => array ( 'engine' => 'MyISAM', ), 'fields' => array ( 'name' => '', 'city' => '', 'phone' => '', 'email' => '', 'done' => 1, ), 'fieldMeta' => array ( 'name' => array ( 'dbtype' => 'varchar', 'precision' => '100', 'phptype' => 'string', 'null' => false, 'default' => '' ), 'city' => array ( 'dbtype' => 'varchar', 'phptype' => '50', 'null' => true, 'default' => '' ), 'phone' => array ( 'dbtype' => 'varchar', 'phptype' => '50', 'null' => true, 'default' => '' ), 'email' => array ( 'dbtype' => 'varchar', 'phptype' => '50', 'null' => true, 'default' => '' ), 'done' => array ( 'dbtype' => 'tinyint', 'precision' => '1', 'phptype' => 'boolean', 'null' => true, 'default' => 1 ), ), 'indexes' => array ( 'name' => array ( 'alias' => 'name', 'primary' => false, 'unique' => false, 'type' => 'BTREE', 'columns' => array ( 'name' => array ( 'length' => '', 'collation' => 'A', 'null' => false, ), ), ), 'done' => array ( 'alias' => 'done', 'primary' => false, 'unique' => false, 'type' => 'BTREE', 'columns' => array ( 'done' => array ( 'length' => '', 'collation' => 'A', 'null' => false, ), ), ), ), );