Разработка новой версии vrmedia.tv
Всё началось с того, что мне позвонил знакомый и попросил проконсультировать по их сайту vrmedia.tv.
Его разрабатывал один местный программист и что-то дело начало буксовать — пропал интерес у разработчика. Вроде и дорабатывать он не против, но что-то времени нет, и всё такое. В общем, было принято решение продолжать работу без него.

Я, конечно, ответил на вопросы и предложил поискать кого-то еще. На что мне объяснили, что это уже третий разработчик и надежды на кого-то еще просто нет. Пришлось выручать знакомого.
Итак, сайт написан на фреймфорке Phalcon, который считает себя самым быстрым из всех существующих. Скорее всего, так и есть, ибо он написан на C и требует установки своего модуля для PHP на сервер. То есть, на публичный хостинг сайт уже не поставишь.
Работает он действительно очень быстро, есть встроенный шаблонизатор Volt, своя ORM, хорошая документация, средства отладки для IDE — в общем, всё как положено.
Одна беда, нет чётких правил для работы в этой системе. Точнее, может они и есть, но разработчик сайта их не придерживался, а написал всё сам, руководствуясь собственным представлением о том, как сайт должен работать. Поэтому
Чем же я руководствовался при выборе MODX?
Что у меня в итоге вышло, можно посмотреть сейчас по адресу vrmedia.tv.
Вся логика работы, вёрстка, функционал — всё написано мною с нуля примерно за 2 месяца, с перерывами на новый год и поездку в Минск. Кода от оригинального автора нет ни строчки, старый сайт использовался только как образец того, что уже есть, и более-менее устраивает заказчика.
Изначально вёрстка была адаптивной, но пока попросили отключить. Скорее всего, потом будет разрабатываться отдельная мобильная версия.
На сайте есть разные виды отчётов (фото и видео), организации и события. Все они друг с другом тесно связаны и находятся в отдельных таблицах.
Ресурсы MODX используются как контроллеры для вывода соответствующих разделов. Каждый из разделов оформляется своим шаблоном, написанным на Fenom. Запросы на показы страниц разруливает системный плагин на событие OnPageNotFound.
То есть, страница с альбомами и вот этот альбом — это один ресурс MODX, с одним шаблоном, который на Fenom проверяет данные, выставленные плагином и показывает то или иное оформление страницы. Через это ресурсов очень мало:

Почти все сниппеты вызываются некэшированными. Абсолютно всё написано на Fenom, поэтому кэшированных плейсхолдеров тоже нет. Сами ресурсы кэшируются, но это мало на что влияет.
Для оформления внешнего вида обширно используются сниппеты-модификаторы, которые с некоторых пор стало можно добавлять через системные настройки pdoTools. Причём, во время работы над сайтом я оптимизировал логику запуска этих модификаторов, и теперь они работают гораздо быстрее, чем если их вызывать через синтаксис MODX.
Например, вот чанк, написанный на MODX:
А вот тот же чанк, но уже на Fenom:
Как вам разница в 5 раз при более удобном синтаксисе — 1 секунда против 0.2? И это только один чанк.
Ничего нового я не изобрёл, а использовал только теплые ламповые решения:
Пакет VRmedia устанавливает свой namespace, модель, процессоры, так что руки полностью развязаны и везде можно использовать pdoResources для выборки данных.
Для правильной работы mFilter2 пришлось добавить простенький сниппет-обёртку vrResources:
Также VRmedia регистрирует свой контроллер в Office, после чего я могу обращаться к нему с разными запросами.
Через этот контроллер работает регистрация и авторизация по номеру телефона, вместо email, и админка для менеджеров.
Всё, что нужно для управления сайтом, вынесено в одно место.
В родную админку MODX никто, кроме меня, заходить не будет. Даже для работы с баннерами BannerY используется свой интерфейс и процессоры из пакета VRmedia.
Для каждого раздела требуются свои разрешения, все действия логируются в системный журнал.
Ну, тут вы можете посмотреть сами —счётчик внизу каждой страницы, справа. По просьбе заказчика непонятные цифры внизу экрана убрали, смотрите в исходном коде.
В среднем выходит 0.2 — 0.4 сек для всех страниц. Кэш никакой не используется, так что убыстрять еще есть куда, если вдруг понадобится.
Вот сравнение через WebPageTest:
Минимальный тариф modhost.pro за 180 рублей против тарифа за $100 от Linode.
На данный момент новый сайт заливает оставшиеся фотографии в хранилище. Процесс это небыстрый, ибо нужно взять оригиналы 90 000 фотографий с одного сервера, добавить к ним 2 превьюшки и выгрузить на другой сервер через протокол OpenStack Swift API. Необходимость в этой процедуре возникла оттого, что на старом сайте все превьюшки непрогрессивные, да еще и размер довольно мелкий — не подходит под мою вёрстку.
Как только этот процесс будет закончен (еще 2 — 3 суток), мы переедем на новый сайт полностью и удалим старый на Linode.
Наверное, всех очень интересует цена. Так как это мои хорошие знакомые, сделал всего за 100к рублей. К тому же, когда я брался за сайт, думал что смогу использовать имеющуюся вёрстку, но в итоге и её пришлось писать заново.
Настоящая цена за подобную работу, по прикидкам Витали Дощенко из modstore.pro, примерно 200к рублей.
Вот такой рассказ об использовании MODX в качестве замены для фреймворка. Надеюсь вам было интересно, жду ваших вопросов в комментариях.
Его разрабатывал один местный программист и что-то дело начало буксовать — пропал интерес у разработчика. Вроде и дорабатывать он не против, но что-то времени нет, и всё такое. В общем, было принято решение продолжать работу без него.

