Добрый день. Я занимаюсь web-разработкой и почти всем, что с этим связано.

MODX: мобильная версия сайта

Введение

В общем, без лишних слов о достоинствах мобильной версии сайта, я расскажу о том, как я реализовал её на базе MODX Revolution. Ранее я рассказывал о том, как создать мобильную версию сайта на CodeIgniter. В принципе, ничем кардинально подходы к организации мобильной версии в обоих фреймворках не отличаются.

Задача

Вкратце задача следующая: основной сайт доступен по адресу site.ru, мобильная версия — m.site.ru. Если человек заходит на m.site.ru с ПК, то его перенаправляем на site.ru. Если человек заходит с мобильного устройства на site.ru, то он автоматически перенаправлется на m.site.ru. Не перенаправляется человек в единственном случае — если он сам решил, какую версию ему показывать. Естественно, всё это в рамках одной установки MODX. Таким образом, оба домена ссылаются на один и тот же каталог с установленным MODX.

Шаблоны

Обычно при правильном проектировании сайта на MODX количество шаблонов минимально. В моём случае не пришлось создавать отдельные шаблоны для полной и мобильной версии (учитывая, что различаться версии должны только оформлением, но не содержимым). Однако, внутри шаблонов чанки вызываются особым образом. Дело в том, что имя чанка создаётся динамически на основе того, какую версию сайта нужно отобразить. Например, у меня есть два чанка: header и headerMobile. В шаблоне вызывается это дело следующим образом: . Если сниппет SiteVersion возвращает пустую строку, то отображается чанк header. Если же сниппет возвращает строку Mobile, то в шаблоне вызывается чанк headerMobile (происходит конкатенация имени чанка и вывода сниппета).

Ниже показан пример шаблона главной страницы. В самом начале вызывается некэшированный сниппет siteVersionRedirect, который решает, нужно ли перенаправить (redirect) посетителя на другую версию этой страницы. Например, если посетитель впервые зашёл на этот сайт (например, site.ru/articles/how-to-do-it) с мобильного устройства, то сниппет его перенаправит на мобильный адрес этой страницы (m.site.ru/articles/how-to-do-it).


[[!siteVersionRedirect]]
<!DOCTYPE html>
<html>
<head>
[[$head[[!SiteVersion]]]]
</head>
<body>
    <div class="wrapper">
        <div class="container">
            [[$header[[!+site_version]]]]
            [[$navigation[[!+site_version]]]]
            [[$mainPart1[[!+site_version]]]]
            [[$popularNews[[!+site_version]]]]
            [[$advForm[[!+site_version]]]]
            [[$newsLine[[!+site_version]]]]
            [[$footer[[!+site_version]]]]
        </div>
        [[scripts[[!+site_version]]]]
    </div>
</body>
</html>

Сниппеты

Для функционирования этой системы я создал 4 простых сниппета. Не всё идально, есть некоторые проблемы с производительностью и чистотой кода, но я решил опубликовать хотя бы этот вариант.

SiteMobileBrowser

Сниппет анализирует User-Agent браузера и решает, принадлежит он мобильному устройству или нет. Возвращает true или false.


$userUA = $modx-&gt;getOption('HTTP_USER_AGENT', $_SERVER, false);
$uas = array('Android', 'iPad', 'iPod', 'iPhone', 'Mini');
$isMobile = false;
foreach ($uas as &amp;$ua) {
    if (substr_count($userUA, $ua) &gt; 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

Анализирует текущий адрес запроса и сессию пользователя, а затем, если требуется, перенаправляет посетителя на соответствующую страницу. Например, если человек с телефона в поисковой системе нашёл статью, адрес которой принадлежит настольной версии сайта (например, site.ru/news/google-rip), то сниппет перенаправит его на мобильную версию статьи (m.site.ru/news/google-rip).


$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. Он должен содержать доменное имя сайта с точкой в начале.

Объединить cookies для разных доменов
Объединить cookies для разных доменов

Готовые решения

Я не пробовал, но есть и готовые решения: http://rtfm.modx.com/extras/revo/modmobile .

Вывод

Не скажу, что решение идеальное и универсальное, но уже прошло испытание на одном сайте. Единственной нерешённой проблемой является то, что кэшируется только одна версия страницы (которая загрузилась первой). Отображается, конечно, всё нормально, иначе я бы не поделился своим решением. Допустим, если сначала на страницу зашли с мобильного устройства, она закэшируется и количество запросов к БД и время генерации спадёт, но только для тех, кто будет заходить с мобильного устройства. Если человек будет заходить с настольного компьютера на эту же страницу, то количество обращений к БД и время генерации страницы спадать не будет. По крайней мере, именно эта печальная ситуация обнаружилась на первом подопытном.

Комментарии