Расширение любых таблиц MODX
В очередной раз понадобилось изменить таблицу сайта, менять которую не предусмотрено.
Что обычно люди делают в таких случаях? Верно, редактируют ядро или нужный компонент, и больше их не обновляют. Но ведь это неправильно, и можно решить вопрос иначе.
Пишем обычный плагин, выставляем для него событие OnMODXInit и меняем модель для нужных классов системы или дополнений. Например, я добавил id менеджера в заказ miniShop2:
Больше никаких проблем с обновлениями.
В комментариях возникли вопросы, а что со скоростью?
Отвечаю, этот код отрабатывает примерно за 0.00048 сек. Прикиньте, сколько нужно инициализировать новых классов и добавить полей, чтобы заметить тормоза.
Что обычно люди делают в таких случаях? Верно, редактируют ядро или нужный компонент, и больше их не обновляют. Но ведь это неправильно, и можно решить вопрос иначе.
Пишем обычный плагин, выставляем для него событие OnMODXInit и меняем модель для нужных классов системы или дополнений. Например, я добавил id менеджера в заказ miniShop2:
<?php
switch ($modx->event->name) {
case 'OnMODXInit':
$modx->loadClass('msOrder');
$modx->map['msOrder']['fields']['manager_id'] = 0;
$modx->map['msOrder']['fieldMeta']['manager_id'] = array(
'dbtype' => 'int',
'precision' => 10,
'attributes' => 'unsigned',
'phptype' => 'integer',
'null' => true,
'default' => 0,
);
break;
}
Остаётся только физически добавить свои колонки в таблицы базу данных, и MODX работает с ними, как с родными. Сохраняет, выбирает, фильтрует — всё как положено.Больше никаких проблем с обновлениями.
В комментариях возникли вопросы, а что со скоростью?
Отвечаю, этот код отрабатывает примерно за 0.00048 сек. Прикиньте, сколько нужно инициализировать новых классов и добавить полей, чтобы заметить тормоза.
Комментарии: 60
Василий, я правильно понимаю, что это как-то связано вот с этим — habrahabr.ru/post/253737/?
И что твоим способом можно обойтись без создания своих компонентов ради моделей, как описано в той инструкции от Николая?
И что твоим способом можно обойтись без создания своих компонентов ради моделей, как описано в той инструкции от Николая?
Наверное, как-то связано, но реализации через простейший плагин я нигде не видел.
На инновацию не претендую, может я опять изобрёл велосипед.
На инновацию не претендую, может я опять изобрёл велосипед.
Не знаю, как у других, у меня и такого велосипеда не было. В закладки.
Это гораздо ближе вот к этому (на коммунити этот пост был написан еще в 2013-ом году).
А разве нельзя будет с помощью способа в этом плагине обойтись без создания компонента ради модели, действуя по твоей инструкции?
Можно. Просто я плагины недолюбливаю. В них отладка хромает. Метадата в компоненте надежней. Вот еще советую к ознакомлению статью по теме.
А плагин должен постоянно висеть?
Конечно, в этом и фокус. При каждой загрузке системы изменяется модель в её памяти.
Если плагин отключить, то, как-бы, ничего и не расширялось.
Если плагин отключить, то, как-бы, ничего и не расширялось.
Если будет много добавленных полей не будет грузить сервер?
Чем? Добавлением значений в массив?
Нет, не будет.
Нет, не будет.
Удобно, спасибо за пост!
Просто мне всегда казалось, что плагины это не самое лучшее решение для многих задач.
Просто мне всегда казалось, что плагины это не самое лучшее решение для многих задач.
Получше многих.
Это просто PHP скрипт, который вызывается из другого PHP скрипта. В частности OnMODXInit вызывается из основного класса MODX в момент инициализации контекста.
Это просто PHP скрипт, который вызывается из другого PHP скрипта. В частности OnMODXInit вызывается из основного класса MODX в момент инициализации контекста.
Если не злоупотреблять, то грузить не будет.
Как понять «злоупотреблять»? Не совсем понимаю, сколько надо подгрузить loadClass и прописать дополнительных колонок, чтобы начало грузить систему?
Экспериментальным путем определеяется. Все же зависит от производительности сервера, нагрузки и тд. Дополнить массив — операция не сильно дорогая, но при большой нагрузке это булет происходить при кажлом запросе. Так что на нагруженных серверах нужно собирать статистику и метрики по расходу памяти и затраченном времени. Ну и возможно на посещаемом сайте придется другой метод использовать, но это уже задачи из раздела «Оптимизация».
Спасибо. Я так понимаю, на посещаемых сайтах решение, которое предоставил Николай в своей статье подойдёт больше?
Возможно.
В теории, наверное, да.
А на практике в системе и так уже грузятся десятки классов и всё это занимает миллисекунды. Те же классы miniShop2 тоже грузятся, но чуть позже, поэтому нужно вызвать loadClass здесь вручную.
Работа с массивом вообще, считай, ничего не стоит.
Я не могу придумать, как можно по-настоящему затормозить систему таким плагином. Добавить 10 000 полей, разве что, и то не факт.
А на практике в системе и так уже грузятся десятки классов и всё это занимает миллисекунды. Те же классы miniShop2 тоже грузятся, но чуть позже, поэтому нужно вызвать loadClass здесь вручную.
Работа с массивом вообще, считай, ничего не стоит.
Я не могу придумать, как можно по-настоящему затормозить систему таким плагином. Добавить 10 000 полей, разве что, и то не факт.
Если злоупотреблять, то тоже не будет.
У меня этот код отрабатывает за 0.00048 сек. Добавил в заметку.
У меня этот код отрабатывает за 0.00048 сек. Добавил в заметку.
Василий, можно ли этим плагином добавить в msProduct какой либо параметр товар и дальше с ним работать не используя плагины miniShop?
На первый взгляд это попроще будет чем плагины писать
На первый взгляд это попроще будет чем плагины писать
Можно, суть ровно та же.
Только плагины miniShop2 позволяют еще и в саму админку всякое добавлять.
Только плагины miniShop2 позволяют еще и в саму админку всякое добавлять.
Можно то можно, я попробовал и понял для себя что в админке без понимания работы extJs никуда
С помощью плагинов расширяется только вкладка «Свойства товара», так ведь?
Кстати, если кто будет экспериментировать на старых сайтах, имейте виду, что событие OnMODXInit появилось только в MODX-2.3.0. На более ранних версиях это не сработает.
А есть какой-то вариант расширения класса, к примеру tickets, чтобы переписать пару функций, и класс уже грузился с переписанным функционалом?
Только если разработчиком предусмотрена такая возможность. Насколько помню, основной класс в Tickets штатно переопределить нельзя.
Обойти это Вы можете следующим способом:
1. Сделать расширяющий класс
2. Сделать копию необходимых сниппетом от Tickets
3. В копиях сниппетов изменить способ инициализации основного сервиса, чтобы подгружался расширенный класс.
Если воспользуетесь поиском, найдете подробную статью Василия с описанием этого метода.
Обойти это Вы можете следующим способом:
1. Сделать расширяющий класс
2. Сделать копию необходимых сниппетом от Tickets
3. В копиях сниппетов изменить способ инициализации основного сервиса, чтобы подгружался расширенный класс.
Если воспользуетесь поиском, найдете подробную статью Василия с описанием этого метода.
О. Спасибо. Упустил видимо заметку Василия на эту тему. Не помните, как называлась?
Увы. Запомнил только механизм из нее, как более ценное )
Наверное, речь про Как не хакать сторонние классы.
Она самая. Спасибо!
Супер. Спасибо)
Обнаружил проблему. Создал простенький плагин и одну дополнительную колонку в БД. Всё работает, но при сохранении ресурсов процесс сохранения подвисает на долгое время. Отключаю плагин — всё ок.
Вот код плагина:
Вот колонка в базе:
Никто не сталкивался с этим, в чём может быть проблема?
Вот код плагина:
<?php
switch ($modx->event->name) {
case 'OnMODXInit':
$modx->loadClass('modResource');
$modx->map['modResource']['fields']['old_content'] = '';
$modx->map['modResource']['fieldMeta']['old_content'] = array(
'dbtype' => 'mediumtext',
'phptype' => 'string',
'null' => true,
'default' => '',
);
break;
}
Вот колонка в базе:
Никто не сталкивался с этим, в чём может быть проблема?
$modx->loadClass('modResource');
Удалите
Удалил, но проблема осталась...(
а как именно вы туда пишете покажите
А не смущает что данный комментарий 5 лет назад написан?
Да, я давно уже разобрался)) Сейчас легко расширяю что только можно, в том числе в админке) Но в этом вопросе не знаю что не так, на скринах вроде всё верно.
Для начала надо смотреть логи и консоль браузера. Код валидный. Видимо где-то есть проблема в обработке.
Вот консоль:
Дословно:
причём если отключить плагин, то этой записи уже не будет. А в логах пусто…
Дословно:
XSS Auditor отказался выполнить сценарий в 'http://good4live.ru/connectors/index.php', потому что его исходный код был найден в запросе. Аудитор был включен в качестве сервера послал ни в «X-XSS-защиты», ни заголовка «Content-Security-Policy».
причём если отключить плагин, то этой записи уже не будет. А в логах пусто…
up!)
Подскажите, пожалуйста, что делаю не так:
1. Создал плагин точь-в-точь как в этом посте. Повесил на событие OnMODXInit.
2. Добавил в таблицу modx_ms2_orders столбец manager_id joxi.ru/52azjXgu4goPyA
3. В стандартном сниппете msOrder добавил
1. Создал плагин точь-в-точь как в этом посте. Повесил на событие OnMODXInit.
2. Добавил в таблицу modx_ms2_orders столбец manager_id joxi.ru/52azjXgu4goPyA
3. В стандартном сниппете msOrder добавил
$miniShop2->order->add('manager_id', 4);
Ни на что не ругается, заказ создается, но в базу записывается 0 всегда. Каким образом сохранять значение в этот столбец?
Присоединяюсь! У нас не сохраняются продукты в modx_ms2_products из-за того, что в таблицу в кастомное поле bitrix_id все время пишется 0, а ноль — это дубликат (ведь поле является ключом-индексом), из-за чего продукт просто не вставляется! Хотя в БД это поле по умолчанию NULL. Попробовал расширить модель, не помогло:
$eventName = $modx->event->name;
switch ($eventName) {
case 'OnMODXInit':
// Регистрируем поле bitrix_id в модели продукта
$modx->loadClass('modProduct');
$modx->map['modProduct']['fields']['bitrix_id'] = null;
$modx->map['modProduct']['fieldMeta']['bitrix_id'] = array(
'dbtype' => 'int',
'precision' => 10,
//'attributes' => 'unsigned',
'phptype' => 'integer',
'null' => true,
'default' => null
);
break;
}
Решил! Надо было использовать другой класс и другое событие.
Вот готовый и рабочий вариант:
Вот готовый и рабочий вариант:
<?php
$eventName = $modx->event->name;
switch ($eventName) {
//case 'OnMODXInit':
case 'OnBeforeManagerPageInit':
// Загружаем в модель продукта поле bitrix_id
$modx->loadClass('msProductData');
$modx->map['msProductData']['fields']['bitrix_id'] = null;
$modx->map['msProductData']['fieldMeta']['bitrix_id'] = array(
'dbtype' => 'int',
'precision' => 10,
//'attributes' => 'unsigned',
'phptype' => 'integer',
'null' => true,
//'default' => null,
);
break;
}
Хотя нет! При сохранении некоторых ресурсов такая же фигня!
Я не могу…
Я не могу…
Финальный вариант плагина.
Я бы еще не заполнял к нему описание. Мало ли дело было в нём…
Добавил на всякий случай еще одно событие. Сейчас все работает:
Я бы еще не заполнял к нему описание. Мало ли дело было в нём…
Добавил на всякий случай еще одно событие. Сейчас все работает:
<?php
$eventName = $modx->event->name;
switch ($eventName) {
case 'OnMODXInit':
case 'OnBeforeManagerPageInit':
case 'OnManagerPageInit':
case 'OnWebPageInit':
// Загружаем в модель продукта поле bitrix_id
$modx->loadClass('msProductData');
$modx->map['msProductData']['fields']['bitrix_id'] = null;
$modx->map['msProductData']['fieldMeta']['bitrix_id'] = array(
'dbtype' => 'int',
'precision' => 10,
//'attributes' => 'unsigned',
'phptype' => 'integer',
'null' => true,
'default' => null,
);
break;
}
Добавил, не работает с BannerY. Это только с минишопом работает?
<?php
switch ($modx->event->name) {
case 'OnMODXInit':
$modx->loadClass('byAd');
$modx->map['byAd'] = array(
'fields' =>
array(
'active_bgr',
'active_ukr',
'active_eng',
),
'fieldMeta' =>
array(
'active_bgr' =>
array(
'dbtype' => 'boolean',
'precision' => 1,
'attributes' => 'unsigned',
'phptype' => 'boolean',
'null' => true,
'default' => 0,
),
'active_ukr' =>
array(
'dbtype' => 'boolean',
'precision' => 1,
'attributes' => 'unsigned',
'phptype' => 'boolean',
'null' => true,
'default' => 0,
),
'active_eng' =>
array(
'dbtype' => 'boolean',
'precision' => 1,
'attributes' => 'unsigned',
'phptype' => 'boolean',
'null' => true,
'default' => 0,
),
),
);
break;
}
Пишет:
Could not load class: byAd from mysql.byad.
Хотя в консоли класс этот видит.
попробуй дописать баннеры в настройку extension_packages
А есть ли способ проверить, что модель действительно расширена после этих манипуляций?
У меня создано именно такое поле в таблице заказов, и я прямо из заказа отправляю номер менеджера, но в таблицу всегда попадает ноль.
У меня создано именно такое поле в таблице заказов, и я прямо из заказа отправляю номер менеджера, но в таблицу всегда попадает ноль.
Самое простое — получить объект через getObject(), затем получить массив всех полей через toArray().
Доброго времени суток.
Добавил в таблицу site_content поле import_cid и во внешнем скрипте (modx проинициализирован) пытаюсь получить объект через getObject с условием по этому полю, но выдаёт «Unknown column 'modResource.import_сid' in 'where clause'».
var_dump($modx->map['modResource']); в моём скрипте говорит, что import_сid есть.
Не подскажите в какую сторону копать?
Добавил в таблицу site_content поле import_cid и во внешнем скрипте (modx проинициализирован) пытаюсь получить объект через getObject с условием по этому полю, но выдаёт «Unknown column 'modResource.import_сid' in 'where clause'».
var_dump($modx->map['modResource']); в моём скрипте говорит, что import_сid есть.
Не подскажите в какую сторону копать?
в двойных кавычках писали?
Я из заметки не понял, добавленные поля должны где-то появляться? (имеется в виду окно редактирования ресурса)
Например добавил столбец content2 в БД и поставил плагин:
Например добавил столбец content2 в БД и поставил плагин:
<?php switch ($modx->event->name) { case 'OnMODXInit': $modx->map['modResource']['fields']['content2'] = 0; $modx->map['modResource']['fieldMeta']['content2'] = array( 'dbtype' => 'mediumtext', 'phptype' => 'string', 'index' => 'fulltext', 'indexgrp' => 'content_ft_idx' ); break; }Ничего нигде не поменялось. Нужны еще какие-то манипуляции для добавление полей в форму редактирования ресурса?
Нет, не появится ничего и нигде. Только при прямом доступе к объекту в коде будут доступны эти поля.
В форму редактирования нужно добавлять самостоятельно.
В форму редактирования нужно добавлять самостоятельно.
В админке на каждой странице каждое поле отдельно жестко прописано, с указанием всех свойств (строка, текст, число). Это сделано в js массиве.
Если ты что то новое добавляешь в карту объекта и базу данных (уточню на всякий случай что еще и таблицу базы данных нужно расширять ручками) — то так же ручками нужно добавлять новый дополнительный код в js массив полей.
Обычно делают отдельный плагин, чтобы не вносить правки в исходный код MODX.
Вот пример, прямо из рабочего проекта дернул
Если ты что то новое добавляешь в карту объекта и базу данных (уточню на всякий случай что еще и таблицу базы данных нужно расширять ручками) — то так же ручками нужно добавлять новый дополнительный код в js массив полей.
Обычно делают отдельный плагин, чтобы не вносить правки в исходный код MODX.
Вот пример, прямо из рабочего проекта дернул
switch ($modx->event->name){ case 'OnDocFormPrerender': $modx->controller->addHtml(" <script type='text/javascript'> Ext.ComponentMgr.onAvailable('modx-panel-resource', function(){ if(this.items[1].items[0].id == 'minishop2-product-tab'){ var leftCol = this.items[1].items[0].items[0].items[0].items[0].items[0].items[0].items[0]; }else{ if(this.items[1].items[0].id== 'modx-resource-settings'){ var leftCol = this.items[1].items[0].items[0].items[0]; } if(this.items[1].items[1].id == 'modx-resource-settings'){ var leftCol = this.items[1].items[1].items[0].items[0]; } } var kz_title = { anchor: '100%', description: '<b>[[*kz_title]]</br>Заголовок на казахском</b>', fieldLabel: 'Заголовок на казахском', id: 'modx-resource-kz-title', maxLength:255, msgTarget: 'under', name:'kz_title', xtype:'textfield' } leftCol.items.splice(2, 0, kz_title); var kz_description = { anchor: '100%', description: '<b>[[*kz_description]]</br>Описание на казахском</b>', fieldLabel: 'Описание на казахском', id: 'modx-resource-kz-description', maxLength:255, msgTarget: 'under', name:'kz_description', xtype:'textarea' } leftCol.items.splice(4, 0, kz_description); }); </script>"); break; }На выходе получилось вот так prntscr.com/mnm25m
Здорово!
А я добавил TV поле и при его сохранении плагин записывал значение в БД
А я добавил TV поле и при его сохранении плагин записывал значение в БД
case 'OnDocFormSave': $resource->set('content2', $resource->getTVValue('content2')); $resource->save(); break;Правда, я ожидал, что можно будет делать обычный вызов на странице документа
[[*content2]], но он почему-то возвращает массив
( [0] => content2 [1] => контент 2 [2] => default [3] => [4] => text )Надо попробовать твой вариант!
Это тоже отличная практика. В TV много разных типов, и эту особенность часто удобно использовать.
Что касается проблемы — смотри, у тебя одновременно есть и поле content2 в таблице ресурсов и такой же TV. Конфликт явный. Надо tv переименовать.
Что касается проблемы — смотри, у тебя одновременно есть и поле content2 в таблице ресурсов и такой же TV. Конфликт явный. Надо tv переименовать.
Действительно) А я подумал, что это из-за того, что за основу взял шаблон поля content и начал уже все переделывать. Опыт, штука полезная) Спасибо
Посмотрите вот эти уроки, там хорошо разжёвывается данная тема:
Свои поля в интерфейсе редактирования ресурса MODx
Свои вкладки и поля ресурса в админке MODx
Свои поля в интерфейсе редактирования ресурса MODx
Свои вкладки и поля ресурса в админке MODx
ОМАГАД! Просто я искал эту статью наверное всю жизнь! :D
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.