Я, конечно, ответил на вопросы и предложил поискать кого-то еще. На что мне объяснили, что это уже третий разработчик и надежды на кого-то еще просто нет. Пришлось выручать знакомого.
Исходные данные
Итак, сайт написан на фреймфорке Phalcon, который считает себя самым быстрым из всех существующих. Скорее всего, так и есть, ибо он написан на C и требует установки своего модуля для PHP на сервер. То есть, на публичный хостинг сайт уже не поставишь.
Работает он действительно очень быстро, есть встроенный шаблонизатор Volt, своя ORM, хорошая документация, средства отладки для IDE — в общем, всё как положено.
Одна беда, нет чётких правил для работы в этой системе. Точнее, может они и есть, но разработчик сайта их не придерживался, а написал всё сам, руководствуясь собственным представлением о том, как сайт должен работать. Поэтому
- Нет прав доступа, вообще. Для входа в админку требуется только пароль (без имени)
- Соотвественно, нет никаких логов активности
- Нет стандартизированной работы с ajax. Одни методы возвращают JSON, другие целые числа, третьи могут вообще вернуть пустую строку
- Да и логов ошибок тоже нет, всё фигачится прямо на экран
- Так как нет прав доступа, объекты ничего не проверяют и удаляются post запросами от любого гостя
- Модели используются только для работы с БД, без логики. Например, при удалении фотографии из админки, сам файл не удаляется с HDD сервера
- Нет никаких источников файлов, все 100 Gb изображений хранятся прямо на сервере, за большие деньги (на Linode минимально подходящий тариф для таких объёмов стоит $100)
Чем же я руководствовался при выборе MODX?
- Я его неплохо знаю. Это очень важно для разработчика, на самом деле — знать свой инструмент.
- Массу функционала можно реализовать готовыми дополнениями.
- Гигабайты файлов можно загрузить в облако Selectel через Swift — это радикально снизит стоимость хостинга, ведь можно будет переехать на modhost.pro. К тому же, сервер h2 находится в РФ, в сети Selectel и со своим хранилищем будет общаться очень быстро.
- Шаблонизатор Fenom позволяет реализовать сложный функционал без особых заморочек
- Все данные, кроме пользователей, будут храниться в своих таблицах
- И для их управления будет своя админка снаружи сайта, сделанная при помощи Office.
Что у меня в итоге вышло, можно посмотреть сейчас по адресу vrmedia.tv.
Вся логика работы, вёрстка, функционал — всё написано мною с нуля примерно за 2 месяца, с перерывами на новый год и поездку в Минск. Кода от оригинального автора нет ни строчки, старый сайт использовался только как образец того, что уже есть, и более-менее устраивает заказчика.
Изначально вёрстка была адаптивной, но пока попросили отключить. Скорее всего, потом будет разрабатываться отдельная мобильная версия.
Логика работы
На сайте есть разные виды отчётов (фото и видео), организации и события. Все они друг с другом тесно связаны и находятся в отдельных таблицах.
Ресурсы MODX используются как контроллеры для вывода соответствующих разделов. Каждый из разделов оформляется своим шаблоном, написанным на Fenom. Запросы на показы страниц разруливает системный плагин на событие OnPageNotFound.
То есть, страница с альбомами и вот этот альбом — это один ресурс MODX, с одним шаблоном, который на Fenom проверяет данные, выставленные плагином и показывает то или иное оформление страницы. Через это ресурсов очень мало:

