contentSync - синхронизация контента прода и дева для MODX Revolution

Привет, я снова тут!

Одна из дегенеративных особенностей MODX — хранить все в базе. Кони, люди, контент — все хранится в базе.
Еще более дегенеративная особенность — MODX учит вас хранить все в ресурсах и это не сказать что правильная логика.

Часто возникает ситуация, что тестовая версия сайта начинает плотно отставать по контенту от продакшена и необходимо как-то синхронизировать изменения.

Я придумал решение, которое отчасти решает мою задачу и потребность. Если решение вам понравится — я упакую все в пакет и выложу в бесплатный доступ.
Дисклеймер: Это прототип, грубо говоря proof of concept. Код — не финальный. Да и я — не гений.
> Ниже — буду указаны базовые действия чтобы воспроизвести все руками. Код, который попадет в компонент — скорее всего будет другой.
Суть в том, что все объекты зависимые от modResource можно паковать в JSON объекты и переносить их от сайта к сайту, делая обратное воспроизведение.

Для начала создадим папку для хранения нашего json чуда. Я выбрал core/components/contentSync. Создаем эту папку.

Далее — необходимо сделать так, чтобы менеджеры при изменении ресурсов генерили нам наши объекты. Самый простой способ — создать плагинчик на событие OnDocFormSave
Листинг прилагаю:

<?php
if ($modx->event->name == 'OnDocFormSave'){
    if (is_object($resource)){
        $element = $resource->toJson();
        if (!empty($element)){
            if (file_put_contents(MODX_CORE_PATH.'components/contentSync/'.$resource->get('id').'.json', $element) === FALSE){
                $modx->log(1,'Ошибка'); //Укажите адекватный и понятный для вас код ошибки
                return 0;
            }
        }
    }
}
Собственно — тут все понятно. Делаем проверки, чтобы не попала никакая дичь в нашу папку. Классно, что у xPDO есть методы toJson и fromJson. Самую малость облегчает нам жизнь.

Итак, плагин мы добавили, менеджер шустрит по сайту и создает элементы. Рано или поздно мы обнаружим целый склад файлов json и папке созданной выше.
Переносим в эту же папку на нашем dev сайте.

Далее — необходимо реализовать алгоритм который будет обратно создавать объекты. Учтем что объекты нужно обновлять и создавать. Конечно неплохо бы еще удалять но я распишу ниже почему я не сделал это прямо сейчас.

В качестве реализации и просто создал внешний от MODX файл, аля коннектора. Опять же, решение исключительно для демонстрации. Все же, рекомендую файл запускать позже из-под CLI, ибо если ресурсов сотни\тысячи то лимита сервера на выполнения может не хватить (не всегда можно переопределять данные в скрипте).

Файл sync.php в корне. Его листинг:
<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
header('Content-type: text/html; charset=utf-8');
set_time_limit(0);
ini_set('memory_limit', '4048M');
ini_set('max_execution_time', '0');

define('MODX_API_MODE', true);
require 'index.php';

$modx->getService('error','error.modError');
$modx->setLogLevel(modX::LOG_LEVEL_INFO);

$response = $modx->runProcessor('security/login', array('username' => 's21494', 'password' => 'm9XK60bX9kq1'));
if ($response->isError()) {
    $modx->log(modX::LOG_LEVEL_ERROR, $response->getMessage());
    echo 'Ошибка авторизации';
    die();
}

$modx->initialize('mgr');




$path = MODX_CORE_PATH.'/components/contentSync/';

$collection = [];

if (is_dir($path)){
    if ($folder = opendir($path)){
       while (false !== ($entry = readdir($folder))) {
       if (strpos($entry,'.json')){
           array_push($collection,$entry);
       }
    }
    }
}
closedir($folder);

