Тест вложенности ресурса в контейнер [с блекджеком и плюхами]

Проверить, есть ли контейнер в списке родителей ресурса, можно разными способами:

  • Можно сделать это используя карту ресурсов, которую MODX создаёт для каждого контекста после обновления кеша. Для этого необходимо воспользоваться методом $modx->getParentIds. Таким образом нагрузка и время затрачиваемое на проверку будет минимальным.

  • Не заморачиваться и сделать всё исключительно на Fenom, без модификаторов. Тут мы тоже можем воспользоваться методом $_modx->getParentIds, который Василий услужливо вынес в список доступных в Fenom.

  • Можно даже извратиться до того, что на каждую такую проверку получать объект через getObject('modResource') + получать объекты его родителей через getOne('Parent'). К слову, что будет в случае, если нам, к примеру, надо проверить 50 товаров на странице на вложенность в определённую категорию?
    Ради интереса, я воспроизвёл подобную ситуацию на тестовом сайте Modhost:
    1. Без каких-либо проверок 50 товаров в списке категории выводятся за 0,0282 сек.
    2. С подобной проверкой, за 0.1304 сек.
    3. С проверкой, которую мы рассматриваем в этом посте, за 0.0307 сек.

    А потом заказчики сходят с ума, почему их сайт так долго открывается...

Поймите меня правильно

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

Преимущества данного решения

В чём же преимущества данного решения в отличие, к примеру, от этого:
  1. Скорость работы и низкая нагрузка на сервер,
  2. Возможность передавать опции, вроде ключа контекста,
  3. Возможность указать уровень вложенности проверки,
  4. Основной параметр проверки (id контейнера) не вшит в сниппет, а указывается извне.

Сниппет-модификатор проверки вложенности ресурса в контейнер

Каким же должен быть правильный сниппет-модификатор проверки вложенности ресурса в контейнер:
// Parent
$parent = ($options['parent'] ?: null);
unset($options['parent']);

if (empty($input) || empty($parent)) {
    return null;
}

// Height
$height = ($options['height'] ?: 10);
unset($options['height']);

// Alt keys
foreach (array(
    'context_key' => 'context',
    'ctx' => 'context',
) as $k => $v) {
    if (isset($options[$k])) {
        $options[$v] = $options[$k];
    }
}

if (!$parents = $modx->getParentIds($input, $height, $options)) {
    return null;
}

return in_array($parent, $parents) ?: null;
Создаём сниппет inParent с этим кодом.

Пример использования

Использовать его до безобразия просто:
{if ($_modx->resource['id'] | inParent : [
    'parent' => 2,
    'height' => 3,
    'ctx' => 'web',
])?}
    Родитель с id 2 найден на 3 уровня вверх в контексте web.
{else}
    Не найден.
{/if}

P.S.: За то, что так скрупулёзно отношусь к нагрузке на сервер и скорости работы скрипта, спасибо Михаилу Воеводскому, что ткнул носом однажды. =)
Павел Гвоздь
26 апреля 2017, 05:28
modx.pro
16
3 802
+15
Поблагодарить автора Отправить деньги

Комментарии: 8

Николай
26 апреля 2017, 09:37
+4
Спасибо за труд, очень правильно, что написали.
Сам внимательно отношусь к нагрузке на сервер и скорости загрузки, т.к. как покупатель не люблю тормозные сайты.

Сильно не хватает таких постов с разбором внутренних механизмов кеширования модх.

Хотя про getParentids и getchildIds все должны знать.