Почти все сниппеты вызываются некэшированными. Абсолютно всё написано на Fenom, поэтому кэшированных плейсхолдеров тоже нет. Сами ресурсы кэшируются, но это мало на что влияет.
Для оформления внешнего вида обширно используются сниппеты-модификаторы, которые с некоторых пор стало можно добавлять через системные настройки pdoTools. Причём, во время работы над сайтом я оптимизировал логику запуска этих модификаторов, и теперь они работают гораздо быстрее, чем если их вызывать через синтаксис MODX.
Например, вот чанк, написанный на MODX:
<div class="media-thumb album half">
<div class="date">[[+date:pretty_date]]</div>
<div class="wrapper">
<div class="image">
<a href="/albums/[[+url]]">
[[+thumb:isnot=``:then=`
<img src="http://st.vrmedia.tv/[[+thumb]]" alt="">
`:else=``]]
</a>
</div>
</div>
<div class="info">
<div class="title">[[+title]]</div>
[[+institution:isnot=``:then=`
<div class="institution">
<a href="/institutions/[[+institution_url]]">
[[+institution]]
</a>
</div>
`:else=``]]
<div class="buttons">
<span class="photos">
<i class="fa fa-camera"></i> <span>[[+count_photos:number_format]]</span>
</span>
<span class="views">
<i class="fa fa-eye"></i> <span>[[+count_views:number_format]]</span>
</span>
<span class="likes">
<a href="#" class="action[[+liked:isnot=``:then=` active`:else=``]]" data-action="like/album/[[+id]]">
<i class="fa fa-heart-o"></i> <span>[[+count_likes:number_format]]</span>
</a>
</span>
</div>
</div>
</div>Выборка и оформление 100 альбомов занимает0.0038090: Loaded chunk «tpl.Album.small»
0.9992838: Returning processed chunks
1.0031760: Total time
А вот тот же чанк, но уже на Fenom:
<div class="media-thumb album half">
<div class="date">{$date | pretty_date}</div>
<div class="wrapper">
<div class="image">
<a href="/albums/{$url}">
{if $thumb?}
<img src="http://st.vrmedia.tv/{$thumb}" alt="">
{/if}
</a>
</div>
</div>
<div class="info">
<div class="title">{$title}</div>
{if $institution?}
<div class="institution">
<a href="/institutions/{$institution_url}">
{$institution}
</a>
</div>
{/if}
<div class="buttons">
<span class="photos">
<i class="fa fa-camera"></i> <span>{$count_photos | number_format}</span>
</span>
<span class="views">
<i class="fa fa-eye"></i> <span>{$count_views | number_format}</span>
</span>
<span class="likes">
<a href="#" class="action{$liked ? ' active' : ''}" data-action="like/album/{$id}">
<i class="fa fa-heart-o"></i> <span>{$count_likes | number_format}</span>
</a>
</span>
</div>
</div>
</div>Выдаёт вот такие результаты:0.0038309: Loaded chunk «tpl.Album.small»
0.0015459: Compiled Fenom chunk with name «chunk/29»
0.1887040: Returning processed chunks
0.1926551: Total time
Как вам разница в 5 раз при более удобном синтаксисе — 1 секунда против 0.2? И это только один чанк.
Используемые дополнения
Ничего нового я не изобрёл, а использовал только теплые ламповые решения:
- Ace — мой любимый редактор, с поддержкой синтаксиса Smarty (который хорошо подходит и для Fenom)
- BannerY — бесплатный компонент для управления рекламой на сайте
- debugParser — платный компонент для диагностики тормозов
- HybridAuth — авторизация через соцсети
- Jevix — типограф и HTML фильтр для вывода пользовательских текстов по установленным правилам
- MinifyX — собирает мои стили из SCSS, минифицирует, склеивает и подключает на страницу. То же и с javascript.
- mSearch2 — фильтрует отчёты, события и заведения через специльный расширенный класс.
- Office — платный компонент для работы с пользователями. Регистрация, авторизация, сброс пароля.
- Swift — источник файлов для работы с облачным хранилищем Selectel. При разработке сайта пришлось переписать этот компонент заново, так как меня не устраивала скорость работы старой версии Openstack SDK.
- Translit — используется для генерации url объектов
- Vrmedia — пакет, в который собраны все ресурсы, чанки, шаблоны, права, правила установки и всё остальное, что мне нужно для комфортной разработки сайта.

