Переключение контекстов в зависимости от URL

Периодически появляется задача — создать несколько контекстов и сделать так, чтобы нужный загружался для работы автоматически в зависимости от URL. Будем считать, что ЧПУ настроены. Более никаких изменений ни в .htaccess, ни в конфигах сервера. Ведь часто встречаются злые хостеры, которые не позволяют этого делать. А еще нежелательно создавать подкаталоги и копировать в них index.php, иначе после каждого обновления придется следить за его изменением.

Мы будем переключать контексты с помощью плагина на событие OnHandleRequest.

Есть 2 основных вида URL, которые надо обрабатывать:

  • context.site.ru

  • site.ru/context/

Поскольку между этими вариантами отличаются только плагин и три настройки контекстов, предварительно опишем общие действия:

  • Создать субдомен его направить на тот же каталог, где лежит основной сайт ИЛИ создать алиас для основного домена;

  • Создать контекст и следующие его настройки:

    • error_page = ID страницы для 404 ошибки в данном контексте

    • site_name = Название сайта для этого контекста

    • site_start = ID главной страницы в данном контексте

  • Создаем плагин с любым именем (например, swCtx), отмечаем галкой событие onHandleRequest.

Первый вариант очень простой, плагин имеет всего несколько строк.

Для соответствующего контекста создаем следующие настройки:

В нашем плагине пишем следующий код:

<?php
if ($modx->event->name == 'OnHandleRequest') {
    if ($modx->context->get('key') == 'mgr') {return;}
    // сравнение текущего имени хоста и искомого
    if (strstr($_SERVER['HTTP_HOST'], 'testen.a.aaa0.ru') != false) {
        $modx->switchContext('endomain');
        return;
    }
}

Для первого случая больше ничего делать не надо. Если контекстов на субдомены несколько, просто скопируйте нужное количество раз блок

    if (strstr($_SERVER['HTTP_HOST'], 'context.site.ru') != false) {
        $modx->switchContext('context');
        return;
    }

, заменив в нем название контекста и субдомена.

Больше ничего.

Со вторым вариантом чуть сложнее. Дело в том, что кроме переключения контекста необходимо дополнительно отправить пользователя через $modx->sendRedirect() на запрашиваемую страницу. А для этого ее надо еще и найти.

Настройки контекста:

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

<?php
if ($modx->event->name == 'OnHandleRequest') {
    if ($modx->context->get('key') == 'mgr') {return;}

    // получение первого каталога из URI
    preg_match('/^(\/[a-z0-9_-]*\/)/i', $_SERVER['REQUEST_URI'], $matches);
    $key = $matches[0];
    // сопоставление полученного названия каталога и пути для контекста
    switch ($key) {
    case '/ctxfolder/' : 
        $modx->switchContext('ctxfolder'); 
        $context = 'ctxfolder';
        // получение URI страницы относительно контекста
        $uri = preg_replace('/^\/ctxfolder\//i', '', $_SERVER['REQUEST_URI']);
        break;
    }
    if ($uri != '') {
     // если URI не пуст, ищем страницу в нужном контексте
    $res = $modx->getObject('modResource', array('context_key' => $context, 'uri' => $uri));
    // если страница существует, отправляем на нее. Если нет, вызываем 404
    if ($res) $modx->sendForward($res->get('id'));
    else $modx->sendErrorPage();
    }
    // если URI пуст, отправляем на стартовую страницу контекста
    else $modx->sendForward($modx->getOption('site_start'));
}

В таком виде все должно работать. Здесь не учитывается 403 ошибка, тк я, честно говоря, до сих пор не понял логику MODX в ее случае, ведь вместо нее создается видимость отсутствия ресурса, и соответственно выдается 404.

Возможно, приведенный код не является оптимальным. Но он отвечает на вопрос: как переключить контекст в зависимости от URL. И если в случае с субдоменом решений в интернете достаточно, то для второго случая без изменения исходников я видел только черновик в комментариях Василия.

P.S.: буду рад, если Василий напишет свои комментарии для улучшения этого мануала.

Обновлено

Плагин Василия для второго случая, когда контекст в субкаталоге, лаконичнее и правильнее:

<?php
if ($modx->context->key == 'mgr') {return;}