if (!empty($collection)){
    foreach ($collection as $element){
        $json_data = file_get_contents($path.$element);
        $resource = $modx->getObject(modResource::class,['id' => preg_replace('/\.\w+$/', '', $element)]);
        if (is_object($resource)){
            $response = $modx->runProcessor('resource/update', $modx->fromJson($json_data));
            if ($response->isError()) {
                print_r($modx->error->failure($response->getMessage()));
            }
        } else {
           $response = $modx->runProcessor('resource/create', $modx->fromJson($json_data));
            if ($response->isError()) {
                print_r($modx->error->failure($response->getMessage()));
            } 
        }
    }
}
else {
    echo 'Коллекция элементов пуста';
}
Обратите внимание — вам нужно авторизоваться в контексте mgr, для этого необходимо ввести данные пользователя, который обладает правами на создание и обновление ресурсов, а так же доступ в админка, собственно.

Подумаем о светлом будущем и будем писать названия классов через ::class. Вдруг этот компонент нужно будет переводить на тройку и этим действием мы выключим кучу deprecated ошибок в логах. В двойке данная реализация тоже работает.

Последний этап — запуск скрипта. Если вы увидите в консоли какие-то ошибки, следовательно какой-то ресурс обновится не смог. Обратите внимание на ошибку.

Вот и вся история с хроникой.

Итак, немного послесловия. Почему данная реализация не самая классная?

  1. Нельзя передать id при создании элемента. Следовательно нужно полное совпадение текущих id на сайтах. Если менеджер удаляет ресурсы без возврата — инкремент в базе сместит все айдишники и это крайне грустно. В качестве финального решения возможно стоит использовать alias. В одном и том же контексте двух элементов с одним alias быть не может, а следовательно его можно использовать как ключ.
  2. Не синхронизируются элементы которые не являются ресурсами. А следовательно условный справочник «Бренды» в minishop2 останется не актуальный. Возможно, стоит дёргать всех наследников xPDOObject и xpdoSimpleObject используя getCollectionGraph
Что вы думаете по этому поводу? Стоит ли попытаться сделать такой компонент? Ваши плюсики — моя радость.
Ваши донаты — абсолютно честно пойдут на пиво.

Спасибо!

Разработка проспонсирована компанией «Тузик» zoosklad.by -> Оптовая продажа товаров для животных в Республике Беларусь

P.S Фича реквест для @Василий Наумкин
Очень было бы круто, что если я указываю определенный тег, то ниже код парсится как markdown текст. Я часто готовлю любые тексты в Bear для macOS и в нем архиудобно, но потом приходится переводить текст в теги сайта. Если это не слишком долго и сложно, может стоит рассмотреть к реализации?
Павел Бигель
20 февраля 2020, 13:55
modx.pro
4
1 349
+16
Поблагодарить автора Отправить деньги

Комментарии: 39

