Наверх

Как работают дополнения MODX. Часть 5 — Подготовка процессора

В нашей таблице можно вывести любые объекты MODX. Но для примера создадим свой тип объектов, так как это самая частая задача при разработке дополнений.

Хранить объекты будем в базе данных MODX. Для этого создадим в ней табличку с таким же префиксом, как и у других таблиц:
CREATE TABLE `modx_things_names` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
    `description` TEXT CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
    `active` TINYINT(1) UNSIGNED NOT NULL DEFAULT 1,
    
    PRIMARY KEY (`id`),
    INDEX (`active`)
) ENGINE=MyISAM CHARSET=utf8 COLLATE utf8_general_ci;

Ну и можно через PhpMyAdmin заполнить тестовыми данными


Чтобы MODX мог получать данные из нашей таблицы, ему надо о ней «рассказать». Для этого нужно создать xPDO-модель. Создаём папку /core/components/things/model/things/ и в ней файл metadata.mysql.php
<?php
$xpdo_meta_map = array(
    'xPDOSimpleObject' => // Наши объекты будут расширять xPDOSimpleObject
        array(
            0 => 'ThingsName', // Придумываем название класса для наших объектов
        ),
);

Если мы создаём несколько таблиц в рамках одного дополнения, прописываем все эти таблицы в нашем файле. Теперь для каждой таблицы нужно создать 3 файла:

/core/components/things/model/things/thingsname.class.php
<?php
class ThingsName extends xPDOSimpleObject {}

/core/components/things/model/things/mysql/thingsname.class.php
<?php
require_once (dirname(dirname(__FILE__)) . '/thingsname.class.php');
class ThingsName_mysql extends ThingsName {}

/core/components/things/model/things/mysql/thingsname.map.inc.php
В этом файле нужно описать все поля объекта и его связи.
<?php
// Указываем, к какому объекту относится описание
$xpdo_meta_map['ThingsName'] = array(
    'package' => 'things',
    'version' => '1.1',
    'table' => 'things_names', // Имя соответствующей таблицы
    'extends' => 'xPDOSimpleObject',
    'fields' => // Список полей объекта
        array(
            'name' => '',
            'description' => '',
            'active' => 1,
        ),
    'fieldMeta' =>
        array( // Описываем свойства каждого поля
            'name' =>
                array(
                    'dbtype' => 'varchar', // Тип поля в БД
                    'precision' => '255', // Длина поля
                    'phptype' => 'string', // Тип поля в PHP
                    'null' => false, // Допустимо ли значение NULL
                    'default' => '', // Значение по умолчанию
                ),
            'description' =>
                array(
                    'dbtype' => 'text',
                    'phptype' => 'string',
                    'null' => true,
                    'default' => '',
                ),
            'active' =>
                array(
                    'dbtype' => 'tinyint',
                    'precision' => '1',
                    'phptype' => 'boolean',
                    'null' => true,
                    'default' => 1,
                ),
        ),
);

Этого достаточно, чтобы следующий код успешно отработал в любом сниппете:
<?php
$modx->addPackage('things', MODX_CORE_PATH . 'components/things/model/');
$things = $modx->getCollection('ThingsName');
$output = array();
foreach ($things as $thing) {
    $output[] = $thing->name;
}
return implode(PHP_EOL, $output);

Но нам нужно, чтобы список объектов был получен в процессоре getlist, чтобы вывести потом в табличке.

Можно вызывать метод addPackage в процессоре, но процессоров у нас потом будет несколько, поэтому вызовем этот метод в коннекторе /assets/components/things/connector.php:
<?php
$base_path = dirname(__FILE__);
while (!file_exists($base_path . '/config.core.php')) {
    $base_path = dirname($base_path);
}

require_once $base_path . '/config.core.php';
require_once MODX_CORE_PATH . 'config/' . MODX_CONFIG_KEY . '.inc.php';
require_once MODX_CONNECTORS_PATH . 'index.php';