Пакет VRmedia устанавливает свой namespace, модель, процессоры, так что руки полностью развязаны и везде можно использовать pdoResources для выборки данных.
{$_modx->runSnippet('pdoResources', [
'class' => 'vrAlbum',
'sortby' => 'date desc, id',
'sortdir' => 'desc',
'limit' => 16,
'where' => ['active' => 1],
'leftJoin' => [
'Institution' => ['class' => 'vrInstitution'],
'Like' => [
'class' => 'vrLike',
'on' => 'vrAlbum.id = Like.id_item AND Like.type = "album" AND Like.id_user = ' ~ $_modx->user.id,
],
],
'select' => [
'vrAlbum' => '*',
'Institution' => 'Institution.title as institution, Institution.url as institution_url',
'Like' => '(Like.type IS NOT NULL) as liked',
],
'tpl' => 'tpl.Album.small',
'tplWrapper' => '@INLINE
<div class="media-thumbs album full">
[[+output]]
<div class="clearfix"></div>
</div>
<div class="more">
<a href="/albums/">Смотреть все фото</a>
</div>
',
])}Для правильной работы mFilter2 пришлось добавить простенький сниппет-обёртку vrResources:
<?php
/**@var array $scriptProperties */
if (!empty($resources)) {
if (!empty($where)) {
if (!is_array($where)) {
$where = $modx->fromJSON($where);
}
} else {
$where = array();
}
$where['id:IN'] = !is_array($resources)
? array_map('trim', explode(',', $resources))
: $resources;
if (!empty($where)) {
$scriptProperties['where'] = $modx->toJSON($where);
}
}
return $modx->runSnippet('pdoResources', $scriptProperties);Это потому, что мы фильтруем не таблицу modResource и приходящий параметр &resources нужно превратить в WHERE id IN (1,2,3,...).Также VRmedia регистрирует свой контроллер в Office, после чего я могу обращаться к нему с разными запросами.
Через этот контроллер работает регистрация и авторизация по номеру телефона, вместо email, и админка для менеджеров.
Админка
Всё, что нужно для управления сайтом, вынесено в одно место.
В родную админку MODX никто, кроме меня, заходить не будет. Даже для работы с баннерами BannerY используется свой интерфейс и процессоры из пакета VRmedia.
Для каждого раздела требуются свои разрешения, все действия логируются в системный журнал.
Скорость работы
Ну, тут вы можете посмотреть сами —
В среднем выходит 0.2 — 0.4 сек для всех страниц. Кэш никакой не используется, так что убыстрять еще есть куда, если вдруг понадобится.
Вот сравнение через WebPageTest:
Минимальный тариф modhost.pro за 180 рублей против тарифа за $100 от Linode.
Заключение
На данный момент новый сайт заливает оставшиеся фотографии в хранилище. Процесс это небыстрый, ибо нужно взять оригиналы 90 000 фотографий с одного сервера, добавить к ним 2 превьюшки и выгрузить на другой сервер через протокол OpenStack Swift API. Необходимость в этой процедуре возникла оттого, что на старом сайте все превьюшки непрогрессивные, да еще и размер довольно мелкий — не подходит под мою вёрстку.
Как только этот процесс будет закончен (еще 2 — 3 суток), мы переедем на новый сайт полностью и удалим старый на Linode.
Наверное, всех очень интересует цена. Так как это мои хорошие знакомые, сделал всего за 100к рублей. К тому же, когда я брался за сайт, думал что смогу использовать имеющуюся вёрстку, но в итоге и её пришлось писать заново.
Настоящая цена за подобную работу, по прикидкам Витали Дощенко из modstore.pro, примерно 200к рублей.
Вот такой рассказ об использовании MODX в качестве замены для фреймворка. Надеюсь вам было интересно, жду ваших вопросов в комментариях.
Комментарии: 66
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.












