MigxDB - Делаем безграничное хранилище "объектов" в ресурсах.
Всем привет! Наконец добрался до написания статьи по MigxDB!
Итак, предыстория: Была дана задача, реализовать управление квартирами в продаваемых строящихся домах. В каждом доме по 198 квартир, конечно же, обычный конфигуратор MIGX тут не подойдёт с его ограничением в 100 строк и отсутствием пагинации, поэтому, лучший вариант это — MigxDB.

И так! Давайте приступим к реализации!
1. Заходим Пакеты -> MIGX -> Вкладка «Package Manager»
1.1 Пишем в поле Package Name название нашего будущего «компонента», у меня это будет «FlatsTEST», и нажимаем ниже в первой вкладке Package кнопку Create Package
После этих действий, должна появиться папка с названием Вашего «компонента» по пути: core/components/*папка*

1.2. Теперь, переходим в последнюю вкладку Xml Schema и пишем разметку будущих полей в БД и название будущей таблицы… Для написания этой разметки, необходимо понимать какие поля мы собираемся использовать и под что. Ниже пример моей разметки, который можете взять за основу и «подбить» под свою задачу.
Здесь, указываем у тега model в атрибуте package название нашего «компонента».
У тега object в атрибуте class, класс по которому мы потом будем вызывать объекты, в атрибуте table название таблицы где будут храниться объекты, у меня это flats_test
Итак, разобравшись с разметкой или взяв мою и вставив в поле Schema, жмём кнопку Save Schema

1.3. Теперь переходим во вкладку create Tables и жмём единственную кнопку create Tables, таким образом, мы создадим новую таблицу в БД, результат можно увидеть в phpMyAdmin:

2. Теперь, переходим к созданию конфигурации MIGX.
2.1. Заходим Пакеты -> MIGX, жмём кнопку Добавить элемент.
В открывшемся всплывающем окне, в поле name указываем название конфигурации латинскими буквами, у меня будет flatstest, в поле Замена «Добавить элемент» можно указать свой вариант текста кнопки добавления объектов, у меня будет Добавить квартиру.

2.2. Открываем вкладку MIGXdb-Settings, в поля Package и Classname прописываем название «компонента» и класс объектов, что мы задавали в разметке, и в поле Load Grid ставим auto, что бы наши объекты подгружались автоматически при их вызове из БД.
В итоге, жмём Сохранить и закрыть, что бы создать конфигурацию и связь с нашим MigxDB «компонентом»

2.3. Снова открываем редактирование конфигурации и переходим во вкладку Formtabs жмём Добавить элемент, в открывшемся окне в поле Caption пишем название окна добавления объекта, у меня как всегда Добавить квартиру. Дальше жмём Выбрать db-поля, и уже в 3м открывшемся окне в единственном поле, добавляем все поля кроме тех, что будут чекбоксами (управление чекбоксами мы сделаем в таблице объектов дальше) после полей id и resource_id, они заполняются автоматически…

Добавив поля жмём Сохранить и закрыть, возвращаемся во 2е окно где эти поля отобразятся, теперь нужно у всех полей задать в Caption название по русски и в Input TV type тип полей.

2.4. Переходим ко вкладке Columns, здесь действуем по тому же принципу, как и в Formtabs, используем Выбрать db-поля. Поля в таблицу выводим по своему усмотрению и используем Renderer: this.renderImage для полей с изображениями.

2.5. А вот поля-чекбоксы, оформим своеобразно.
У таких полей необходимо во вкладке Renderer в поле Renderer указать this.renderSwitchStatusOptions, в поле on Click выбрать switchOption. Далее в самом низу в Renderoptions добавить 3 опции, пример поля Забронировано где при добавлении объекта это поле изначально в положении выкл.

Если хотите готовый пример конфигурации MIGX, скачайте мой.
3. Осталось дело за малым… Создаём ТВ поле назвав как Вам угодно, указав во вкладке Параметры ввода
На этом всё! Результат всех этих шагов Вы видели в первом скрине этой статьи…
Само собой у Вас возникнут вопросы по данной статье, задавайте их в комментариях, обязательно отвечу!
В целом, постарался «разжевать» всё максимально…
Пример вывода объектов на frontend:
P.S. Как принцип использования MIGX и MigxDB Вам станет понятен, Вы сможете организовывать более сложные структуры, главное разобраться, что за чем тянется!)
Поблагодарить автора, а также немного порадовать других активных участников сообщества можно,
отправив донат сообществу найдя реквизиты на странице О нас
Спасибо за внимание!
Итак, предыстория: Была дана задача, реализовать управление квартирами в продаваемых строящихся домах. В каждом доме по 198 квартир, конечно же, обычный конфигуратор MIGX тут не подойдёт с его ограничением в 100 строк и отсутствием пагинации, поэтому, лучший вариант это — MigxDB.

И так! Давайте приступим к реализации!
1. Заходим Пакеты -> MIGX -> Вкладка «Package Manager»
1.1 Пишем в поле Package Name название нашего будущего «компонента», у меня это будет «FlatsTEST», и нажимаем ниже в первой вкладке Package кнопку Create Package
После этих действий, должна появиться папка с названием Вашего «компонента» по пути: core/components/*папка*

1.2. Теперь, переходим в последнюю вкладку Xml Schema и пишем разметку будущих полей в БД и название будущей таблицы… Для написания этой разметки, необходимо понимать какие поля мы собираемся использовать и под что. Ниже пример моей разметки, который можете взять за основу и «подбить» под свою задачу.
<?xml version="1.0" encoding="UTF-8"?>
<model package="FlatsTEST" baseClass="xPDOObject" platform="mysql" defaultEngine="InnoDB" version="1.1">
<object class="FlatsTEST" table="flats_test" extends="xPDOSimpleObject">
<field key="resource_id" dbtype="int" precision="10" phptype="integer" null="false" index="index" />
<field key="name" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="image" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="fnumber" dbtype="int" precision="12" phptype="integer" null="false" default="0" />
<field key="lsquare" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="nbsquare" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="square" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="type" dbtype="text" phptype="string" null="true" />
<field key="floor" dbtype="text" phptype="string" null="true" />
<field key="rooms" dbtype="text" phptype="string" null="true" />
<field key="sprice" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="price" dbtype="varchar" precision="255" phptype="string" null="false" default="" />
<field key="busy" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
<field key="sold" dbtype="tinyint" precision="1" attributes="unsigned" phptype="integer" null="false" default="0" />
</object>
</model>Здесь, указываем у тега model в атрибуте package название нашего «компонента».
У тега object в атрибуте class, класс по которому мы потом будем вызывать объекты, в атрибуте table название таблицы где будут храниться объекты, у меня это flats_test
Обратите внимание на поле id, которое желательно использовать в объектах, которое будет индексируемым для более быстрой работы…В целом, нужно указывать в разметке поля «под текст», «под числа» или «под чекбокс» (1/0). И что бы разобраться, какое поле как оформить, погуглите «Типы данных в MySQL»…
Так же, здесь есть обязательное поле resource_id которое связывает объекты в БД с ресурсами, в случае если Вы не собираетесь привязывать объекты к ресурсам, а использовать объекты отдельно от них, то поле указывать не надо.
Итак, разобравшись с разметкой или взяв мою и вставив в поле Schema, жмём кнопку Save Schema

1.3. Теперь переходим во вкладку create Tables и жмём единственную кнопку create Tables, таким образом, мы создадим новую таблицу в БД, результат можно увидеть в phpMyAdmin:

2. Теперь, переходим к созданию конфигурации MIGX.
2.1. Заходим Пакеты -> MIGX, жмём кнопку Добавить элемент.
В открывшемся всплывающем окне, в поле name указываем название конфигурации латинскими буквами, у меня будет flatstest, в поле Замена «Добавить элемент» можно указать свой вариант текста кнопки добавления объектов, у меня будет Добавить квартиру.

2.2. Открываем вкладку MIGXdb-Settings, в поля Package и Classname прописываем название «компонента» и класс объектов, что мы задавали в разметке, и в поле Load Grid ставим auto, что бы наши объекты подгружались автоматически при их вызове из БД.
В итоге, жмём Сохранить и закрыть, что бы создать конфигурацию и связь с нашим MigxDB «компонентом»

2.3. Снова открываем редактирование конфигурации и переходим во вкладку Formtabs жмём Добавить элемент, в открывшемся окне в поле Caption пишем название окна добавления объекта, у меня как всегда Добавить квартиру. Дальше жмём Выбрать db-поля, и уже в 3м открывшемся окне в единственном поле, добавляем все поля кроме тех, что будут чекбоксами (управление чекбоксами мы сделаем в таблице объектов дальше) после полей id и resource_id, они заполняются автоматически…

Добавив поля жмём Сохранить и закрыть, возвращаемся во 2е окно где эти поля отобразятся, теперь нужно у всех полей задать в Caption название по русски и в Input TV type тип полей.

2.4. Переходим ко вкладке Columns, здесь действуем по тому же принципу, как и в Formtabs, используем Выбрать db-поля. Поля в таблицу выводим по своему усмотрению и используем Renderer: this.renderImage для полей с изображениями.

ВАЖНО! Что бы изменения сохранялись и отображались, в Columns обязательно должно быть поле ID!
2.5. А вот поля-чекбоксы, оформим своеобразно.
У таких полей необходимо во вкладке Renderer в поле Renderer указать this.renderSwitchStatusOptions, в поле on Click выбрать switchOption. Далее в самом низу в Renderoptions добавить 3 опции, пример поля Забронировано где при добавлении объекта это поле изначально в положении выкл.
- 1 опция
Name: notcommerce
Use as fallback for all undefined values: *checked*
on Click: switchOption
Image: assets/components/migx/style/images/cb_empty.png - 2 опция
Name: yescommerce
Use as fallback for all undefined values: не отмечаем
on Click: switchOption
Image: assets/components/migx/style/images/cb_ticked.png - 3 опция
Name: notcommerce
Use as fallback for all undefined values: не отмечаем
on Click: switchOption
Image: assets/components/migx/style/images/cb_empty.png

Если хотите готовый пример конфигурации MIGX, скачайте мой.
3. Осталось дело за малым… Создаём ТВ поле назвав как Вам угодно, указав во вкладке Параметры ввода
- Тип вода: migxdb
- Конфигурации: название конфигурации MIGX (у меня flatstest)
На этом всё! Результат всех этих шагов Вы видели в первом скрине этой статьи…
Само собой у Вас возникнут вопросы по данной статье, задавайте их в комментариях, обязательно отвечу!
В целом, постарался «разжевать» всё максимально…
Пример вывода объектов на frontend:
[[!migxLoopCollection?
&classname=`FlatsTEST`
&packageName=`FlatsTEST`
&where=`{"resource_id":"ID ресурса откуда выводить объекты"}`
&sortConfig=`[{"sortby":"id","sortdir":"DESC"}]`
&tpl=`flatsTableItem`
]]P.S. Как принцип использования MIGX и MigxDB Вам станет понятен, Вы сможете организовывать более сложные структуры, главное разобраться, что за чем тянется!)
Поблагодарить автора, а также немного порадовать других активных участников сообщества можно,
отправив донат сообществу найдя реквизиты на странице О нас
Спасибо за внимание!
Комментарии: 11
На удивление первый раз за всю карьеру вижу пример работы с migxDB (сам не пользуюсь, есть modExtra для этого)
А всё потому что, в поиске по MigxDB находятся всего несколько внятных статей но и те, уже не совсем актуальны… Да и кейс подвернулся подходящий)
Эхх… А когда-то я начинал с заметок по MIGX :|
А про MigxDB забыл) Мало информации по нему на modx.pro (
Хм, твой комментарий наводит меня на мысли «А, что я буду делать через пару лет? Тоже работать над docs.modx.pro/modx.pro/modhost.pro?», я ведь уже не слабо так «прирос» к нашему сообществу, и останавливаться не собираюсь)
Молодежь поджимает.
Можно, кстати, и не создавать ТВ, а вывести сразу пункт меню. А там уже по фильтру получать квартиры и их модификации и т.д.
Тогда привязку к ресурсам нужно будет руками указывать и теряется смысл поля resource_id
Да, об этом я косвенно упомянул в blockquote, в пункте 1.2)) Если не привязываться к ресурсам, то действительно, можно в отдельный пункт меню вынести управление объектами, такой вариант подходит например к «хранилищу документов» сайта. В общем, к чему то, что может быть общим для всех ресурсов…
Добрый день! Спасибо за описание)
Еще стоит упомянуть про вкладку Actionbuttons (в настройке MIGX-конфигурации) — тут надо отметить галочку additem — это кнопки которыми можно управлять записями в таблице.
Еще стоит упомянуть про вкладку Actionbuttons (в настройке MIGX-конфигурации) — тут надо отметить галочку additem — это кнопки которыми можно управлять записями в таблице.
код для генерации схем MIGXdb
может кому пригодится или доработается
может кому пригодится или доработается
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>XML Generator for MIGX (Table View)</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
color: #333;
}
.config-group {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 6px;
background-color: #f9f9f9;
}
.config-group label {
display: block;
margin: 8px 0 4px;
font-weight: bold;
font-size: 0.9em;
}
.config-group input {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.mjg_input_rows {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.mjg_input_rows th {
background-color: #f2f2f2;
text-align: left;
padding: 12px;
font-weight: bold;
border: 1px solid #ddd;
}
.mjg_input_rows td {
padding: 10px;
border: 1px solid #ddd;
vertical-align: top;
}
.mjg_input_field input,
.mjg_input_caption input,
.mjg_input_inputTVtype select {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.mjg_input_showintabs label,
.mjg_input_sortable label {
display: flex;
align-items: center;
gap: 5px;
}
.mjg_input_deleterow_action {
color: #dc3545;
cursor: pointer;
font-weight: bold;
}
.mjg_input_rows_add_wr {
margin: 15px 0;
}
.mjg_input_rows_add {
display: inline-block;
background: #28a745;
color: white;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
font-size: 0.9em;
}
.mjg_input_rows_add:hover {
background: #218838;
}
textarea {
width: 100%;
height: 300px;
font-family: monospace;
padding: 12px;
border: 1px solid #ccc;
border-radius: 4px;
resize: vertical;
box-sizing: border-box;
margin-top: 10px;
font-size: 0.95em;
}
h1 {
color: #2c3e50;
margin-bottom: 20px;
}
/* Стили для выпадающего списка опций (скрыт по умолчанию) */
.mjg_input_inputTVtype_inputOptionValues {
margin-top: 8px;
display: none;
}
.mjg_input_inputTVtype_inputOptionValues_item {
display: flex;
gap: 8px;
margin-bottom: 5px;
}
.mjg_input_inputTVtype_inputOptionValues_item input {
flex: 1;
padding: 6px;
}
.mjg_input_inputTVtype_inputOptionValues_item_delete {
color: #dc3545;
cursor: pointer;
font-weight: bold;
}
.mjg_input_inputTVtype_inputOptionValues_add {
color: #007bff;
cursor: pointer;
font-size: 0.85em;
margin-top: 5px;
}
</style>
</head>
<body>
<h1>Генератор XML для MIGX/MODX (Таблица)</h1>
<!-- Основные настройки модели -->
<div class="config-group">
<label for="modelPackage">Package (model package)</label>
<input type="text" id="modelPackage" value="mygallery" placeholder="Например: mygallery">
<label for="objectClass">Class (object class)</label>
<input type="text" id="objectClass" value="myGallery" placeholder="Например: myGallery">
<label for="tableName">Table (table)</label>
<input type="text" id="tableName" value="migx_gallery" placeholder="Например: migx_gallery">
</div>
<div class="mjg_input_rows">
<table>
<thead>
<tr>
<th>Ключ (key)
<span style="font-size:12px;color:#888;">Например: title</span></th>
<th>Тип БД (dbtype)
<span style="font-size:12px;color:#888;">varchar, int и др.</span></th>
<th>Точность (precision)
<span style="font-size:12px;color:#888;">Для varchar/int</span></th>
<th>PHP-тип (phptype)
<span style="font-size:12px;color:#888;">string, integer и др.</span></th>
<th>NULL?</th>
<th>Значение по умолчанию</th>
<th>Индекс</th>
<th></th>
</tr>
</thead>
<tbody id="fieldsContainer">
<!-- Первая строка по умолчанию -->
<tr class="mjg_input_row" id="field_1">
<td class="mjg_input_field">
<input type="text" name="key" placeholder="title" required>
</td>
<td class="mjg_input_dbtype">
<select name="dbtype" onchange="updatePhpType(this)">
<option value="varchar">varchar</option>
<option value="text">text</option>
<option value="int">int</option>
<option value="datetime">datetime</option>
<option value="tinyint">tinyint</option>
<option value="json">json</option>
</select>
</td>
<td class="mjg_input_precision">
<input type="text" name="precision" placeholder="255">
</td>
<td class="mjg_input_phptype">
<select name="phptype" required>
<option value="string">string</option>
<option value="integer">integer</option>
<option value="datetime">datetime</option>
<option value="json">json</option>
</select>
</td>
<td class="mjg_input_null">
<label><input type="checkbox" name="null" checked> Да</label>
</td>
<td class="mjg_input_default">
<input type="text" name="default" placeholder="0">
</td>
<td class="mjg_input_index">
<select name="index">
<option value="">—</option>
<option value="index">index</option>
<option value="fulltext">fulltext</option>
<option value="unique">unique</option>
</select>
</td>
<td class="mjg_input_deleterow">
<span class="mjg_input_deleterow_action" onclick="removeField('field_1')">X</span>
</td>
</tr>
</tbody>
</table>
<div class="mjg_input_rows_add_wr">
<div class="mjg_input_rows_add" onclick="addField()">Добавить строку</div>
</div>
</div>
<!-- Кнопка генерации XML -->
<div style="margin: 20px 0;">
<button type="button" onclick="generateXML()" style="
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-size: 1em;
">Сгенерировать XML</button>
</div>
<h2>Результат (XML)</h2>
<textarea id="output" placeholder="Здесь появится сгенерированный XML..." readonly></textarea>
<script>
let fieldCount = 1; // Начинаем с 1, т.к. первая строка уже есть
function addField() {
fieldCount++;
const container = document.getElementById('fieldsContainer');
const newRow = document.createElement('tr');
newRow.className = 'mjg_input_row';
newRow.id = `field_${fieldCount}`;
newRow.innerHTML = `
<td class="mjg_input_field">
<input type="text" name="key" placeholder="title" required>
</td>
<td class="mjg_input_dbtype">
<select name="dbtype" onchange="updatePhpType(this)">
<option value="varchar">varchar</option>
<option value="text">text</option>
<option value="int">int</option>
<option value="datetime">datetime</option>
<option value="tinyint">tinyint</option>
<option value="json">json</option>
</select>
</td>
<td class="mjg_input_precision">
<input type="text" name="precision" placeholder="255">
</td>
<td class="mjg_input_phptype">
<select name="phptype" required>
<option value="string">string</option>
<option value="integer">integer</option>
<option value="datetime">datetime</option>
<option value="json">json</option>
</select>
</td>
<td class="mjg_input_null">
<label><input type="checkbox" name="null" checked> Да</label>
</td>
<td class="mjg_input_default">
<input type="text" name="default" placeholder="0">
</td>
<td class="mjg_input_index">
<select name="index">
<option value="">—</option>
<option value="index">index</option>
<option value="fulltext">fulltext</option>
<option value="unique">unique</option>
</select>
</td>
<td class="mjg_input_deleterow">
<span class="mjg_input_deleterow_action" onclick="removeField('field_${fieldCount}')">X</span>
</td>`;
container.appendChild(newRow);
}
function removeField(id) {
const el = document.getElementById(id);
if (el) el.remove();
}
function updatePhpType(selectEl) {
const dbtype = selectEl.value;
const row = selectEl.closest('tr');
const phptypeSelect = row.querySelector('select[name="phptype"]');
let suggestedType = 'string';
switch (dbtype) {
case 'int':
case 'tinyint':
suggestedType = 'integer';
break;
case 'datetime':
suggestedType = 'datetime';
break;
case 'json':
suggestedType = 'json';
break;
}
phptypeSelect.value = suggestedType;
}
function escapeXml(str) {
if (typeof str !== 'string') return '';
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function generateXML() {
// Получаем основные параметры
const modelPackage = escapeXml(document.getElementById('modelPackage').value.trim() || 'mytable');
const objectClass = escapeXml(document.getElementById('objectClass').value.trim() || 'myTable');
const tableName = escapeXml(document.getElementById('tableName').value.trim() || 'migx_table');
const rows = document.querySelectorAll('#fieldsContainer tr.mjg_input_row');
const xmlLines = [];
// Заголовок XML
xmlLines.push('<?xml version="1.0" encoding="UTF-8"?>');
xmlLines.push(`<model package="${modelPackage}" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.1">`);
xmlLines.push(` <object class="${objectClass}" table="${tableName}" extends="xPDOSimpleObject">`);
// Обработка каждой строки таблицы
rows.forEach(row => {
const keyInput = row.querySelector('input[name="key"]');
const key = keyInput ? keyInput.value.trim() : '';
// Пропускаем строку, если ключ не указан
if (!key) {
console.warn('Пропущена строка без ключа:', row);
return;
}
let fieldXml = ` <field key="${escapeXml(key)}"`;
const dbtype = row.querySelector('select[name="dbtype"]').value;
if (dbtype) fieldXml += ` dbtype="${escapeXml(dbtype)}"`;
const precision = row.querySelector('input[name="precision"]').value.trim();
if (precision) fieldXml += ` precision="${escapeXml(precision)}"`;
const phptype = row.querySelector('select[name="phptype"]').value;
if (phptype) fieldXml += ` phptype="${escapeXml(phptype)}"`;
const nullCheckbox = row.querySelector('input[name="null"]');
const nullVal = nullCheckbox.checked ? 'true' : 'false';
fieldXml += ` null="${nullVal}"`;
const defaultVal = row.querySelector('input[name="default"]').value.trim();
if (defaultVal) fieldXml += ` default="${escapeXml(defaultVal)}"`;
const indexVal = row.querySelector('select[name="index"]').value;
if (indexVal) fieldXml += ` index="${escapeXml(indexVal)}"`;
fieldXml += ' />';
xmlLines.push(fieldXml);
});
// Добавляем стандартные агрегации
xmlLines.push(' <aggregate alias="Resource" class="modResource" local="resource_id" foreign="id" cardinality="one" owner="foreign" />');
xmlLines.push(' <aggregate alias="Creator" class="modUser" local="createdby" foreign="id" cardinality="one" owner="foreign" />');
// Закрывающие теги
xmlLines.push(' </object>');
xmlLines.push('</model>');
// Выводим результат в текстовое поле
document.getElementById('output').value = xmlLines.join('\n');
}
// Инициализация: добавляем обработчик для кнопки генерации
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('.mjg_input_rows_add').addEventListener('click', addField);
});
</script>
</body>
</html>
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.