Связи объектов в CustomExtra
Сегодня я покажу, как быстренько модернизировать CustomExtra, чтобы объекты можно было связать друг с другом.
Для начала представим гипотетическую ситуацию, в которой у нас на сайте есть некие «Предметы» и с каждым предметом можно проводить некие «Операции». Попробуем связать Предметы и Операции.
После установки CustomExtra включим 2 вкладки — у предметов 0 числовых, строковых и текстовых полей, а у операций — 1 дополнительное числовое поле (в него мы будем сохранять id предмета)
Для начала изменим тип поля у операций на выпадающий список. Для этого переходим в файл, ответственный за построение окон — assets/components/customextra/js/mgr/widgets/operations.windows.js, находим часть, которая перебирает числовые поля и добавляем условие, чтобы конкретное поле id1 имело особый xtype:
Добавить условие надо в 2 места, так как у нас есть окно создания операции и окно редактирования операции. Но, если по задумке так надо, эти окна могут быть разными. Например, из окна редактирования операции это поле можно вообще убрать, тогда не получится случайно изменить связь с предметом.
Теперь, в конце файла добавляем такой код (тут создаётся особый тип поля, который будет получать предметы и подставлять их в выпадающий список):
Теперь, если вы обновите страницу CustomExtra (Ctrl + R), окно создания операций будет выглядеть так:
Однако в самой табличке будет отображаться id, а не называние предмета.
За формирование списка операций отвечает процессор core/components/customextra/processors/mgr/operation/getlist.class.php
Добавим в него выборку предметов и подстановку в ячейку нужного значения:
И в файле, ответственном за построение таблицы, укажем, что показывать надо имя, а не id
Теперь и в табличке показывается название предмета
Чтобы вывести табличку нужно добавить такой код в метод окна редактирования (перед return):
Табличка выводится, но она выводится вся, а нам нужен список только связанных операций. Для этого добавим 2 строчки в файл, отвечающий за табличку операций assets/components/customextra/js/mgr/widgets/operations.grid.js
А в процессор добавим выборку по id предмета:
Та-дам!
Операции можно создавать, редактировать, удалять. Даже поиск работает. Но есть неудобство — при клике на кнопку «Создать операцию», поле «Предмет» будет пустым. Хочется, чтобы там уже был указан текущий предмет. Для этого добавим id в список устанавливаемых значений в файле operations.grid.js:
Вот теперь всё удобно и красиво
Итак, чтобы добавить свой combobox к полю, нужно:
Для начала представим гипотетическую ситуацию, в которой у нас на сайте есть некие «Предметы» и с каждым предметом можно проводить некие «Операции». Попробуем связать Предметы и Операции.
После установки CustomExtra включим 2 вкладки — у предметов 0 числовых, строковых и текстовых полей, а у операций — 1 дополнительное числовое поле (в него мы будем сохранять id предмета)
Добавляем ComboBox
Для начала изменим тип поля у операций на выпадающий список. Для этого переходим в файл, ответственный за построение окон — assets/components/customextra/js/mgr/widgets/operations.windows.js, находим часть, которая перебирает числовые поля и добавляем условие, чтобы конкретное поле id1 имело особый xtype:
...
,items: [{
// У первого поля особый тип
xtype: i == 1 ? 'customextra-combo-items' : 'textfield',
fieldLabel: _('customextra_operation_id' + i),
name: 'id' + i,
id: config.id + '-id' + i,
anchor: '99%',
allowBlank: true,
}]
...
Добавить условие надо в 2 места, так как у нас есть окно создания операции и окно редактирования операции. Но, если по задумке так надо, эти окна могут быть разными. Например, из окна редактирования операции это поле можно вообще убрать, тогда не получится случайно изменить связь с предметом.
Теперь, в конце файла добавляем такой код (тут создаётся особый тип поля, который будет получать предметы и подставлять их в выпадающий список):
customExtra.combo.items = function(config) {
config = config || {};
Ext.applyIf(config,{
name: 'id1'
,fieldLabel: 'Предмет'
,hiddenName: 'id1'
,displayField: 'name'
,valueField: 'id'
,anchor: '99%'
,fields: ['id','name']
,pageSize: 20
// Если будете использовать не в CustomExtra, не забудьте проверить правильность
// написание параметра connector_url - у вас он может отличатсья.
,url: customExtra.config.connector_url
,editable: true
,allowBlank: true
,emptyText: 'Выберите Предмет'
,baseParams: {
action: 'mgr/item/getlist'
,combo: 1
}
,tpl: new Ext.XTemplate(
'<tpl for=".">\
<div class="x-combo-list-item">\
<strong>{name}</strong> <sup>({id})</sup>\
</div>\
</tpl>'
,{compiled: true}
)
});
customExtra.combo.items.superclass.constructor.call(this,config);
};
Ext.extend(customExtra.combo.items,MODx.combo.ComboBox);
Ext.reg('customextra-combo-items',customExtra.combo.items);
Теперь, если вы обновите страницу CustomExtra (Ctrl + R), окно создания операций будет выглядеть так:
Однако в самой табличке будет отображаться id, а не называние предмета.
За формирование списка операций отвечает процессор core/components/customextra/processors/mgr/operation/getlist.class.php
Добавим в него выборку предметов и подстановку в ячейку нужного значения:
public function prepareQueryBeforeCount(xPDOQuery $c) {
// Добавляем в выборку предметы
$c->leftJoin('customExtraItem', 'customExtraItem', '`customExtraItem`.`id` = `'.$this->classKey.'`.`id1`');
$c->select($this->modx->getSelectColumns($this->classKey, $this->classKey));
$c->select('`customExtraItem`.`name` as `item_name`');
$c->groupby($this->classKey . '.id');
// конец
$query = trim($this->getProperty('query'));
if ($query) {
$c->where(array(
'name:LIKE' => "%{$query}%",
'OR:description:LIKE' => "%{$query}%",
));
}
return $c;
}
И в файле, ответственном за построение таблицы, укажем, что показывать надо имя, а не id
getFields: function (config) {
// Добавляем поле item_name в список
var fields = ['id', 'name', 'description', 'item_name', 'active', 'actions', 'deleted', 'published', 'paid', 'new', 'hit', 'favorite'];
//...
},
getColumns: function (config) {
// ...
// А в колонке вместо первого дополнительного id показываем имя
for(var i = 1; i < ids; i++) {
columns.push({
header: _('customextra_operation_id' + i),
dataIndex: i == 1 ? 'item_name' : 'id' + i,
sortable: true,
width: partWidth,
});
}
// ...
},
Теперь и в табличке показывается название предмета
Выводим вложенную табличку в окне
Чтобы вывести табличку нужно добавить такой код в метод окна редактирования (перед return):
Ext.extend(customExtra.window.UpdateItem, MODx.Window, {
getFields: function (config) {
// ...
fields.push({
xtype: 'customextra-grid-operations',
fieldLabel: _('customextra_operations'),
id: config.id + '-operations',
record: config.record,
anchor: '99%'
});
return fields;
},
// ...
});
Табличка выводится, но она выводится вся, а нам нужен список только связанных операций. Для этого добавим 2 строчки в файл, отвечающий за табличку операций assets/components/customextra/js/mgr/widgets/operations.grid.js
customExtra.grid.Operations = function (config) {
config = config || {};
// Чтобы табличка отдельно работала, создадим нулевой объект
config.record = config.record || {object: {id: 0}}
if (!config.id) {
config.id = 'customextra-grid-operations';
}
Ext.applyIf(config, {
// ...
baseParams: {
action: 'mgr/operation/getlist'
// В процессор будем передавать id предмета
,item_id: config.record.object.id
},
А в процессор добавим выборку по id предмета:
public function prepareQueryBeforeCount(xPDOQuery $c) {
// Добавляем в выборку предметы
$c->leftJoin('customExtraItem', 'customExtraItem', '`customExtraItem`.`id` = `'.$this->classKey.'`.`id1`');
$c->select($this->modx->getSelectColumns($this->classKey, $this->classKey));
$c->select('`customExtraItem`.`name` as `item_name`');
$c->groupby($this->classKey . '.id');
// конец
// Добавляем выборку по id предмета
$item_id = trim($this->getProperty('item_id'));
if ($item_id) {
$c->where(array(
'id1' => $item_id
));
}
// конец
$query = trim($this->getProperty('query'));
if ($query) {
$c->where(array(
'name:LIKE' => "%{$query}%",
'OR:description:LIKE' => "%{$query}%",
));
}
return $c;
}
Та-дам!
Операции можно создавать, редактировать, удалять. Даже поиск работает. Но есть неудобство — при клике на кнопку «Создать операцию», поле «Предмет» будет пустым. Хочется, чтобы там уже был указан текущий предмет. Для этого добавим id в список устанавливаемых значений в файле operations.grid.js:
createOperation: function (btn, e) {
// ...
w.setValues({active: true, published: true, id1: this.record.object.id});
w.show(e.target);
},
Вот теперь всё удобно и красиво
Шпаргалка
Итак, чтобы добавить свой combobox к полю, нужно:
- У нужного поля указать свой тип xtype
- Описать этот xtype в том же файле
- В процессор getlist добавить выборку нужных объектов
- В табличке (grid) у нужной ячейки указать нужное поле для отображения
- Добавить эту таблицу в список полей окна
- В самой таблице добавить передачу нужного id (config.record.object.id)
- В процессор getlist добавить выборку по соответствующему полю
- Для вложенного окна создания указать значение по умолчанию
Комментарии: 10
Спасибо! Очень полезная инструкция.
И в файле, ответственном за построение таблицы, укажем, что показывать надо имя, а не idВы не уточнили, какой именно файл ответственен за построение таблицы. Такие кусочки кода есть в разных файлах customExtra, например в media.grid.js…
- item — Предметы
- order — Заказы
- operation — Операции
- media — Медиа
- link — Ссылки
Каждый файл отвечает за свою вкладку. Для вкладки «Предметов» вносим изменения в items.grid.js
Ок, спасибо! Похоже я тоже его нашел.
Просто есть много похожих файлов, например файлов с названием getlist.class.php есть в каждой из папок (item, link, media, operation, order). Я ещё не совсем понял для чего каждая из этих папок отвечает.
Просто есть много похожих файлов, например файлов с названием getlist.class.php есть в каждой из папок (item, link, media, operation, order). Я ещё не совсем понял для чего каждая из этих папок отвечает.
Каждая папка отвечает за свою вкладку
А, точно, спасибо! Очевидно же! У меня уже глаза замылились, пора развеятся… )))
Спасибо за подсказки!
Спасибо за подсказки!
Мне нужно вывести список ресурсов в combobox.
Описанный же метод в статье выводит список добавленных предметов в customExtra.
Попытался внедриться в getlist.class.php, но не получилось сделать выборку ресурсов.
Создал отдельный файл процессора getproducts.class.php и указал к нему путь в combobox-е, но почему-то combobox упорно не хочет видеть указанный ему файл процессора и обращается к старому.
Вот регистрация комбобокса:
А вот и код самого процессора getproducts.class.php:
Почему combobox не видит файл нового процессора? Или как по-другому подсунуть комбобоксу список ресурсов?
Описанный же метод в статье выводит список добавленных предметов в customExtra.
Попытался внедриться в getlist.class.php, но не получилось сделать выборку ресурсов.
Создал отдельный файл процессора getproducts.class.php и указал к нему путь в combobox-е, но почему-то combobox упорно не хочет видеть указанный ему файл процессора и обращается к старому.
Вот регистрация комбобокса:
customExtra.combo.items = function(config) {
config = config || {};
Ext.applyIf(config,{
name: 'id1'
,fieldLabel: 'Товар'
,hiddenName: 'id1'
,displayField: 'pagetitle'
,valueField: 'id'
,anchor: '99%'
,fields: ['id', 'pagetitle']
,pageSize: 20
,hideMode: 'offsets'
// Если будете использовать не в CustomExtra, не забудьте проверить правильность
// написание параметра connector_url - у вас он может отличатсья.
,url: customExtra.config.connector_url
,editable: true
,allowBlank: false
,emptyText: 'Выберите товар'
,baseParams: {
action: 'mgr/item/getproducts' // ПУТЬ К НОВОМУ ФАЙЛУ ПРОЦЕССОРА
,combo: true
}
/*
,tpl: new Ext.XTemplate(
'<tpl for=".">\
<div class="x-combo-list-item">\
<strong>{resource_pagetitle}</strong> <sup>({resource_id})</sup>\
</div>\
</tpl>'
,{compiled: true}
)
*/
});
customExtra.combo.items.superclass.constructor.call(this,config);
};
Ext.extend(customExtra.combo.items,MODx.combo.ComboBox);
Ext.reg('customextra-combo-items',customExtra.combo.items);
А вот и код самого процессора getproducts.class.php:
<?php
class msResourceGetListProcessor extends modObjectGetListProcessor
{
public $classKey = 'modResource';
public $languageTopics = array('resource');
public $defaultSortField = 'pagetitle';
/**
* @param xPDOQuery $c
*
* @return xPDOQuery
*/
public function prepareQueryBeforeCount(xPDOQuery $c)
{
if ($this->getProperty('combo')) {
$c->select('id,pagetitle');
}
if ($id = (int)$this->getProperty('id')) {
$c->where(array('id' => $id));
}
if ($query = trim($this->getProperty('query'))) {
$c->where(array('pagetitle:LIKE' => "%{$query}%"));
}
return $c;
}
/**
* @param xPDOObject $object
*
* @return array
*/
public function prepareRow(xPDOObject $object)
{
if ($this->getProperty('combo')) {
$array = array(
'id' => $object->get('id'),
'pagetitle' => '(' . $object->get('id') . ') ' . $object->get('pagetitle'),
);
} else {
$array = $object->toArray();
}
return $array;
}
}
return 'msResourceGetListProcessor';
Почему combobox не видит файл нового процессора? Или как по-другому подсунуть комбобоксу список ресурсов?
почему-то combobox упорно не хочет видеть указанный ему файл процессора и обращается к старому.таких чудес не бывает…
— либо ты чтото не там прописываешь…
— либо кеш
— либо твой комбо гдето ниже еще переопределяется
— либо твой комбо гдето ниже еще переопределяетсяБлестяще! Спасибо за наводку! Поменял название комбо и всё заработало!
Кстати, спасибо тебе, Володя, и Василию, за классные компоненты, по коду которых я сейчас и делаю всё это…
можете подсказать, я тоже вывел через ресурсы, только у меня постоянно появляются цифры, какое поле указать тут что выводился pagetitle?
за основу я взял ваш процессор
Спасибо заранее за ответ
за основу я взял ваш процессор
Спасибо заранее за ответ
for(var i = 1; i < ids; i++) {
columns.push({
header: _('customextra_item_id' + i),
dataIndex: i == 1 ? 'pagetitle' : 'id' + i,
sortable: true,
width: partWidth,
});
}
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.