Одностраничники на базе MODx без контекстов


Доброго времени суток!
Стояла задача реализовать создание одностраничных сайтов с минимальными затратами по времени и ресурсам.
Моя реализация основана принципе создания одностраничного сайта в рамках стандартного ресурса MODx и подмены шаблона и содержимого главной страницы в зависимости от доменного имени сайта, по которому пользователь перешел.
Итак, суть решения:
  1. К каждому ресурсу, который представляет собой отдельный одностраничный сайт присваивается tv поле с доменным именем
  2. на событие OnLoadWebDocument вешается плагин, который получает текущий домен и находит ресурс,
    привязанный к данному домену, получает и применяет шаблон данного ресурса к главной странице, создает плейсхолдер [[+resid]], в котором хранится id нужного нам ресурса
  3. Выводится нужный нам контент в соответствии с плейсхолдером [[+resid]] в нужном нам шаблоне
Собственно сам плагин:
<?php
$id = $modx->resource->get('id');

if($modx->event->name == 'OnLoadWebDocument' ){
    $domain = $_SERVER['HTTP_HOST'];
    $q = $modx->newQuery('modTemplateVarResource', array('tmplvarid'=>1,'value'=>$domain));// 'tmplvarid'=>1 это id тв поля с доменом
    $q->select('contentid');
    $RESID = $modx->getValue($q->prepare());// получаем id ресурса соответствующего домену
    
    if($RESID){
        $modx->setPlaceholder('resid', $RESID);
        if(!in_array($id,array(3,4))){//отсекаем страницы роботса и сайтмап
            $res = $modx->getObject('modResource',$RESID);// 
            $template = $res->get('template');// сохраним id нужного ресурса для вызова в шаблонах
            $modx->resource->set('template', $template);//задаем шаблон вывода
            $modx->resource->save();
            $cm = $modx->getCacheManager();
            $cm->refresh();
        }
    }
}
Конечно можно просто создать для каждого сайта свой шаблон и не заморачиваться, но это не то, что нам нужно, поэтому весь контент в шаблонах вызывается через fastField: [[#[[+resid]].pagetitle]]

Если кому-то будет полезно — буду рад.
Поправил по замечаниям, спасибо что указали на ошибки
06 октября 2017, 11:24    Арсений   
8    470 +5

Комментарии (16)

  1. Евгений Шеронов 08 октября 2017, 18:08 # +2
    Вот такие конструкции:
    $data = "SELECT contentid FROM base_site_tmplvar_contentvalues WHERE tmplvarid = 1 AND value = '$domain'";
    Лучше писать более универсально:
    $q = $modx->newQuery('modTemplateVarResource');
    $q->where(array('tmplavarid'=>1,'value'=>$domain));
    $q->select('contentid');
    $q->limit(1); // один же ресурс ищем
    if($q->prepare() && $q->stmt->execute()) {
       if($RESID = $q->stmt->fetch(PDO::FETCH_COLUMN)) { // - один столбец для одной стройки
           // весь остальной код 
        }
    }
    
    1. Сергей Шлоков 09 октября 2017, 07:17 # +2
      Получить одно поле —
      $q = $modx->newQuery('modTemplateVarResource', array('tmplavarid'=>1,'value'=>$domain));
      $q->select('contentid');
      $RESID = $modx->getValue($q->prepare());
      
      1. Евгений Шеронов 09 октября 2017, 08:36 # 0
        Оказывается, вообще красиво можно — запомню :)
    2. Alexander V 09 октября 2017, 04:15 # 0
      Довелось недавно делать одностраничник на Modx. В итоге получилось:
      10+ ресурсов
      10+ TV
      20+ чанков
      1. Сергей Шлоков 09 октября 2017, 07:15 # +2
        "SELECT contentid FROM base_site_tmplvar_contentvalues WHERE tmplvarid = 1 AND value = '$domain'";
        Жесть. Кому нужен дырявый сайт, налетай.

        Стыдно должно быть, молодой человек!
        1. Арсений 09 октября 2017, 11:26 # 0
          а как в данном случае может проявиться уязвимость? этот скрипт запускается в плагине и, по сути из параметров принимает только домен. Как можно на этой дыре что-то сделать?
          Спасибо за критику!
          1. Василий Наумкин 09 октября 2017, 13:54 # +3
            Домен у тебя получается из $_SERVER['HTTP_HOST'], который при не очень хорошо настроенном сервере будет выдавать то, что прислал юзер.

            А прислать он может что угодно, включая длиннющую строку со слепой SQL инъекцией.
            1. Арсений 09 октября 2017, 17:19 # 0
              Но здесь же по сути переменная получает данные типа site.ru, то есть все остальные параметры просто не принимаются типа get параметров и прочего, или я не прав?
              1. Василий Наумкин 09 октября 2017, 17:26 # +1
                Ты не прав. HTTP_HOST присылает юзер в запросе.

                Если у сервера нет определённых настроек (которых по умолчанию и нет), тебе могут вместо site.ru прислать что угодно, включая SQL инъекцию.
                1. Арсений 09 октября 2017, 17:39 # 0
                  Спасибо! дополнительно ознакомлюсь с темой!
            2. Сергей Шлоков 09 октября 2017, 15:47 # +2
              Спасибо за критику!
              Ну если такое отношение к критике, то возьму на себя смелость разобрать код.
              1. Теперь на один просмотр страницы будет 2 лишних запроса к БД.
              2. Вот это
              $modx->resource->save();
              зачем? Кроме того, это ещё один запрос к БД.
              3. Вы понимаете, что после каждого просмотра вы убиваете весь кэш сайта?
              4. И наконец, эта странная фраза собака друг человека
              весь контент в шаблонах вызывается через fastField: [[#[[+resid]].pagetitle]]
              Это как интересно?

              Почему нельзя просто сделать форвард на найденный ресурс?

              Вообще, эта задача легко решается через контексты. И поддержка такого решения гораздо проще, ведь всё понятно. А вот для вашего решения нужна инструкция, ибо следующий программист вряд ли сходу поймёт ваше творчество.
              1. Арсений 09 октября 2017, 17:15 # 0
                2. $modx->resource->save(); без этого зачастую не срабатывала привязка шаблона, хз почему но с этим была беда
                3. Да, согласен, это лишнее
                4. А каким образом делается форвард на ресурс, при условии что url измениться не должен?

                Про контексты — когда стоит задача сделать систему такую, чтоб обычный юзверь, без дополнительных регистраций контекстов, мог зарегистрировать, наполнить и запустить еще пару тройку одностраничников, по готовым шаблонам — мне кажется так проще. Человеку просто нужно зайти и заполнить ресурс как обычно + добавить url в поле — больше ничего делать не нужно (ну кроме регистрации домена, но это уже не касается системы)
                1. Алексей 09 октября 2017, 17:41 # 0
                  modx.pro/development/2157-method-sendforward-and-save-the-fields-of-the-resource/
                  А вот контент поменять врятли удастся:
                  github.com/modxcms/revolution/pull/13163
                  Вообще это хороший вопрос про sendforward
                  1. Василий Наумкин 10 октября 2017, 12:39 # +5
                    Если покопаться в MODX API то можно научиться выводить любой ресурс в плагине на нужном событии:
                    $modx->resource = $modx->getObject('modResource', $modx->getOption('site_start'));
                    $this->modx->resource->set('content', $pdoTools->runSnippet('@FILE snippets/get_panel.php'));
                    $this->modx->request->prepareResponse();
                    
                    Здесь, как видно, еще и замена контента на свой идёт, на лету.

                    Можно так на лету и виртуальные ресурсы делать, со своими pagetitle и прочими свойствами. При желании, можно вообще весь сайт без ресурсов сделать =)
                    1. Арсений 10 октября 2017, 15:23 # 0
                      Хорошая штука:) Василий, а что скажете на счет такой реализации? Работать то она работает, но не является ли это каким-то велосипедом? Как-то привык доверять вашему мнению)
                      1. Василий Наумкин 10 октября 2017, 15:29 # +1
                        Ну а что здесь велосипедного?

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