17 авг. 2013 г., 2:29
MODX::findResource(). Поиск id ресурса по УРЛ. Маленькое звено большой истории.
Данный топик является для меня не только важным по содержанию, но и очень интересным в том плане, что является как бы законченной мозаикой, которая собиралась более двух лет (с апреля 2011-го). Тогда это была моя первая статья на community.modx-cms.ru (и последняя на ближайшие полтора года). В ней я писал про естественные ограничения MODX Revolution на уровне кеша (оригинал с картинками), а так же разгонял MODX до 1 000 000 документов, и как показывает loadimpact, сайт стабильно держал нагрузку хоть на одном, хоть на 50-ти пользователях одновременно. Собственно, благодаря еще тем давним исследованиям я и продолжаю сидеть на MODX Revolution (а не откатываюсь к MODX Evolution или перехожу на какой-то другой фрейм), потому что четко знаю, что в управлении у меня сайт всегда будет удобен, а производительность меня не будет беспокоить даже на крупных сайтах.
Так вот, почему же я говорю, что эта статья — старт для моей мозаики, которая собралась только сегодня? Сейчас я все расскажу, включая и краткий обзор парочки попутных событий.
В той статье я предлагал собственный способ решения проблемы большого кеша: я предлагал ввести такой параметр ресурса «не включать в кеш карты ресурсов». То есть это примерно тоже самое, что параметр «не показывать в дереве ресурсов» (show_in_tree), только там документ не выводится в дерево ресурсов, чтобы дерево не делало браузер плакать (когда документов очень много в одном уровне, и браузер просто физически не может прорендерить дерево), а здесь этот параметр бы позволял не делать выборку документов из базы данных при формировании кеша карты ресурсов, что значительно снизило бы нагрузку и на базу данных, и на сам MODX. Сразу скажу, что это не реализовано в официальной версии MODX-а, но кое что все-таки у нас появилось в последних версиях MODX-а, и как раз об этом я и хочу рассказать.
В общем, эксперимент экспериментом, но без реального проекта конечно окончательных выводов сделать нельзя, и в силу того, что у меня длительное время не было крупного проекта (кроме еще парочки небольших экспериментов), по сути эта технология была заморожена, и в данном направлении я больше не двигался. Но, как я не раз уже говорил, в начале года я получил заказ на крупный интернет-магазин (свыше 13 000 товаров), и этот проект дал толчок для старта сразу нескольких исследований и разработок, включая разработку shopModx и докручивание phpTemplates и modxSmarty. То есть само собой эти пакеты не просто так появились, а создавались именно для решения тех задач и проблем, с которыми мы сталкиваемся на крупных проектах. Кстати, по просьбе заказчика я особо не афиширую этот магазин (который является еще и двойным — один магазин — розничный, а другой — оптовый, каждый со своей структурой и своим оформлением, но тем не менее оба крутятся на одном движке и с единым каталогом с оптовыми и розничными ценами), но розничный магазин я могу показать: www.factum-doors.ru. Сразу отмечу, что дизайн, верстка и почти полный программинг розничного магазина выполнял специалист со стороны заказчика. А вот первичное программирование всего движка и оптового магазина мы делали вместо. То есть после того, как был создан оптовый магазин, программирование розничного магазина уже не составляло проблем. Все процессоры уже имелись, а modxSmarty с легкостью позволяет для разных контекстов указать разные Smarty-шаблоны. А на уровне шаблонов уже и процессоры разные можно вызывать, и параметры разные передать, и логику вывода поменять. Так что получилось реально два разных сайта на одном движке и не едином каталоге и наборе процессоров. Можете мне поверить что из коробки на MODX-е, используя стандартные шаблоны, чанки и сниппеты, это сделать было бы на несколько порядков сложнее. При этом, как вы можете убедиться, сайт работает довольно быстро. То есть помимо удобства в разработке и сопровождении, мы получаем и хорошие показатели по производительности.
И вот про этот магазин я не просто так говорю (и не рекламы ради). Это одно очень важное звено во всей этой истории. Дело в том, что я его разрабатывал еще во времена MODX Revolution 2.2.6. И вот тогда я зная какие ожидаются проблемы с кешем, решил сделать следующее: не вводить довольно сложный и хардкорный допфункционал с этой галочкой «не включать в кеш карты ресурсов», а использовать свою недавнюю наработку cacheOptimizer. Это не просто пакет, а системный патч. А родился он в результате вот этих исследований кеширования системных настроек и контекстов. Данный патч заменяет системный файл modContext-а и меняет механизм запроса документов из базы данных, которые в дальнейшем используются для формирования карты ресурсов. Можете довольно смело его ставить, так как опция включается через системные настройки, а при деинсталлизации пакета системный файл восстанавливается из бекапа (выполнено на уровне пакета). В общем, установив этот патч и отключив кеширование карты ресурсов я получил совсем маленьгий системный кеш, который нес в себе все системные настройки и т.п., но не кешировал карту ресурсов, что позволяло вообще не переживать за объем кеша на большом количестве ресурсов. Все это дело я очень подробно описывал в этом топике. И вот тогда-то я и столкнулся со следующей проблемой — 404 Not Found для всех внутренних страниц (об этом так же написано в том же топике). Происходило это по следующей причине: при заходе на страницу поиск ресурса по адресной строке modRequest выполнял с помощью как раз этого кеша карты ресурсов ($modx->aliasMap), без каких-либо запросов к БД. А так как я отключил кеширование карты ресурсов, то MODX просто ничего не знал о своих страницах. Тогда я не остановился перед этой проблемой, а написал свой расширяющий modRequest класс ShopmodxRequest, который до сих пор имеется в составе shopModx-а (на всякий случай). Он как раз и выполнял поиск ресурсов по УРЛ-у. Для себя он использовал кеш-провайдер, чтобы хранить id-шники уже найденных ресурсов, чтобы не дергать каждый раз базу данных. Учитывая, что использовался memcache, не приходилось переживать за избыточный кеш и проблемы со сбросом кеша, которые имеются в случае использования стандартного файлового кеш-провайдера.
Плюс к этому и еще одна проблема имелась — сломался $modx->makeUrl(), то есть тег [[~id]] просто перестал работать по той же причине — нет карты ресурсов. Для решения этой проблемы я написал небольшой расширяющий modParser-класс. Кстати, сразу хочу сказать, что над стандартным modParser предстоит еще серьезно поработать, чтобы расширять его было удобно, так как в данном случае для того, чтобы просто указать другой класс для обработки тегов-ссылок, пришлось переопределить целый метод на 100 с лишним строк. В общем, этот модифицированный парсер обрабатывал теги [[~id]] новым классом, и эта проблема так же разрешилась. И в целом все работало и все было ОК.
А вот недавно я взялся за разработку магазина на 150 000+ товаров. И там я так же использовал свой cacheOptimizer, чтобы отключить карту ресурсов. Но каково было мое удивление, когда я обнаружил, что MODX не потерял свои страницы при заходах, а так же продолжил корректно обрабатывать теги [[~id]]. Ведь я точно знал, что раньше он этого делать не мог, и даже где-то об этом написал. И вот сейчас мы подошли к самому главному… Сегодня мне надо было написать небольшой роутер на этом большом магазине на событие OnPageNotFound, а так как у нас карта ресурсов отключена, то само собой встал вопрос о том, чтобы искать ресурс по УРЛ-у в самой базе данных. Но мне стало интересно, а как же сам MODX выполняет этот поиск? В поисках ответа я полез в modRequest, и вот что я там нашел:
if ($method == 'alias') { $resourceId = $this->modx->findResource($identifier); }
То есть он использует метод modX::findResource(). Но я точно знаю, что его там раньше не было (не было в modRequest, и имел подозрения, что его не было и в modX). Конечно, я сразу увязал все эти новшества с главной фишкой MODX Revolution 2.2.7 — опциональным отключением кеширования карты ресурсов, но это не суть, я все равно этот момент распишу чуть подробней, так как есть еще один довольно важный момент.
Вот код этого метода:
public function findResource($uri, $context = '') { $resourceId = false; if (empty($context) && isset($this->context)) $context = $this->context->get('key'); if (!empty($context) && (!empty($uri) || $uri === '0')) { $useAliasMap = (boolean) $this->getOption('cache_alias_map', null, false); if ($useAliasMap) { if (isset($this->context) && $this->context->get('key') === $context && array_key_exists($uri, $this->aliasMap)) { $resourceId = (integer) $this->aliasMap[$uri]; } elseif ($ctx = $this->getContext($context)) { $useAliasMap = $ctx->getOption('cache_alias_map', false) && array_key_exists($uri, $ctx->aliasMap); if ($useAliasMap && array_key_exists($uri, $ctx->aliasMap)) { $resourceId = (integer) $ctx->aliasMap[$uri]; } } } if (!$resourceId && !$useAliasMap) { $query = $this->newQuery('modResource', array('context_key' => $context, 'uri' => $uri, 'deleted' => false)); $query->select($this->getSelectColumns('modResource', '', '', array('id'))); $stmt = $query->prepare(); if ($stmt) { $value = $this->getValue($stmt); if ($value) { $resourceId = $value; } } } } return $resourceId; }
Да, здесь видно, что если кеширование карты ресурсов отключено и id-шник ресурса не был найден, то он выполняет запрос к базе данных. Правда здесь сразу недоработка на мой взгляд — если не указан контекст, то он использует текущий. А на мой взгляд надо бы просто искать без учета контекста, или как минимум делать это когда включена настройка сквозной работы с контекстами (такая имеется в MODX-е).
Так что же мне здесь стало более всего интересно? А интересно стало то, что упоминаний об этой новой функции по сути нигде не было! И хотя эта функция в коде класса modX находится на 900-ой строчке, она не является старой, а была введена в марте этого года как раз в версии 2.2.7 При этом ее нет ни в официальной документации, ни в релизах, ни даже в changelog. Лишь только в версии 2.2.8 в changelog-е говорится про багфикс этой функции, но введена функция была в 2.2.7, и тогда про нее ничего не было сказано.
Почему я сейчас делаю такой акцент на том, что была введена какая-то функции, и про нее ничто не рассказали? Подумаешь, невидаль какая-то… А делаю я это потому, что основываясь на своем опыте, я хочу сказать, что это одна из очень важных новых функций API MODX-а. Это тот кирпич, который используется в строительстве фундамента, а не каркаса зданий (да, я знаю, что фундамент как правило заливают битоном, но учитывая то, что принято говорить про кирпичики, позвольте использовать именно такую метафору.). То есть это не та функция, которая просто дает какую-то фишка в каких-то узкопрофильных надстройках. Это часть основы движка. Чтобы вы понимали, это примерно тоже самое, как когда-то у нас были простые процессоры (в которых просто писался php-код для поточного выполнения через include), а потом разработали и ввели новые «классные» процессоры, что по сути обеспечило невероятный прорыв для MODX-а. Так что произошло во времена ввода новых процессоров? Прикол заключился в том, что официальная документация по этим процессорам просто не была написана. То есть процессоры ввели, а написать про них не написали. И как тогда народ про них узнал? Mark Hamstra тогда что-бы хоть как-то исправить эту ситуацию, написал заметку про них в своем блоге, а bezumkin сделал очень вольный перевод этого дела, и взял эти процессоры на вооружение. Затем bezumkin рассказал о «классных» процессорах мне, и хотя он и утверждает, что я к нему за помощью обращался, он спутал обсуждение найденного мной бага и обращение за помощью, так как я только хотел узнать его мнение по поводу использует ли он обычное require_once для подгрузки процессоров, или имеется какой-то особый способ, на что его «помощь» выразилась во фразах типа, цитирую «У тебя видно дохера времени свободного».
Но не суть кто кому что помог или не помог. Суть в том, что осваивать все это приходилось, ковыряя ядро самому. То есть, на мой взгляд, очевиден огромный пробел в плане документации. К примеру, лично я считаю, что самая классная документация у ExtJS. Она четко структурирована и даже примерчики имеются. То есть на мой взгляд, мы имеем систему, которая намного впереди идет реального уровня ее освоения. В отсутствие документации большинство MODX-еров почти не знают MODX-а. И получается, что хотя система реально развивается (и весь этот топик как раз и писался для того, чтобы показать, что MODX не стоит на месте, что даже проблемы двухлетней давности не забываются, что появляются нововведения, которые реально делают MODX более продвинутым в практическом плане), фактически MODX не получает заслуженных позиций на мировом рынке просто в силу слабой освещенности. Под этим углом сразу вспоминается 1С со всеми своими тренингами, сертификациями и т.п.
А резюме будет таким: если кто-то думает, что он перейдет на другой движок/фрейм и т.п., и все его проблемы решатся, а все сайты будут всегда летать и всегда будут номинированы на звание лучшего сайта года, я думаю, он жестоко ошибается. Везде есть свои плюсы и минусы. И чтобы на новом движке суметь их определить, так же придется его очень серьезно изучать. Так вот, не стоит прыгать с движка на движок. MODX — отличная система, и она развивается и обязательно будет развиваться и дальше. И надо всего лишь больше ее изучать. И тогда все у вас будет классно, и сайты будут быстро работать, и функционал любой и т.д. и т.п.
Вот как-то так…