Создание компонента

09 Сентября 2019 15:03

Будет рассмотрено создание компонента на основе modExtra 1, которую создал товарищ Безумкин на основе официальной заготовки, поставляемой от modx.com. В статьях Безумкина, на мой взгляд, много лишнего по типу "создание modx cloud", "работа с git" и т.д. Делаем на основе первой версии т.к. новая версия лично у меня всегда выдавала 500 на этапе запуска build.php. Решений пока найдено не было.

Устанавливаем заготовку

  1. Качаем modExtra 1.0.zip;
  2. Распаковываем в корень сайта;
  3. Необходимо переименовать компонент так как нам удобно. Открываем файл %stename.ru%/modExtra/rename_it.php?name=fscontests с параметром name и указанием желаемого имени. Переименуются все классы и все папки на то, что укажем в параметре.
  4. Далее мы можем собарть пакет, а также установить на сайт, чтобы вести разработку на основе уже работающего компонента. Делаем это запустив файл %sitename.ru%/fscontests/_build/build.transport.php. По умолчанию, пакет будет установлен на сайт сразу, а не просто собран. За это отвечает настройка в файле config.inc.php в папке _build. Константа PKG_AUTO_INSTALL true или false соответственно.
  5. После установки компонента, дропаем кеш и обновляем страницу админки 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.

  1. Открываем файл assets/components/fscontests/js/mgr/widgets/items.grid.js. Он отвечает за отрисовку ресурсов в компоненте. Находим функции getFields и getColumns и меняем списки колонок. Я добавил несколько полей и переименовал булевое поле.

  2. Далее если мы сохраним этот файл и обновим нашу страничку с компонентом ctrl+F5 то увидим, что разметка изменилась, но заголовки пустые. Нам необходимо их заполнить в словаре. Подобные записи fscontests_item_namefscontests_item_city и т.д. - это записи словаря, мы их указали в интерфейсе, но не прописали в словарь. Заходим в core/components/fscontests/lexicon/ru/default.inc.php. При разработке компонента делаем это в файле, а не в управлении словарями т.к. во втором случае в файл ничего не сохранится, а нам это необходимо. По аналогии с уже существующими добавляем наши элементы. Также можем редактировать уже имеющиеся дефолтные под себя. Получаем:

    Дропаем кеш сайта, обновлям страничку и видим как заголовки отобразились.
  3. Настраиваем диалоговое окно. Открываем assets/components/fscontests/js/mgr/widgets/items.windows.js. Это файл, генерирующий диалоговые окна. По сути он отвечает за 2 диалоговых окна - при создании и при редактировании элемента. Для обоих случаев схема полей одинаковая. Настраиваем ее для create, а затем дублируем для update.

Настраиваем логику

По дефолту в процессорах организовано все так, что все делается автоматически, например core/components/fscontests/processors/mgr/item/getlist.class.php который отвечает за вывод элементов в компоненте - он выводит все, что есть в базе в таблице fscontests_items. Если нужна какая-то особая логика то можно переписать, но необходимо учитывать, что наш процессор наследует родительский в котором уже многое предусмотрено. Ищем на гитхабе.

Необходимо помнить, что процессор ВСЕГДА должен возвращать имя процессора
  1. Поиск. После того как мы изменили поля нашего компонента, у нас отвалился поиск в компоненте. Произошло это из-за того, что поиск дефолтного компонента настроен на 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;
    }
  2. Схема БД. Это файл 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>
  3. Карта данных. Карта данных необходима, чтобы мы могли используя 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,
            ),
          ),
        ),
      ),
    );