// Подключаем модель наших объектов
$modx->addPackage('things', MODX_CORE_PATH . 'components/things/model/');
$modx->request->handleRequest(array(
	'processors_path' => MODX_CORE_PATH . 'components/things/processors/',
	'location' => '',
));

Теперь в процессоре мы можем получить список всех объектов, подставив свойства limit и start:
<?php
class ThingsGetListProcessor extends modProcessor {

    public function process() {
        $array = array();
        // Создаём запрос
        $q = $this->modx->newQuery('ThingsName');
        // Узнаём общее количество объектов
        $total = $this->modx->getCount('ThingsName', $q);
        // Передаём в запрос start и limit
        $limit = $this->getProperty('limit');
        $start = $this->getProperty('start');
        $q->limit($limit, $start);
        // Получаем объекты и добавляем их в массив
        $objects = $this->modx->getCollection('ThingsName', $q);
        foreach ($objects as $object) {
            $array[] = $object->toArray();
        }
        // Отдаём данные в виде массива табличке ExtJS
        return json_encode(array(
            'success' => true,
            'total' => $total,
            'results' => $array
        ));
    }

}
return "ThingsGetListProcessor";

Как ни удивительно, для таких процессоров в MODX тоже есть заготовки. Всё, что мы прописали здесь (и даже немного больше) уже прописано в классе modObjectGetListProcessor. Мы можем унаследоваться от него, указав только нужный нам класс объекта:
<?php
// Меняем название процессора — просто для удобства
class ThingsNameGetListProcessor extends modObjectGetListProcessor {
    public $classKey = 'ThingsName'; // Класс объекта
    public $defaultSortField = 'id'; // По какому полю сортировать
}
return "ThingsNameGetListProcessor";

В табличке выведем новые поля, которые есть у наших объектов:
Things.grid.Names = function (config) {
    config = config || {};
    Ext.apply(config, {
        columns: [
            {dataIndex: 'id', width: 150, header: 'ID'},
            {dataIndex: 'name', width: 250, header: 'Name'},
            
            // Добавляем новые колонки
            {dataIndex: 'description', width: 500, header: 'Description'},
            {dataIndex: 'active', width: 100, header: 'Active'}
        ],
        autoHeight: true,
        viewConfig: {
            forceFit: true,
            scrollOffset: 0
        },
        url: Things.config.connector_url,
        action: 'mgr/thing/getlist',
        
        // Добавляем новые поля
        fields: ['id','name', 'description', 'active'],
        paging: true,
        pageSize: 1
    });
    Things.grid.Names.superclass.constructor.call(this, config);
}
Ext.extend(Things.grid.Names, MODx.grid.Grid);
Ext.reg('things-grid-names', Things.grid.Names);

У колонок можно указать renderer — это функция, которая определяет, как оформить поле в зависимости от его значения:
// ...
{dataIndex: 'active', width: 100, header: 'Active', renderer: function(value) {
        if (value) { // Если значение поля == true
            return '<span class="green">Yes</span>';
        } else { // Иначе
            return '<span class="red">No</span>';
        }
    }}
// ...

Так же renderer можно использовать, чтобы отобразить картинку, если в базе хранится её адрес:
// ...
{dataIndex: 'image', width: 100, header: 'Image', renderer: function(value) {
        return '<img src="' + value + '">';
    }}
// ...

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


