Введение
Быстренько о том, как я реализовал мобильную версию сайта на базе MODX Revolution. Ранее я рассказывал о том, как создать мобильную версию сайта на CodeIgniter. Эти подходы к организации мобильной версии в обоих фреймворках кардинально не отличаются.
Задача
Основной сайт доступен по адресу site.ru, мобильная версия — m.site.ru. Если человек заходит на m.site.ru с ПК, то его перенаправляем на site.ru. Если человек заходит с мобильного устройства на site.ru, то он автоматически перенаправлется на m.site.ru. Не перенаправляется человек в единственном случае — если он сам решил, какую версию ему показывать.
Шаблоны
Обычно при правильном проектировании сайта на MODX количество шаблонов минимально. В моём случае не пришлось создавать отдельные шаблоны для полной и мобильной версии (учитывая, что различаться версии должны только оформлением, но не содержимым). Однако, внутри шаблонов чанки вызываются особым образом. Дело в том, что имя чанка создаётся динамически на основе того, какую версию сайта нужно отобразить. Например, у меня есть два чанка: header и headerMobile.
В шаблоне вызывается это дело следующим образом:
[[$header[[!SiteVersion]]]]
Если сниппет SiteVersion возвращает пустую строку, то отображается чанк header.
Если сниппет возвращает строку Mobile, то в шаблоне вызывается чанк headerMobile (происходит конкатенация имени чанка и вывода сниппета).
Ниже пример шаблона главной страницы. В самом начале вызывается некэшированный сниппет siteVersionRedirect, который решает, нужно ли перенаправить (redirect) посетителя на другую версию этой страницы. Например, если посетитель впервые зашёл на этот сайт (site.ru/articles) со смартфона, то сниппет перенаправит на мобильный адрес этой страницы (m.site.ru/articles).
[[!siteVersionRedirect]]
<!DOCTYPE html>
<html>
<head>
[[$head[[!SiteVersion]]
</head>
<body>
<div class="wrapper">
<div class="container">
[[$header[[!+site_version`]]]]
[[$navigation[[!+site_version`]]]]
[[$main[[!+site_version`]]]]
[[$footer[[!+site_version`]]]]
</div>
[[scripts[[!+site_version]]]]
</div>
</body>
</html>
Сниппеты
Для функционирования этой системы я создал 4 простых сниппета. Не всё идально, есть некоторые проблемы с производительностью и чистотой кода, но я решил опубликовать хотя бы этот вариант.
SiteMobileBrowser
Сниппет анализирует User-Agent браузера и решает, принадлежит он мобильному устройству или нет. Возвращает true или false.
$userUA = $modx->getOption('HTTP_USER_AGENT', $_SERVER, false);
$uas = array('Android', 'iPad', 'iPod', 'iPhone', 'Mini');
$isMobile = false;
foreach ($uas as &$ua) {
if (substr_count($userUA, $ua) > 0) {
$isMobile = true;
break;
}
}
return $isMobile;
SiteVersion
Возвращает строку Mobile или пустую строку. Используется для формирования имени чанка. Для того, чтобы не вызывать постоянно этот сниппет, я кладу вывод сниппета в плейсхолдер site_version. Таким образом, сниппет вызывается всего один раз, а затем его результат используется всё оставшееся время.
$version = $modx->getOption('site_version', $_SESSION, false);
$version = ($version == 'mobile' || ($modx->isMobileBrowser() && $version != 'desktop')) ? 'Mobile' : '';
$modx->setPlaceholder('site_version', $version);
return $version;
SiteVersionRedirect
Анализирует текущий адрес запроса и сессию пользователя, и если требуется, перенаправляет посетителя на моильную версию страницу.
$output = '';
$protocol = $modx->getOption('server_protocol').'://';
$host = $modx->getOption('HTTP_HOST', $_SERVER, 'quasi-art.ru');
$uri = $modx->getOption('REQUEST_URI', $_SERVER, '');
if (substr_count('/favicon.ico', $uri) > 0) {
return;
}
if (substr($host, 0, strlen('www.')) === 'www.') {
$host = str_replace('www.', '', $host);
}
if (substr($host, 0, strlen('m.')) === 'm.') {
$host = str_replace('m.', '', $host);
}
if (!function_exists('isMobileDomain')) {
function isMobileDomain() {
global $modx;
$host = $modx->getOption('HTTP_HOST', $_SERVER, false);
if (!$host) {
return false;
}
$hostArray = explode('.', $host);
if (!is_array($hostArray)) {
return false;
}
return (isset($hostArray[0]) && ($hostArray[0] == 'm'));
}
}
$currentUrl = $protocol.$modx->getOption('HTTP_HOST', $_SERVER, 'quasi-art.ru').$uri;
$defaultUrl = $protocol.$host.$uri;
$desktopUrl = $protocol.$host.$uri;
$mobileUrl = $protocol.'m.'.$host.$uri;
if ($modx->getOption('site_version', $_SESSION, false) == 'mobile') {
// Если пользователь сам переключился на мобильную версию
if (!isMobileDomain()) {
$modx->sendRedirect($mobileUrl);
}
} elseif ($modx->getOption('site_version', $_SESSION, false) == 'desktop') {
// Если пользователь сам переключился на полную версию
if (isMobileDomain()) {
$modx->sendRedirect($desktopUrl);
}
} else {
// Автоматическое определение
if ($modx->runSnippet('SiteMobileBrowser')) {
if (!isMobileDomain()) {
$modx->sendRedirect($mobileUrl);
}
} else {
if (isMobileDomain()) {
$modx->sendRedirect($desktopUrl);
}
}
}
SwitchToSiteVersion
Этот сниппет используется, когда человек решает вручную выбрать версию сайта.
$version = $modx->getOption('v', $scriptProperties, false);
if ($version == 'auto') {
unset($_SESSION['site_version']);
} else {
$_SESSION['site_version'] = $version;
}
$protocol = 'http://';
$host = $modx->getOption('HTTP_HOST', $_SERVER, 'quasi-art.ru');
if (substr($host, 0, strlen('www.')) === 'www.') {
$host = str_replace('www.', '', $host);
}
if (substr($host, 0, strlen('m.')) === 'm.') {
$host = str_replace('m.', '', $host);
}
$defaultUrl = $protocol.$host;
$desktopUrl = $protocol.$host;
$mobileUrl = $protocol.'m.'.$host;
switch ($version) {
case 'desktop':
// Перенаправление на настольную
$modx->sendRedirect($desktopUrl, array('type' => 'REDIRECT_META'));
break;
case 'mobile':
// Перенаправление на мобильную
$modx->sendRedirect($mobileUrl, array('type' => 'REDIRECT_META'));
break;
default:
$modx->sendRedirect($modx->makeUrl(1), array('type' => 'REDIRECT_META'));
break;
}
Ручное переключение версий
Любой посетитель запросто может поменять версию сайта, перейдя по специальной ссылке. Для этого я создал три ресурса (не обязательно, но можно сгруппировать их под другим ресурсом, как на рисунке). Первый ресурс для переключения на настольную версию, второй — мобильную, третий — для сброса настроек и автоматического определения.
Первый ресурс содержит следующий вызов:
[[!SwitchToSiteVersion? &v=`desktop`]]
Второй ресурс:
[[!SwitchToSiteVersion? &v=`mobile`]]
Третий
[[!SwitchToSiteVersion? &v=`auto`]]
У ресурсов рекомендую устанавливать пустой шаблон:
[[*content]]
Cookies
Чуть не забыл одну важную вещь — cookies для разных доменов будут изолированы. Чтобы исправить это, в настройках системы нужно изменить параметр session_cookie_domain
. Он должен содержать доменное имя сайта с точкой в начале.
Готовые решения
Есть и готовые решения: https://rtfm.modx.com/extras/revo/modmobile. Только дата последнего релиза была более 10 лет назад :(
Вывод
Не скажу, что решение идеальное и универсальное, но уже прошло испытание на одном сайте. Единственной нерешённой проблемой является то, что кэшируется только одна версия страницы (которая загрузилась первой). Отображается, конечно, всё нормально, иначе я бы не поделился своим решением. Допустим, если сначала на страницу зашли с мобильного устройства, она закэшируется и количество запросов к БД и время генерации спадёт, но только для тех, кто будет заходить с мобильного устройства. Если человек будет заходить с настольного компьютера на эту же страницу, то количество обращений к БД и время генерации страницы спадать не будет. По крайней мере, именно эта печальная ситуация обнаружилась на первом подопытном.