Разработка кастомного пакета (Extra) MODX
Extra MODX — полноценное расширение: компонент (интерфейс в менеджере), набор сниппетов, плагинов, чанков, системных настроек. Упаковывается в .transport.zip для установки через Package Manager. Это правильный способ распространять и поддерживать сложную функциональность.
Структура Extra
core/components/myextra/
├── controllers/
│ └── home.class.php # MODX контроллер
├── docs/
│ └── changelog.txt
├── elements/
│ ├── chunks/
│ ├── plugins/
│ ├── snippets/
│ └── templates/
├── lexicon/
│ └── ru/
│ └── default.inc.php # Переводы
├── model/
│ └── myextra/ # xPDO модели
│ ├── myitem.class.php
│ ├── mysql/
│ │ ├── myitem.class.php
│ │ └── myitem.map.inc.php
│ └── myextra.mysql.schema.xml
└── processors/
└── mgr/
└── items/
├── getlist.class.php
├── get.class.php
├── create.class.php
├── update.class.php
└── remove.class.php
assets/components/myextra/
├── js/
│ ├── mgr/
│ │ └── myextra.js # ExtJS интерфейс
│ └── widgets/
│ └── items.grid.js
└── css/
└── mgr.css
xPDO схема БД
<!-- core/components/myextra/model/myextra/mysql/myextra.mysql.schema.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<model package="myextra" baseClass="xPDOObject" platform="mysql" version="1.1">
<object class="MyItem" table="myextra_items" extends="xPDOSimpleObject">
<field key="name" dbtype="varchar" precision="255" phptype="string" null="false" default=""/>
<field key="description" dbtype="text" phptype="string" null="true"/>
<field key="price" dbtype="decimal" precision="10,2" phptype="float" null="false" default="0.00"/>
<field key="status" dbtype="tinyint" precision="1" phptype="integer" null="false" default="1"/>
<field key="created_at" dbtype="datetime" phptype="datetime" null="true"/>
<index alias="name" name="name" primary="false" unique="false" type="BTREE">
<column key="name" length="" collation="A" null="false"/>
</index>
</object>
</model>
xPDO модель
// core/components/myextra/model/myextra/myitem.class.php
class MyItem extends xPDOSimpleObject {
public function getLink(): string {
return $this->xpdo->makeUrl(
$this->xpdo->getOption('myextra.item_page', null, 1),
'',
['id' => $this->get('id')],
'full'
);
}
}
Процессор (CRUD операции)
// core/components/myextra/processors/mgr/items/getlist.class.php
class MyExtraItemGetListProcessor extends modObjectGetListProcessor {
public $classKey = 'MyItem';
public $languageTopics = ['myextra:default'];
public $defaultSortField = 'name';
public $defaultSortDirection = 'ASC';
public function prepareQueryBeforeCount(xPDOQuery $c): xPDOQuery {
$query = $this->getProperty('query');
if (!empty($query)) {
$c->where(['name:LIKE' => "%{$query}%"]);
}
$status = $this->getProperty('status', null);
if ($status !== null && $status !== '') {
$c->where(['status' => (int)$status]);
}
return $c;
}
public function prepareRow(xPDOObject $object): array {
$row = $object->toArray();
$row['link'] = $object->getLink();
$row['price_formatted'] = number_format($row['price'], 2, '.', ' ') . ' ₽';
return $row;
}
}
Сниппет Extra
<?php
// core/components/myextra/elements/snippets/myextra.snippet.php
$basePath = $modx->getOption('myextra.core_path', null,
$modx->getOption('core_path') . 'components/myextra/');
$modx->addPackage('myextra', $basePath . 'model/');
$modx->lexicon->load('myextra:default');
$limit = (int)($scriptProperties['limit'] ?? 10);
$offset = (int)($scriptProperties['offset'] ?? 0);
$tpl = $scriptProperties['tpl'] ?? 'myextra.item';
$c = $modx->newQuery('MyItem');
$c->where(['status' => 1]);
$c->limit($limit, $offset);
$c->sortby('name', 'ASC');
$items = $modx->getCollection('MyItem', $c);
$output = '';
foreach ($items as $item) {
$output .= $modx->getChunk($tpl, $item->toArray());
}
return $output;
Сборка Transport Package
// _build/build.transport.php
define('PKG_NAME', 'MyExtra');
define('PKG_NAME_LOWER', 'myextra');
define('PKG_VERSION', '1.0.0');
define('PKG_RELEASE', 'pl');
// Создание пакета
$builder = new modPackageBuilder($modx);
$builder->createPackage(PKG_NAME_LOWER, PKG_VERSION, PKG_RELEASE);
$builder->registerNamespace(PKG_NAME_LOWER, false, true, '{core_path}components/' . PKG_NAME_LOWER . '/');
// Добавить категорию с элементами
$category = $modx->newObject('modCategory');
$category->set('id', 1);
$category->set('category', PKG_NAME);
// Добавить сниппеты, чанки, плагины...
$snippets = includeSnippets($builder, $modx);
$category->addMany($snippets);
// Создать vehicle
$attr = [
xPDOTransport::PRESERVE_KEYS => false,
xPDOTransport::UPDATE_OBJECT => true,
xPDOTransport::UNIQUE_KEY => 'category',
xPDOTransport::RELATED_OBJECTS => true,
xPDOTransport::RELATED_OBJECT_ATTRIBUTES => ['Snippets' => [...], 'Chunks' => [...]],
];
$vehicle = $builder->createVehicle($category, $attr);
$builder->putVehicle($vehicle);
// Финальная упаковка
$builder->pack();
Сроки
Разработка Extra с CRUD интерфейсом в менеджере, 2–3 сниппетами и базовой схемой БД — 2–3 недели.