17 комментариев

  1. Саня 28 октября 2017, 20:50 # 0
    Илья, привет.

    При вызове сниппета ничего не происходит, в логах ошибка:
    Could not get table class for class: FormconstructorName
    Error 42000 executing statement: 
    Array
    (
        [0] => 42000
        [1] => 1064
        [2] => You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'AS `FormconstructorName`' at line 1
    )
    
    P.S. ThingsName заменил на FormconstructorName
    1. Илья Уткин 28 октября 2017, 21:55 # 0
      Точно во всех файлах заменил? Где-то ошибка в модели.
      1. Саня 29 октября 2017, 16:46 # 0
        Сегодня сделал все заново с первого урока. Ничего не менял. Ошибка та же:
        Could not get table name for class: ThingsName
        ...
        
        Если интересно, то могу дать доступ в админку.
        1. Саня 29 октября 2017, 16:54 # +1
          Я нашел проблему.

          Вот этот файл thingsname.map.inc.php должен лежать в
          core/components/things/model/things/mysql/
          
          а у тебя в статье
          core/components/things/model/myextra/things/
          
          1. Илья Уткин 30 октября 2017, 08:42 # 0
            Действительно) Невнимательность моя)
            1. Alexander Neishkasha 05 апреля 2018, 13:11 # 0
              Измените в статье, пожалуйста:
              /core/components/things/model/things/things/thingsname.map.inc.php
              на:
              /core/components/things/model/things/mysql/thingsname.map.inc.php

              Я часа два сидел и не мог понять, почему у меня не работает))
              1. Илья Уткин 05 апреля 2018, 13:29 # 0
                Да, спасибо, поправил)
    2. УстюПаша 28 октября 2017, 23:10 # +1
      Очень долгожданно! Осталось самое интересное! Создание и редактирование )
      1. Станислав Однолетко 12 ноября 2017, 01:34 # 0
        Привет, Илья. У меня что-то тоже класс найти не может. Структура файлов такая rgho.st/6sMzfRJNn/thumb.png

        1. Станислав Однолетко 13 ноября 2017, 17:09 # 0
          Илья, пути все на месте, но возникла следующая ошибка Could not get table class for class: ThingsName. Чет не могу догнать в чем дело. Эта ошибка не связана случайно с моделью xml?(догадка)
          1. Станислав Однолетко 13 ноября 2017, 17:15 # 0
            Вообщем, разобрался, все таки в путях проблема была)
          2. catsmeatman 04 октября 2018, 13:28(Комментарий был изменён) # 0
            Тут ошибка
            'description' =>
                            array(
                                'dbtype' => 'text',
                                'phptype' => 'text',
                                'null' => true,
                                'default' => '',
                            ),
            phptype' => 'text' не существует, есть phptype' => 'string'
            1. Илья Уткин 05 октября 2018, 08:17 # 0
              Да, конечно) Спасибо
            2. Анастасия 21 января 2019, 17:54 # 0
              Добрый день! А можно ли как-то выполнить процедуру, которая была написана для своей таблицы?
              1. Илья Уткин 22 января 2019, 12:05 # 0
                Здравствуйте. Так мы как раз для «своей» таблицы это и делаем. В MODX нет таблицы things
              2. Максим 11 августа 2019, 08:47 # 0
                Добрый день Илья. У меня такая задача, нужно в своей вкладке вывести список ресурсов с определенным родителем, но я не могу понять как это сделать. Делаю так:

                MODx.grid.Names = function (config) { // Придумываем название, например, «Names»
                    config = config || {};
                    Ext.apply(config, {
                        // Сюда перемещаем все свойства нашей таблички
                        columns: [ // Перечисляем список столбцов
                            {dataIndex: 'id', width: 150, header: 'ID'},
                            {dataIndex: 'pagetitle', width: 250, header: 'Товар'},
                        ],
                
                        name: 'resource_id',
                        url: MODx.config.connector_url,
                        params: {
                            action: 'resource/getlist',
                            id: 523
                        },
                        typeAhead: true,
                        editable: true,
                        anchor: '99%',
                        paging: true,
                        pageSize: 20,
                        fields: ['pagetitle','id'],
                    });
                    MODx.grid.Names.superclass.constructor.call(this, config);
                }
                Ext.extend(MODx.grid.Names, MODx.grid.Grid);
                Ext.reg('things-grid-names', MODx.grid.Names);
                
                
                Пишет что процессор не найдет. Если вместо params поставить baseParams то выводятся все ресурсы со всех родителей, как будто выставлен родитель = 0. Подскажите как можно в процессор передать значения?
                1. Илья Уткин 19 августа 2019, 09:21 # 0
                  Стандартный процессор не может ограничивать выборку по родителю. Вам нужно писать свой процессор.

                Авторизация

                через сервис Loginza:


                Шаблоны MODX

                1 2 Дальше »

                Объектная
                модель
                MODX