elseif ($modx->event->name == 'OnHandleRequest') {
    $var = $modx->getOption('request_param_alias', null, 'q');
    $request = $_REQUEST[$var];
    
    if (strpos($request, 'context/') === 0) {
        $modx->switchContext('context');
        
        $_REQUEST[$var] = substr($request, 8);
    }
}
← Предыдущая заметка
Решение: История просмотров на MODx Revo
Следующая заметка →
Вывод ТВ со своим оформлением
Комментарии (8)
bezumkinВасилий Наумкин
17.10.2013 12:39

У меня на дев-сайте второй контекст называется "context". Использую вот такой плагин:

<?php
if ($modx->context->key == 'mgr') {return;}

elseif ($modx->event->name == 'OnHandleRequest') {
    $var = $modx->getOption('request_param_alias', null, 'q');
    $request = $_REQUEST[$var];
    
    if (strpos($request, 'context/') === 0) {
        $modx->switchContext('context');
        
        $_REQUEST[$var] = substr($request, 8);
    }
}
mvoevodskiyВоеводский Михаил
17.10.2013 22:41

Предполагал такое решение, но не додумал.

Спасибо.

mvoevodskiyВоеводский Михаил
17.10.2013 22:44

Василий, а это баг или нет, что после редактирования пост снова попадает в "Вопросы"?

bezumkinВасилий Наумкин
18.10.2013 06:52

Это недоработка Tickets.

У тебя же нет права публиковать в другой блог, а компонент не умеет в таких случаях оставлять уже имеющийся parent. Вот и перекидывает куда можно.

Sergey Sinicin
22.01.2014 01:13

Был старый сайт на html. где ссылки: домен.ру/о нас.html- о нас русский домен.ру/эбаут ас.htmlо нас английский Перенес все на модх, создал русский и английский контексты. Установил бабел. А дальше потребовалось настроить так, что бы ссылки остались теми же.

Можно ли сделать так? ..... $var = $modx->getOption('request_param_alias', null, 'q'); $request = $_REQUEST[$var]; //далее команды пока не знаю модх-найти документ по урл $request ид контекста = модх-определить ид контекста по документу $modx->switchContext(ид контекста);

как-то так... все только для того чтобы сохранить старые ссылки.

bezumkinВасилий Наумкин
22.01.2014 08:24

Просто пропиши вручную нужные uri - Revolution это позволяет

Для поиска ресрсов по uri есть метод modX::findResource().

Sergey Sinicin
22.01.2014 10:44

Спасибо, Василий за наводку. При стандартном htaccess, вот такой плагин переключателя языков получился:

if($modx->context->get('key') != "mgr"){
    
    $param_url = $modx->getOption('request_param_alias', null, 'q');
    $url = $_REQUEST[$param_url];
    $resource = $modx->getObject('modResource', array('uri' => $url));
    $cultureKey = $resource->get('context_key');
    
     /* grab the current langauge from the cultureKey request var */
      switch ($cultureKey) {
..................................................

Не знаю на сколько это правильно ...

Sergey Sinicin
22.01.2014 11:05

if($modx->context->key != "mgr"){
    
    $param_uri = $modx->getOption('request_param_alias', null, 'q');
    $uri = $_REQUEST[$param_uri];
    $resource = $modx->getObject('modResource', array('uri' => $uri));
    $cultureKey = $resource->get('context_key');

    if ($cultureKey != $modx->context->key){
        $modx->switchContext($cultureKey);
    }

}
bezumkin
Василий Наумкин
09.04.2024 01:45
Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. Во...
futuris
Futuris
04.04.2024 05:56
Я просто немного запутался. Когда в абзаце &quot;Vesp/Core&quot; ты пишешь про &quot;новый trait Fil...
bezumkin
Василий Наумкин
20.03.2024 18:21
Volledig!
Андрей
14.03.2024 10:47
Василий! Как всегда очень круто! Моё почтение!
russelgal
russel gal
09.03.2024 17:17
А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал ...
inetlover
Александр Наумов
27.01.2024 00:06
Василий, спасибо! Извини, тупанул.
bezumkin
Василий Наумкин
22.01.2024 04:43
Давай-давай!
bezumkin
Василий Наумкин
24.12.2023 11:26
Спасибо!
bezumkin
Василий Наумкин
27.11.2023 02:43
Ура!
bezumkin
Василий Наумкин
25.11.2023 08:30
Vesp тянет 2 зависимости: vesp-frontent для фронта и vesp-core для бэкенда. Их можно обновлять, но э...