Павел Бигель
20 февраля 2020, 14:05
+1
Слова после всех слов: Не страдайте херней и делайте dev версию как контекст и гоняйте изменения транспортниками. Почему этим пользуются полтора человека — я не знаю.
    Yurij Finiv
    21 февраля 2020, 00:59
    0
    Не очень удобно. Думаю когда-то создать вывод через pdoFetch спасибо @Василий Наумкин за данной функционал он очень удобный, и переносить всё изменения на prod, но когда будет данная реализация не могу сказать. Возможно кто-то быстрее сделает, время покажет.
    Василий Наумкин
    20 февраля 2020, 14:08
    +2
    Суть в том, что все объекты зависимые от modResource можно паковать в JSON объекты и переносить их от сайта к сайту, делая обратное воспроизведение.
    Всё уже давно придумано, сделано, и работает — modmore.github.io/Gitify/ru/

    Очень было бы круто, что если я указываю определенный тег, то ниже код парсится как markdown текст. Я часто готовлю любые тексты в Bear для macOS и в нем архиудобно, но потом приходится переводить текст в теги сайта.
    markdowntohtml.com
      Павел Бигель
      20 февраля 2020, 14:10
      0
      У меня как-то исторически не сложились отношения с gitify. Я его не понимаю. Но это в целом моя проблема.
        Олег Щавелев
        20 февраля 2020, 15:20
        +1
        Open Source — это всегда много интересных решений, даже если кто-то, когда-нибудь придумал уже как выполнять данную задачу.

        +1 В карму)
          Игорь Терентьев
          21 февраля 2020, 19:26
          0
          Несколько лет только им и пользуюсь для контроля версий и переноса изменений с дев на прод. В принципе все устраивает.
        Сергей Шлоков
        20 февраля 2020, 19:47
        +1
        Еще более дегенеративная особенность — MODX учит вас хранить все в ресурсах
        И где он такому учит? Просто никому не хочется заморачиться с созданием отдельного интерфейса. Есть дерево ресурсов и нормально. Но есть уже дополнение Collections. Пользуйся на здоровье.
        Плохо, что MODX не учит, что ресурс — это эндпойнт.
          Павел Бигель
          20 февраля 2020, 20:34
          +1
          При установке MODX пример домашней страницы вполне себе учит тебя хранить контент в ресурсах
            Олег Щавелев
            20 февраля 2020, 23:28
            +2
            Хочу ставить своих пять копеек. Как не парадоксально вы права оба.

            Изначально MODX имел тип платформы CMS/CMF, что заложило более гибкую модель разработки. То есть проект начинаем со стандартных задач, использую объектную модель MODX и в какой-то момент роста проекта модель расширяется появляться кастомные таблицы, появляются свои классы и кастомизируется бек-энд часть MODX. Вот пример как MODX LLC видело использование своей платформы Связи с этим комментарий Павла Бигеля даже аргументом Сергею Шлокову нельзя назвать. Так как он не противоречит вышесказанному.

            Но в какой-то момент MODX REVO обрусел, появилось экосистема базирующиеся на трех ресурсах modx.pro, modstore.pro, modhost.pro и тема гибридности начала транспонироваться в CMS. Cвоими особенностями недостатками минусами и плюсами. И если смотреть с точки зрения призмы сообщества modx.pro точки зрения Павла Бигеля уместна.
              Это сообщение было удалено
              Сергей Шлоков
              21 февраля 2020, 12:27
              0
              Ничего подобного. Устанавливается каркас для работы с сайтом. Для визитки этого вполне хватит. А для сайтов типа блога умный разработчик ставит Collections или аналогичный пакет. И где-то в дебрях руководств Джейсон это объяснял.
                Василий Наумкин
                21 февраля 2020, 12:44
                +1
                Так Collections использует те же ресурсы, нет? Просто прячет их в админке, делая более удобной работу с каталогом.
                miniShop2 делает так же, но это всё равно ресурсы — они так же попадают в кэш ресурсов сайта и так же приведут к тормозам, если их будет много.

                Принципиальной разницы я не вижу.
                  Сергей Шлоков
                  21 февраля 2020, 12:49
                  -1
                  Разве? Так делал Articles. Лично у меня в голове отложилось, что разница у них в том, что Collections хранит контент в отдельной таблице. И Джейсон где-то в обсуждениях говорил, что юзайте Collections, а не пихайте всё в дерево ресурсов, которые являются эндпойнтами.
                    Василий Наумкин
                    21 февраля 2020, 12:57
                    +1
                    Ну я в их модели никаких новых типов для документаов не вижу, только CollectionContainer, который расширяет modResource.

                    Стало быть, храниться в нём будут обычные ресурсы. Вот и подтверждение в процессоре publish на строке 32
                    $this->resource = $this->modx->getObject('modResource', $id);
                    Дальше мне копать исходники лень, но вроде и так всё понятно.
                    Сергей Шлоков
                    21 февраля 2020, 12:53
                    0
                    Ан нет, Collections — это тоже CRC.
                      Василий Наумкин
                      21 февраля 2020, 12:59
                      +2
                      Как и Tickets, как и miniShop2.

                      А как только ты захочешь написать популярное дополнение, которое будет хранить свой контент не в ресурсах, тебе сразу придётся написать еще кучу сниппетов для генерации по ним меню, карты сайта, хлебных крошек и т.д.

                      То есть, создать параллельную инфраструктуру сайта для твоего допа. Которую людям надо будет осваивать вместо обычной.

                      Я вот до сих пор этого не сделал, хотя одно время всерьёз над этим подумывал, как вектор для развития miniShop.
                        Сергей Шлоков
                        21 февраля 2020, 13:05
                        0
                        И как люди во фреймворках живут? ))
                          Василий Наумкин
                          21 февраля 2020, 13:09
                          +2
                          Где-то во фреймворках есть заранее созданные таблицы под контент? С контекстами, прикрученными картами ресурсов кэша, менеджерами с кастомизацией форм для их редактирования?

                          Нет, никто так не делает во фреймворках, там ты всё пишешь под себя.

                          Именно поэтому я и доказываю тебе, что в ресурсы в MODX — они именно что для контента. Это ограничивает систему, но делает её удобной для новичков и небольших сайтов.

                          Использование modResource в MODX — это единственно верный путь для хранения пользовательского контента в 99% использования системы. А если кому-то это не подходит, нужно поискать другую систему, таков мой вывод.
                Pavel Zarubin
                21 февраля 2020, 09:45
                +3
                Учит — учит, а делает он это требованием совершать большое количество однотипных действий для расширения модели, создания интерфейса на extjs и т.д., Эта проблема бы конечно же решилась если бы вместо фреда выпустили бы конструктор новых типов ресурсов и хотя бы основных моделей, особенно учитывая то, что это не сложная задача но трудоемкая, но видимо сами разработчики modx'a изначально предполагали что хранить все надо в ресурсах, и это же подтверждает наличие настройки форм позволяющее для каждого шаблона сделать свою конфигурацию полей.
                А сейчас, по факту, пока я буду расширять класс modResource под каждую свою модель я на том же ларавеле за это время половину админки напишу
                  Олег Щавелев
                  21 февраля 2020, 12:16
                  +1
                  @Pavel Zarubin

                  А сейчас, по факту, пока я буду расширять класс modResource под каждую свою модель я на том же ларавеле за это время половину админки напишу
                  А это примерно как сравнивать «запорожец», «формулу 1», «самолет», и «круизный лайнер» — они выполняют одну задачу, перевозят людей. Но делают он очень по-разному. IT рынок безграничен. И решений очень много. Я думаю, с этим согласен каждый.
                    Pavel Zarubin
                    21 февраля 2020, 12:26
                    +2
                    Ну во первых я не сравнивал modx с ларавелом, я буквально говорю:
                    «CMS — для быстрого создания сайта, но процесс расширения ресурсов вручную настолько времязатратен в modx что за то же время можно половину своей админки на Laravel написать», подразумевая что написать свою админку в laravel далеко не быстрая задача, и такие времязатраты в CMS не допустимы и именно по этому modx именно «приучает» хранить все в ресурсах и не делать свои модели под разные сущности, а унифицировать их через id шаблона, что откровенно очень плохая практика
                      Олег Щавелев
                      21 февраля 2020, 13:08
                      0
                      Любая идея универсальности формулирует определенные ограничения. Я так же могу отметить, что в продуктовом маркетинге MODX было заложено очень много больных вопросов, к примеру CMF с такой системой хранения данных уже вызывают дискуссии по поводу попаданию под «норму» даже при гибридном статусе. Но таких гибридных систем с дробью всего две и мне кажется очень здорово, что они есть. Но мне кажется это уже обсуждение истории.
                    Сергей Шлоков
                    21 февраля 2020, 12:44
                    +3
                    Не надо наводить тень на плетень. Возможность расширения базового функционала никак не является руководством к использованию ресурсов как к хранилищу контента сайта. Такая возможность есть, но она не является обязательной. И для опытного разработчика нет никакой надобности добавлять свой CRC. Только если, конечно, не поставлена именно такая задача — своё контекстное меню, процессоры и т.п в дереве ресурсов.

                    А вообще для меня выглядит странно — принять руководство по расширения базового класса инструкцией к использованию ресурсов как хранилище контента сайта.

                    пока я буду расширять класс modResource под каждую свою модель я на том же ларавеле за это время половину админки напишу
                    А зачем? Есть уже готовые пакеты. В крайнем случае, можно по аналогии сделать свой пакет из заготовки modExtra.

                    И на ларавел ты не напишешь быстрее аналог дерева ресурсов. А на твоё возражение, что ты и не будешь его писать, отвечу — и в MODX не надо всё пихать в дерево ресурсов.

                    П.С. Это конечно моё мнение и оно может отличаться от твоего. Это не значит, что оно правильное или нет. Просто нет одного правильного решения. Каждый пилит код как умеет.
                      Василий Наумкин
                      21 февраля 2020, 12:53
                      +5
                      Возможность расширения базового функционала никак не является руководством к использованию ресурсов как к хранилищу контента сайта.
                      Насколько я помню, modResource наследуется классами modDocument, modLink, modWebLink и modStaticResource.

                      И по умолчанию в админке создаётся именно modDocument. Это не для хранения контента? И все сниппеты заточены именно под вывод этих документов, и для построения по ним навигации. Да и поля у этих документов как-бы намекают: published, pub_date, hide_menu, link_attributes и т.д.

                      Что бы там Джейсон себе не фантазировал, но как только разработчик доходит до мысли, что нужные ему данные проще хранить в своих таблицах, ему резко перестают быть нужны ТВ, шаблоны, чанки и сам MODX.
                      Потому что он переходит работать на фреймворки, которые создают таблицы и модели гораздо проще и быстрее.
                        Сергей Шлоков
                        21 февраля 2020, 13:03
                        0
                        Мда. Вера в MODX окончательно пошатнулась. Я был уверен, что хоть у одного разработчика родилась мысль сделать отдельную таблицу для ресурсов. И тут на тебе… Ни одного не оказалось. Ау, ребята, вот вам крутая идея!!!

                        Что бы там Джейсон себе не фантазировал, но как только разработчик доходит до мысли, что нужные ему данные проще хранить в своих таблицах, ему резко перестают быть нужны ТВ, шаблоны, чанки и сам MODX.
                        100%. Многие бывшие модиксеры из этого сообщества это подтвердят.
                          Василий Наумкин
                          21 февраля 2020, 13:05
                          0
                          Выше отписал про это.

                          Слишком большой объём работы и непонятный выхлоп. Экономически не выгодно.
                            Сергей Шлоков
                            21 февраля 2020, 13:11
                            0
                            Сильно больше, чем вложено в Collections? На примере своих дополнений не вижу ничего особо сложного. У меня и данные в своих таблицах, и сниппеты с ними работают, и чанки и т.п.
                              Василий Наумкин
                              21 февраля 2020, 13:28
                              +2
                              Еще раз — Collections работает с ресурсами. Соответственно, все сниппеты MODX работают с ним.

                              Если ты перетаскиваешь весь контент в другие таблицы, то какой-нибудь GoogleSiteMap для них карту сайта не построит, а mSearch2 их для поиска не проиндексирует.

                              Сегодня есть pdoTools, и с его помощью можно выводить данные из любых таблиц, для которых есть схема, но опять же, все его родные сниппеты заточены именно под ресурсы. Например, в pdoResources прописана сортировка именно по publishedon, которого может не быть в другой таблице. А pdoMenu использует карту ресурсов в modX::getParentIds для построения меню.

                              У меня и данные в своих таблицах
                              У меня тоже.
                              Но это у меня и тебя, в наших непубличных проектах.

                              А теперь представь себе условный miniShop3, который хранит миллионы товаров в своих собственных таблицах. К нему нужно будет поставить и полный набор всех сниппетов для вывода этих товаров, генерации меню, хлебных крошек и т.д.

                              Как оно, сильно больше Collections будет? Одну документацию писать замучаешься, а потом баги отлавливать и править.

                              Говорю же, я много об этом думал и пришёл к выводу, что делать подобное не стоит. Ну а если и делать, то как отдельную независимую либу, которую потом интегрировать с MODX.
                              Собственно, как Андрей Чирко уже сделал с Shopkeeper 4 — и что-то большого успеха на рынке MODX у такого решения не видать.
                        Pavel Zarubin
                        21 февраля 2020, 13:21
                        +3
                        И на ларавел ты не напишешь быстрее аналог дерева ресурсов.
                        Я бы поспорил, особенно учитывая что есть великое множество готовых библиотек (например кстати только у нее звезд на гитхабе почти столько же сколько у modx'a), нужно всего лишь добавить каждой модели parent_id и rank (если нужна сортировка), а на фронте есть великое множество библиотек для работы с деревом, которые на вход принимают один и тот же json и можно менять буквально одной строчкой кода

                        А зачем? Есть уже готовые пакеты. В крайнем случае, можно по аналогии сделать свой пакет из заготовки modExtra.
                        Я бы с радостью посмотрел на эти готовые пакеты, либо мы говорим о разном, либо ты не видел тот же билдер в октябре и не представляешь о чем я говорю (кстати билдер, это первое что приводят в пример защитники октября, и к сожалению — это единственное его преимущество, все еще возникает вопрос зачем?)

                        Такая возможность есть, но она не является обязательной
                        Обязательной не является, но было бы гораздо удобнее если бы являлась и была удобно реализована)

                        Но да ладно, это все действительно имхо
                    Александр Туниеков
                    23 февраля 2020, 07:57
                    0
                    Часто возникает ситуация, что тестовая версия сайта начинает плотно отставать по контенту от продакшена и необходимо как-то синхронизировать изменения.
                    Актуальная тема :-). Как-то давно написал ChangePack, но сейчас даже сам им не пользуюсь :-(. В чем в ChangePack косяки уже забыл. Наверно, что изменения в файлах не ловит и что поменялось в сниппетах и чанках нельзя посмотреть. Но счас тестовый и рабочий сайт надо будет полгода, пока активно разработка идет, синхронизировать. А в ручную это полчаса-час.
                    Про Gitify есть где-нибудь хорошее описание? На русском, текстовое и актуальное :-)?
                      Михаил
                      23 февраля 2020, 08:54
                      0
                      По Gitify тут
                        Александр Туниеков
                        23 февраля 2020, 11:30
                        0
                        На русском, текстовое и актуальное
                          Михаил
                          23 февраля 2020, 11:32
                          +1
                          Вы серьезно или троллите? На руском, текстовое и актуальное
                            Александр Туниеков
                            23 февраля 2020, 11:38
                            0
                            Ну блин. Я же справку хочу. Как установить, как синхронизировать гит какой устанавливать. На виндоус запуститься ли? и т.д. А про то что Gitify есть я давно в курсе. Только инфу по разным сайтам собирать и тестить как что работает не охота. И времени особо на это нет.
                              Михаил
                              23 февраля 2020, 11:40
                              +3
                              вам справку с больницы надо, судя по всему
                                Александр Туниеков
                                23 февраля 2020, 12:09
                                0
                                ААА… блин. Вот я прокосячил. На меню то слева не посмотрел :-(. Простите плиз.
                              Александр Туниеков
                              23 февраля 2020, 12:51
                              0
                              Как сложно все. И просто компонент синхронизации для MODX не сделаешь.
                              У меня такая задача. На модкс частично перенесена система управления производством (Первоначально сделанная на Excel директором. Но excel тупит на больших объемах данных и синхронизация разных книг еще тот геморой). Регулярно на сайте что-то меняется. Сейчас, чтобы не мешать рабочему процессу фирмы, сделали тестовую копию сайта. Теперь желательно сделать так чтобы ее можно было быстро синхронизировать с рабочим сайтом.
                              Нужно чтоб с тестового сайта переносились изменения всех ресурсов, копировались все измененные файлы компонентов и переносилась структура таблиц компонентов без изменения данных. Еще таблицы с настройками желательно настроить чтоб копировались, но это можно и ручками.
                              Если тут спецы по Gitify есть, подскажите пожалуйста, это можно сделать? И где смотреть как такое организовать?
                                Олег Щавелев
                                23 февраля 2020, 19:44
                                0
                                А я перестал за этим продуктом следить в 2016 году. Такой анонс это хорошее ускорение. Как минимум для меня. +1
                          Александр Туниеков
                          29 февраля 2020, 18:30
                          0
                          В качестве финального решения возможно стоит использовать alias. В одном и том же контексте двух элементов с одним alias быть не может, а следовательно его можно использовать как ключ.
                          Может быть :-). У папки может быть включено использовать псевдоним в uri. То есть уникальный, по идее, только uri, но gitify uri использует и все равно какие-то проблемы. И, если id ресурсов меняются, надо в шаблонах и чанках id ресурса переписывать в конструкциях типа
                          {123 | url}
                          {$_modx->makeUrl(21)}
                          и т.д.
                            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                            39