С большим интересом прочитал весь этот рассказ и почерпнул для себя полезные идеи для работы с любимым MODX!
Кастомная админка получилась улётная — всё что надо и ничего лишнего!
интересует что значит«свои». То есть как я понимаю, для работы BannerY в кастомной админке через Office пришлось дописать свои процессоры?
Вот бы такое решение внедрить в сам BannerY даже как платное расширение к Office и BannerY. То есть возможность простого подключения такого же управления через бэкэнд!
Менеджер снаружи не должен создавать баннерные позиции, они уже прописаны в шаблонах. Он может только из них выбирать. А создаю их я, в админке MODX, в родной интерфейсе.
Плюс, как можно заметить, здесь картинки загружаются через окошко, а не выбираются из имеющихся на сервере.
Добавлять это куда-то не планирую, ибо там много своей логики, нужной только для этого проекта.
Классно всё настроил через свой компонент vrMedia — впервые вижу такую практику, очень интересно!
Есть на что ориентироваться при создании проектов на MODX.
P.S.: Побольше бы таких статей о сложных проектах на MODX.pro, где в комментариях можно было бы обсуждать выбор того или иного пути решения задачи и правильность подхода.
Спасибо за статью!
А вот создание отдельной пользовательской админки для меня в новинку.
Насколько я вижу, как и в modhost тут используется extJS ну и бутстрап, куда ж без него.
А вот технологии и логика очень интересуют. Василий может запишешь новый курс на эту тему? Само собой готов голосовать рублем. ДА и практика по использованию шаблонизатора и классов PDOTools тоже не помешает.
Новые курсы я писать пока не планирую.
На modStore не нашел этот компонент, в docs тоже нет ничего.
Тут через поиск наверное найду кучу ссылок, но может получится дать сразу нужную ссылку.
modx.pro/development/3841-modextra-for-modx-2.3/
Я уж молчу про целый курс по написанию компонентов, причём modExtra там берётся за основу.
Версионность сайта реализована только за счет Vrmedia и соответственно любые правки только через него?
Логика работы через OnPageNotFound подобна той, что была описана Ильей Ершовым на минской встрече, это связано с оставшимися таблицами от старого сайта?
Фронтовая админка круто получилась, были ли какие-то сложности при реализации или они были решены при создании Office?
Логику работы через OnPageNotFound я давно описывал, и связана с тем, что новые отчёты на сайте появляются постоянно и держать их в ресурсах мне совершенно не хочется.
Сложностей с админкой никаких нет и быть не может — всё в руках разработчика. Свой контроллер, свои javascript и css файлы. Office только облегчает эту работу и приводит к единому стандарту.
Это же контент, его не нужно пихать в пакет, так же как и 100 Gb фотоотчётов.
С тем же успехом можно прибавлять и Vimeo Plus для хранения видео и плату за Яндекс.Диск, на который выгружают архивы с фотками для заведений.
Т.е. изображения оказывают существенное увеличение стоимости содержания. А дальше он пишет так
180 руб. без изображений сравнивается с $100 с изображениями. Мне кажется это не совсем корректное сравнение. Тогда нужно и из линоды выделить стоимость хранения этих изображений — перенести на какой-нибудь CDN (тот же Selectel). И учитывать просто стоимость содержания сайта. Например, $20 (1600 руб.). Вот такое сравнение будет правильнее.
П.С. Собственно, оптимизация в любом случае приличная.
Linode за $100 — это 8 GB ОЗУ и 6 ядер процессора в полном твоём распоряжении. А скорость работы сайта такая же, как и на минимальном тарифе modhost.pro.
Конечно, можно оставить сайт и на Linode, но минимальный тариф там $10 — а это уже всего 1 ядро процессора и всё равно выходит дороже, чем минимальный modhost.pro + хранилище Selectel.
Импортозамещение во всей красе!
Да уж. Нашему финансовому блоку правительства можно как обычно ничего не делать. Главное — курс задрать повыше.
Fi1osofХорошая работа. Можно показывать любому, кто говорит, что на MODX серьезные проекты не создаются.
Можно было сделать и через тикеты, и через обычные ресурсы, но зачем? У отчёта немного параметров, его не нужно кэшировать, не нужно пихать в карту сайта. Своя мелкая табличка — это проще и быстрее.
При клике страница перезагружается с id автора в url, и mFilter2 выводит только его работы. Это тоже самое, как кликнуть по автору сбоку, только прямая ссылка.
Дописывать понадобилось только класс фильтрации, и то, только потому, что работа идёт не с ресурсами, а со своими таблицами. Всё остальное mFilter2 умеет из коробки.
Кстати, на modx.com запрос с id трансформируется http://modx.com/index.php?id=124. А index.php не убрали почему-то.
Хотя у меня по-прежнему остаётся большой вопрос, с какого перепуга поисковик начнёт индексировать ссылки, которых нигде нет. Сам, что ли, будет проверять index.php и приставлять к нему id?
Но раз это ДУБЛИ!!!111 — закрыл, потому что спорить выйдет дольше.
Данные хранятся не в ресурсах, поэтому нужно подумать еще, как организовать генерацию карты сайтов из разных таблиц.
Но есть одно визуальное отличие. Я застал тот момент когда были оба сайта доступны, старый и Ваш новый переделенный.
Открывал их оба в разных вкладках и по очереди обновлял, получалось что на старом сайте, картинки при обновлении страницы, даже при быстром скроле уже были на странице, а на новом постепенно подгружаются и это визуально заметно.
У Вас картинки выводятся без кеша?
Но вполне может быть, что на старом сайте это не работало и все 150 фотографий альбома грузились сразу, не важно, докрутишь ты до них или нет.
Я немного недопонимаю ещё работу mFilter2 с своей таблицей и мне нужно тоже заставить его работать с моей таблицей.
У меня есть своя таблица, модель сгенерирована, через pdoResources обращаюсь к ней легко:
Как мне заставить mFilter2 работать с моей таблицей? Нужно куда-то добавлять псевдонимы моей таблицы, типа resource == modResource?
Можете помочь с пошаговостью действий? Добавлять свой Метод фильтрации или это только для фильтров типа default, number, year...?
Попробую копать в сторону методов фильтрации…
Плюс код сниппета показывает, что приходящий параметр &resources разбивается на id и отправляется дальше в pdoResources. И всё, больше ничего в нём нет.
mFilter2 рассчитан на работу с ресурсами, поэтому и список id передаёт в &resources. А pdoResources этот &resources понимает только в применении к ресурсам. Так как работа идёт не с ресурсами, надо &resources превратить в WHERE id IN ($resources).
Сама фильтрация, естественно, только через самописные методы mFilter2, как в документации.
Буду дальше копать в этом же направлении.
У тебя всё сделано очень круто, нужно просто планке этого крутого уровня соответствовать.
Спасибо за документацию и примеры!