Улучшаем процессы разработки в Modx Revo: работа с git, деплой с gulp, тестовые окружения
Давно не писал статьи про мою любимую CMS Modx Revo, а зря. Яндекс.метрика убедительно говорит, что статьи на эту тему пользуются большой популярностью у посетителей блога. Сегодня сей пробел частично исправлю и хочу поговорить вот о чем. К сожалению или к счастью, одной из главных фишек Modx является тот факт, что практически все содержимое сайта, включая, ресурсы, шаблоны, сниппеты, чанки и прочее-прочее хранится в mysql-базе. Из этого вытекают и плюсы, и минусы, но мне минусов видится больше.
Основные претензии: работа с git в Modx, настройка рабочего окружения, конфиги для боевых и тестовых серверов, деплой и встроенные редакторы кода
Итак, в этой статье я покажу, как я разбирался с этими пунктами, что удалось сделать успешно и какие моменты не получилось реализовать до конца. Добро пожаловать в статью и комменты!
Подробнее о том, что нас не устраивает в Modx
Главный минус - это непонятность, а кто-то утверждает, что и невозможность
подружить эту cms с любой системой контроля версий.
В cms тупо нет "девелоперских" файлов - все в таблицах БД!
Не знаю, как остальным modx-коллегам, но мне совершенно не хочется вести разработку без привычного git-а.
Сколько я не яндексил по интернетам, так и не нашел адекватного описания, как работать с git в Modx.
Вторая проблема - это неудобство деплоя на тестовый или боевой сайт.
Вы можете сказать, что можно каждый раз делать дамп всей базы и заливать ее через условный phpMyAdmin,
а после копировать все папки-файлы целиком по ftp, но удовольствие довольно сомнительное.
Первый раз это все равно придется делать, но для послерелизных мелких правок
не хочется возиться с дампами базы,
а хочется сделать что-то вроде gulp deploy из командной строки и перезалить сайт без обновления базы.
Третья проблема, это уже мои личные тараканы, но Modx-ские редакторы кода далеки от идеала.
Относительно неплохой редактор кода Ace, не говоря уже о предустановленном редакторе кода,
не позволяет делать кучу вещей, к которым быстро привыкаешь в любимом sublime или phpStorm.
Основная идея сего повествования
Делаем каждый элемент Modx статичным!
Да, именно так. Если git не идет к Modx, то пусть сам Modx идет к git.
Для тех, кто не в курсе: в Modx есть возможность каждый элемент (шаблон, чанк, сниппет) сделать статичным.
Это означает, что содержимое этого элемента хранится во внешнем файле, а не в таблицах БД.
В настройках есть скромная галочка Статичный, включение которой позволяет выбрать файл
для хранения кода этого элемента.
Я не знаю, зачем разработчики Modx ввели эту штуку, но именно ей мы воспользуемся для организации дружбы с git
(хм, а может, для этого и ввели создатели Modx статичные элементы?..)
Итак, идея озвучена, теперь подробнее, как мы ее будем реализовывать и что из этого получится.
Структура проекта Modx Revo.
Допустим, мы создаем сайт webdevkin.ru.
Первое: после установки Modx в корне проекта помимо стандартных папок assets, core, connectors и manager создаем папку modx. Это незатейливое название говорит нам, что именно в ней мы будем хранить код для шаблонов, чанков и сниппетов. А в папке modx создадим еще 3 папочки: templates, chunks и snippets. Дабы не сваливать файлы в одну кучу. Также в корне заведем папку scripts, где будем хранить внешние php-файлы. Думаю, практически на каждом сайте парочка таких php-скриптов да есть. Они потихоньку дергаются с клиента аяксом и делают что-то полезное, например, отправляют сообщение владельцу сайта с формы обратной связи.
Второе: создаем в каталоге assets папку templates, а в ней папку webdevkin. webdevkin - это название шаблона для нашего сайта. В webdevkin лежат такие папки: css, js, fonts, img. Назначение их, думаю, понятно. Таким образом, вся статика для сайта лежит в одном месте - в папке assets/templates/webdevkin
Вопрос: а как создавать чанки, шаблоны и сниппеты
Ответ: создаем как обычно, в админке Modx. После создания отмечаем галочку Статичный и выбираем файл, в котором будет храниться содержимое элемента. Не забываем, для склада этих файлов мы и создали папку Modx. Я предпочитаю для чанков и шаблонов создавать файлы .tpl, для сниппетов .php. Как удобно, так и делайте, хоть храните в .txt все подряд. После создания файла мы можем работать с ним не в админке Modx, а в привычном редакторе кода.
По сути, основная идея уже расписана, базовая структура проекта у нас создана, как создаются элементы, разобрали. Дальше пойдут детали. Но прежде чем перейти к настройке git, рассмотрим такой небольшой момент, как кеширование.
Отключение кэширования в Modx
Про проблемы кэширования рассказано уже много и всем, кому не лень. Даже на нашем сайте есть статья про кэширование в Modx Revo.
Как задевает кэширование нас именно сейчас?
Когда мы пишем код какого-то элемента в админке Modx и нажимаем Сохранить, то, как правило,
кэш очищается и мы сразу можем обновить страницу и полюбоваться результатом.
Конечно, если мы не сняли специально галочку Очистить кэш при сохранении.
Если же мы создаем код по нашей схеме, то Modx не понимает, в какой момент мы сохраняем сниппет в условном sublime и соответственно, не очищает кэш.
Вы обновляете страницу и не видите никаких изменений: все взято из кэша, сохраненного ранее.
Как быть? Не лезть же в админку Modx вручную очищать кэш после каждой правки какого-то чанка?
Нет, не для такого мы все это замутили.
Нужно просто отключить кэширование ресурсов в Modx на время разработки.
Важно: только на время разработки!
Оставить сайт на Modx работать без кэширования - это страх и ужас, modx-товарищи, полагаю, хорошо понимают, о чем речь :-)
Самый простой и надежный способ временно отключить кэширования с помощью гугла отыскался такой:
- 1. Создаем плагин DisableCache
- 2. Добавляем в него такой кодик
global $modx; $modx->documentObject["cacheable"] = 0;
Git и Modx
Я не буду рассказывать, как установить git, как провести инициализацию и прочее - это тема для отдельной статьи (впрочем, вот она). Думаю, соратники по сайтостроительству, привычные к гиту, хорошо представляют, что нужно делать, но одну интересную вещь стоит показать: файлик .gitignore именно для Modx.
/.idea /webdevkin.sublime-project /webdevkin.sublime-workspace /node_modules components /connectors /core /manager /setup custom.log error.log
Разбираем: .idea и .sublime-* - служебные файлы редакторов phpStorm и sublime (работаю и с тем, и другим) node_modules - папка для npm-модулей, как минимум, модуль для деплоя, разберем чуть позже. Дальше идут 5 служебных папок Modx. components указываем без /, потому как подразумеваем папку /assets/components/ И 2 файла с логами, хотя можно и закинуть их в папку logs.
Таким образом, при этой системе мы игнорируем все изменения в служебных файлах Modx и отслеживаем изменения в "рабочих" файлах, то есть тех, которые мы создаем непосредственно в процессе разработки.
Я привел самый простой вариант, когда стандартной поставки Modx вполне хватает для разработки, нам не требуется создавать никакие дополнительные файлы и папки. В реальной жизни структура проекта чаще всего разветвленнее, но Вы наверняка по этому примеру сможете настроить .gitignore под свои нужды.
Деплой сайта по ftp с помощью gulp
Теперь, когда мы избавились от хранения наших программистких кодов-скриптов в базе и вынесли это добро во внешние файлы, мы можем приступать к деплою сайта. Конечно, можно руками копировать файлы через любой ftp-клиент, а можно настроить плагин, например, gulp для работы с ftp. Основы gulp разберем в одной из следующих статей, а пока будем считать, что читатель знаком с системами сборки или имеет желание самостоятельно овладеть любой из них.
UPDATED: если Вы интересуетесь, что можно хорошего сделать с gulp еще, предлагаю ознакомиться со статьей Сборка приложения Backbone + Require.js с помощью gulp
Для настройки задачи деплоя нам понадобятся следующие модули: сам gulp, gulp-util (где он только не нужен) и, главное, vinyl-ftp. Содержимое gulpfile.js таково (мы рассматриваем только одну задачу деплоя нашего сайта на боевой сервер)
'use strict'; var gulp = require('gulp'), gutil = require('gulp-util'), ftp = require('vinyl-ftp'); // Файлы для копирования по ftp var globs = [ 'assets/templates/webdevkin/**/*.*', 'scripts/**/*.php', 'modx/**/*.*' ]; // Соединение с ftp function getConn() { return ftp.create({ host: '111.222.333.444', user: 'ftp_user', pass: 'ftp_pass' }); } gulp.task('deploy', function () { var conn = getConn(); return gulp.src(globs, {base: '.', buffer: false}) .pipe(conn.dest('/path_to_your_files')) .pipe(gutil.noop()); });
Подробнее.
В начале мы подключаем 3 нужных для работы модуля. Дальше указываем в массиве, что мы хотим заливать на боевой сайт.
- 1. Статика (css, js, img, fonts)
- 2. Скрипты php, если они у вас есть, конечно
- 3. Чанки, шаблоны, сниппеты из папки modx
Если все настроено правильно, начиная с установки самого gulp, то по запуску gulp deploy из командной строки в корне проекта на Ваш боевой сайт скопируются все нужные файлы. И больше не нужно будет создавать дамп локальной базы, заходить в phpMyAdmin боевого сайта и восстанавливать всю базу ради правки трех шаблонов. Единственно, нужно помнить о кэше и сбрасывать его каждый раз после процедуры деплоя. Можно делать это в админке Modx боевого сайта, лично меня не напрягает, или покопаться в плагинах того же gulp - наверняка есть плагин, позволяющий очищать кэш Modx, а именно содержимое папки core/cache. У Вас есть какие-то идеи по этому поводу? С удовольствием почитаю в комментариях.
Настройка разных окружений в Modx.
Что я имею в виду под этой громкой фразой?
Чаще всего в разработке на Modx мы имеем дело как минимум с двумя средами - локальная машина и хостинг клиента, то есть боевой сайт.
Каждый раз при заливке файлов на сайт и копированию обратно на свой компьютер нужно не забывать менять в конфигах настройки доступа к mysql,
а также пути к корневой папке проекта.
На хостинге это выглядит примерно как /home/user1234/site.ru/www/, на своем компьютере - c:/projects/www/site.ru/
И соответственно, настройки БД тоже разные.
И это в простом случае, если мы деплоим с локальной среды сразу на рабочий сайт.
Также приходится предварительно проверять функционал на тестовом сервере, настройки которого опять-таки отличаются от вышеуказанных.
Конечно, можно один раз создать 3 варианта файлов настроек для всех окружений, разместить их на каждом из испльзуемых серверов и
копировать файлы сайта только нашим способом, который мы разобрали выше.
Но иногда все-таки приходится перезаливать сайт целиком и нужно каждый раз не забывать менять все 4 файла конфига:
- /config.core.php
- /manager/config.core.php
- /connectors/config.core.php
- /core/config/config.inc.php
Конечно, можно с этим жить, но иногда становится довольно грустно. Хочется настроить эти злосчастные параметры только один раз и вспоминать только тогда, когда нам понадобится создать еще одну тестовую площадку. Например, к работе над проектом присоединяется Ваш коллега, на рабочей машине которого свои настройки базы.
Посмотрим на первые 3 файла. Практически у всех они совпадают до единого пробела - в них указывается путь к ядру Modx, примерно так
define('MODX_CORE_PATH', 'c:/www/site.ru/core/'); define('MODX_CONFIG_KEY', 'config');
Кстати, буду весьма благодарен читателям, ежели подскажете неразумному, по каким причинам был вообще создан этот файл да еще и скопипащен в трех экземплярах. Если Вам нужно в константе MODX_CORE_PATH указать путь к папке core, то можно это сделать и так
$rootPath = $_SERVER['DOCUMENT_ROOT']; if ($rootPath[strlen($rootPath) - 1] != '/') { $rootPath = $rootPath . '/'; } define('MODX_CORE_PATH', $rootPath . 'core/'); define('MODX_CONFIG_KEY', 'config');
Что поменялось? Да ничего, кроме того, что мы не прописываем путь к корню проекта руками, а вытаскиваем его из переменной $_SERVER['DOCUMENT_ROOT']. Условие с добавлением слэша в конец пути добавлено на всякий случай. Некоторые веб-серверы добавляют этот слэш, некоторые нет (сталкивался с таким пару раз). А нам этот слэш категорически нужен для правильного формирования полного пути к core/
Таким образом, заменив содержимое всех трех файлов config.core.php на вышеприведенный код, нам становится совершенно по барабану, на каком хостинге или машине располагается наш проект. Путь к папке core всегда будет определяться точно.
А теперь давайте посмотрим на более интересный файл core/config/config.inc.php Там кроме кучи путей к разным папкам и хостам содержатся настройки к базе данных. Выглядит это примерно так:
/** * MODX Configuration file */ $database_type = 'mysql'; $database_server = 'localhost'; $database_user = 'user_siteru'; $database_password = 'password_siteru'; $database_connection_charset = 'utf8'; $dbase = 'db_siteru'; $table_prefix = 'modx_'; $database_dsn = 'mysql:host=localhost;dbname=db_siteru;charset=utf8'; ... if (!defined('MODX_CORE_PATH')) { $modx_core_path= '/home/site.ru/public_html/core/'; define('MODX_CORE_PATH', $modx_core_path); } ... // Здесь еще десяток аналогичных условий
И каждый раз при деплое всех файлов сайта приходится шерстить этот конфиг и менять в нем все настройки под текущее окружение. Конечно, вопрос внимательности, но не все программисты роботы, могут и ошибиться в одной букве и долго разбираться, почему сайт отказывается работать. Особенно весело бывает в случаях разворачивания проекта на site.ru и test.site.ru. Часто доступ к базе один и тот же, а вот название самой базы, естественно, различается. При частых деплоях забыть об этом совсем немудрено. Как же это исправить?
Нам нужен способ определять текущее окружение разработки: локальный компьютер, тестовый сервер и боевой сайт. Самое простое, что приходит в голову - это определять окружение через тот же $_SERVER['DOCUMENT_ROOT']. Например, на локальном компьютере путь включает в себя projects/www/site, на боевом - siteru/public_html, а на тестовом - siteru/test. Посмотрим на код:
/** * MODX Configuration file */ // Если константы не определены, то задаем их // Проверка на наличие константы важна, так как файл конфига может подключаться несколько раз if (!defined('CONFIG_DBSERVER')) { // Определяем окружение $env = 0; // по умолчанию локальное окружение site.lc if (strpos($_SERVER['DOCUMENT_ROOT'], 'siteru/public_html') !== false) { $env = 1; // боевой сайт site.ru } if (strpos($_SERVER['DOCUMENT_ROOT'], 'siteru/test') !== false) { $env = 2; // тестовый сервер test.site.ru } // Настройки для локального окружения if ($env === 0) { DEFINE('CONFIG_DBSERVER', 'localhost'); DEFINE('CONFIG_DBUSER', 'root'); DEFINE('CONFIG_DBPASSWORD', 'root'); DEFINE('CONFIG_DBNAME', 'siteru'); DEFINE('CONFIG_PATH', 'C:/projects/www/site/'); DEFINE('CONFIG_HOST', 'site.lc'); } // Настройки для боевого сайта if ($env === 1) { DEFINE('CONFIG_DBSERVER', '111.222.333.444'); DEFINE('CONFIG_DBUSER', 'user_siteru'); DEFINE('CONFIG_DBPASSWORD', 'password_siteru'); DEFINE('CONFIG_DBNAME', 'db_siteru'); DEFINE('CONFIG_PATH', '/home/siteru/public_html/'); DEFINE('CONFIG_HOST', 'site.ru'); } // Настройки для тестового сайта test.site.ru if ($env === 2) { DEFINE('CONFIG_DBSERVER', '111.222.333.444'); DEFINE('CONFIG_DBUSER', 'user_test_siteru'); DEFINE('CONFIG_DBPASSWORD', 'password_test_siteru'); DEFINE('CONFIG_DBNAME', 'db_test_siteru'); DEFINE('CONFIG_PATH', '/home/www/siteru/test/'); DEFINE('CONFIG_HOST', 'test.site.ru'); } } $database_type = 'mysql'; $database_server = CONFIG_DBSERVER; $database_user = CONFIG_DBUSER; $database_password = CONFIG_DBPASSWORD; $database_connection_charset = 'utf8'; $dbase = CONFIG_DBNAME; $table_prefix = 'modx_'; $database_dsn = 'mysql:host=' . CONFIG_DBSERVER . ';dbname=' . CONFIG_DBNAME . ';charset=utf8'; // ... if (!defined('MODX_CORE_PATH')) { $modx_core_path = CONFIG_PATH . 'core/'; define('MODX_CORE_PATH', $modx_core_path); } // ...
Как видно из кода, сначала мы определяем настройки базы, хост и путь к корневой директории, а затем используем полученные данные для задания констант Modx. Приведен только один пример для MODX_CORE_PATH, остальные задаются аналогично. Специально не привожу код всего файла, так как настройки в разных версиях cms могут отличаться. Используя такой файл конфига, мы легко можем добавлять новые окружения, сколько угодно перезаливать сайты на боевые и тестовые сервера с локальной машины и в обратную сторону, не боясь, что по невнимательности забудем поправить какую-то настройку.
Подводим итоги
Modx - интересная cms для разработки сайтов, как небольших, так и достаточно больших и сложных. Но принцип хранения ресурсов, заложенных в архитектуре, не позволяет удобно работать над проектом и в одиночку, и особенно в команде. В статье я предложил некоторые приемы, которые выработались у меня за время работы с Modx. Рассказал, как можно подружить cms с git, как настроить деплой, как организовать работу с различными окружениями. В приведенной схеме есть несколько моментов, которые я пока не обошел:
- 1. Как упростить работу с кэшем? (очистка кэша после деплоя)
- 2. Главный вопрос: как создавать чанки, шаблоны и сниппеты на всех окружениях не вручную? Ведь все равно эти элементы нужно заводить в базе и создавать для каждого окружения руками. Неудобство такое есть, оно сказывается при частом создании новых элементов.
- 3. Можно ли улучшить определение окружения, делать это не через $_SERVER['DOCUMENT_ROOT'], а каким-то более элегантным способом?
Напишите все, что думаете, в комментариях!
Истории из жизни айти и обсуждение кода.