Есть еще полезный findResource
В вот так нужный getResourceURI так и не появился(( forums.modx.com/index.php/topic,58207.msg332426.html
    Николай
    27 апреля 2017, 09:55
    1
    +4
    Не стал создавать отдельный топик.

    Вот пример как делают настоящие хардкорные программисты для получения тайтла родителя верхнего уровня:

    if(!$level_1){
                    	$parentID = @$modx->resource->get('parent');
        				$document = @$modx->getObject('modResource',array(
            				'id' => $parentID,
        				));
                    	$parentID = @$document->get('parent');
            			$document = @$modx->getObject('modResource',array(
                			'id' => $parentID,
            			));
            			$parentID = @$document->get('parent');
            			$document_parent = @$modx->getObject('modResource',array(
                			'id' => $parentID,
            			));
            			$level_1="";
            			if($document_parent) {
                			$level_1 = $document_parent->get('pagetitle');
                			$level_1= mb_strtolower($level_1, 'UTF-8');
            			};
                    };
    Сегодня получил этот код от своих тру сеошников
      Sergey Leleko
      27 апреля 2017, 11:48
      +2
      Ничего себе у вас SEOшники код пишут ))
        Евгений Шеронов
        27 апреля 2017, 12:00
        0
        Но это опять же долгий способ с получением нескольких объектов, да ещё и не универсальный.
        Хотя даже с получением объектов можно было сделать цикл, где выискивался бы самый верхний родитель.

        Подходить лишь для такой структуры:
        Родитель 1
        — Родитель 2
        — Родитель 3
        — Ресурс

        P.S. Как я думаю, супер скорость будет при getParentIds и запроса newQuery с лимитом 1 и выборкой pagetitle.
          Николай
          27 апреля 2017, 16:47
          0
          Я как антипример и дал.
          Вот кусочек кода, который сам использую для вытаскивания всех тайтлов родителей просто для примера:
          $parent_ids=$this->modx->getParentIds($id, $level, array('context' => $context));
                $where = array(
                  'id' => $parentIds
                  );
                  array_push($parent_ids,$id); 
                  if(is_array($parent_ids)&&count($parent_ids)>0) {
                      $p_query = $this->modx->newQuery('modResource');
                      $p_query->select(array('id','pagetitle'));
                      $p_query->distinct();
                      $p_query->sortby('id', 'asc');
                      $p_query->where(array('id:IN' => $parent_ids));            
                      if ($p_query->prepare() && $p_query->stmt->execute()){     
                          $p_out= array();        
                         // $this->modx->log(modX::LOG_LEVEL_ERROR, $p_query->toSql());        
                          $p_out=$p_query->stmt->fetchAll(PDO::FETCH_ASSOC);                
                          //$this->modx->log(modX::LOG_LEVEL_ERROR, $p_out); 
                          $titles_array=array();
                         
                          foreach ($p_out as $val){                
                           $titles_array[]=$val['pagetitle'];         
                          }
                      }
                  }
          Сейчас хочу в функцию его завернуть, чтобы можно было конкретный тайтл конкретного родителя с нужного уровня тащить, например.
        Сергей Шлоков
        27 апреля 2017, 18:01
        +4
        Внесу свои 5 копеек в оптимизацию кода.

        1. Самая первая переменная называется $parent, а проверяется $parent_id. Явная опечатка.
        2. Блок с Alt key непонятно зачем нужен. Достаточно просто указать ключ context вместо ctx и context_key. Думаю, программисту это будет не сложно.
        3. В коде
        if (!$parents = $modx->getParentIds($input, $height, $options)) {
            $parents = array();
        }
        можно сразу возвращать null. Зачем тратить время на дальнейшие операции.

        П.С. Ещё желательно, чтобы переменная height была необязательная. Она явно ограничивает применение фильтра.
          Павел Гвоздь
          27 апреля 2017, 18:04
          0
          1, 3 — полностью согласен!
          2 — это для личного удобства. Постоянно путаю, где указать context_key, а где context.
            Павел Гвоздь
            27 апреля 2017, 18:06
            0
            Скорректировал код, спасибо! Приятно, когда твой код кто-то читает. ;)

            П.С. Ещё желательно, чтобы переменная height была необязательная. Она явно ограничивает применение фильтра.
            Тут не понял. Разве сейчас она обязательна?
            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
            8