Получение модулей одним запросом

1143
Продолжим начатое, но чуть менее затратно.

Суть проста: зачем мы в магазине будем заставлять нам нести продавца все печеньки, если нам не нравятся диетические. В переводе значит, что мы прямо в запросе к БД будем проверять доступность модуля группе юзера, тем самым уменьшая нагрузку и экономя трафик с БД.
Как видно на демо, для главной страницы оптимизированной версии при дефолтной установке делается на 6 запросов к БД меньше, чем стоковой версии. Чтобы это увидеть поменяйте в блоке справа шаблон: "По умолчанию" и "_default_" - это неоптимизированная версия, "new_default" - оптимизированная. На время генерации можете не смотреть - мускул кэширует запросы сам.

Приступим к выполнению ключевого различия с этим.
1. Выполним запросы к БД:
Код SQL:
  1. UPDATE cms_modules SET access_list = REPLACE (access_list, '- ', '- allow');
  2. ALTER TABLE `cms_modules` ADD FULLTEXT(`access_list`);
2. Файл /admin/applets/modules.php
Найти:
Код PHP:
  1. 389 и 470 $access_list = $inCore->request('allow_group', 'array_int');
Заменить:
Код PHP:
  1. $access_list = $inCore->request('allow_group', 'array');
Найти:
Код PHP:
  1. 942 echo '<option value="$group['id'].'"';
  2. if ($do=='edit' && $mod['access_list']){
  3. if (inArray($access_list, $group['id'])){
Заменить:
Код PHP:
  1. 942 echo '<option value="allow'.$group['id'].'"';
  2. if ($do=='edit' && $mod['access_list']){
  3. if (inArray($access_list, 'allow'.$group['id'])){
3. Теперь открываем /core/classes/page.class.php и делаем также немаловажное различие:
после:
Код PHP:
  1. public $captcha_count = 1;
вставляем:
Код PHP:
  1. private static $modules;
Ну и вновь перед последней закрывающейся скобкой вставляем:
Код PHP:
  1. public function countThisModules($position){
  2. if (self::$modules === null)self::$modules = self::getThisModules();
  3. return count(self::$modules[$position]);
  4. }
  5. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  6. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  7. private function getThisModules(){
  8.  
  9. $inDB = cmsDatabase::getInstance();
  10.  
  11. //Получаем id пункта меню
  12. $menuid = cmsCore::getInstance()->menuId();
  13. //Проверяем позиции
  14. $strict_sql = !cmsCore::getInstance()->isMenuIdStrict() ? 'AND (m.is_strict_bind = 0)' : '';
  15. //Проверяем доступ
  16. $allow_sql = !cmsUser::getInstance()->is_admin ? "AND (MATCH (m.access_list) AGAINST ('+allow".cmsUser::getInstance()->group_id."' IN BOOLEAN MODE) OR m.access_list = '')" : '';
  17. //Получаем все разрешённые данному юзеру модули для этой страницы
  18. $sql = "SELECT *, m.id as mid, m.template as tpl
  19. FROM cms_modules m, cms_modules_bind mb
  20. WHERE m.published = 1 AND
  21. m.id = mb.module_id AND
  22. (mb.menu_id = '$menuid' OR mb.menu_id = 0)
  23. $strict_sql
  24. $allow_sql
  25. ORDER BY m.ordering ASC";
  26.  
  27. $result = $inDB->query($sql);
  28. if(!$inDB->num_rows($result)){ return false; }
  29. while ($mod = $inDB->fetch_assoc($result)){
  30. $mods[$mod['position']][] = $mod;
  31. }//while
  32.  
  33. return $mods;
  34. }
  35. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  36. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  37. public function printThisModules($position){
  38.  
  39. $inCore = cmsCore::getInstance();
  40. global $_LANG;
  41.  
  42. // Проверяем позиции
  43. if($position=='top' && @$_REQUEST['view']=='search') { return true; }
  44. if(!self::countThisModules($position))return false;
  45.  
  46. foreach(self::$modules[$position] as $mod){
  47. $modulefile = PATH.'/modules/'.$mod['content'].'/module.php';
  48. if (!$mod['user']) { cmsCore::loadLanguage('modules/'.$mod['content']); }
  49. if( !$mod['is_external'] ){
  50. //PROCESS FILTERS
  51. $filters = $inCore->getFilters();
  52. if ($filters){
  53. foreach($filters as $id=>$_data){
  54. require_once PATH.'/filters/'.$_data['link'].'/filter.php';
  55. $_data['link']($mod['content']);
  56. }
  57. }
  58. $callback = true;
  59. $modulebody = $mod['content'];
  60. }
  61.  
  62. if( $mod['is_external'] ){
  63. if (file_exists($modulefile)){
  64. //load module file
  65. require_once $modulefile;
  66. //run module and get its output to $modulebody
  67.  
  68. if ($mod['cache'] && $inCore->isCached('module', $mod['mid'], $mod['cachetime'], $mod['cacheint'])){
  69. $modulebody = $inCore->getCache('module', $mod['mid']);
  70. $callback = true;
  71. } else {
  72. $config = $inCore->yamlToArray($mod['config']);
  73. $inCore->cacheModuleConfig($mod['module_id'], $config);
  74.  
  75. $callback = $mod['content']($mod['module_id']);
  76. $modulebody = ob_get_clean();
  77. if($mod['cache']) { $inCore->saveCache('module', $mod['mid'], $modulebody); }
  78. }
  79. }
  80. }
  81.  
  82. if ( $callback ){ //if module returns TRUE
  83. $module = array();
  84. $mod['body'] = $modulebody;
  85. $smarty = $inCore->initSmartyModule();
  86. if (cmsConfig::getConfig('fastcfg') && cmsUser::getInstance()->is_admin){
  87. $smarty->assign('cfglink', '/admin/index.php?view=modules&do=edit&id='.$mod['mid']);
  88. }
  89. $smarty->assign('mod', $mod);
  90. $module_tpl = file_exists($smarty->template_dir.'/'.$mod['tpl']) ? $mod['tpl'] : 'module.tpl';
  91. $smarty->display($module_tpl);
  92. }
  93. }//foreach
  94. }
  95.  
  96. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  97. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
4. Ну и наконец в своём шаблоне (файле template.php) меняем countModules на countThisModules и printModules на printThisModules.

В следующий раз мы изменим printThisModules для другого метода кэширования, но можете даже не надеяться на появление этого в дистрибутиве, ибо у разработчиков совершенно иной взгляд на саму задачу кэширования)
Как именно будет кэшироваться? А очень просто: будут создаваться файлы кэша с простым html или же массивом данных. Это будет на усмотрение модуля, да, кешировать себя будет модуль сам, в зависимости от его приватности и каких либо изменений для каждого пользователя. Например модуль форума: в зависимости от доступа, он может отображать разные темы разным группам (по крайней мере в моей доработке =D), а также у каждого конкретного пользователя разные прочтённые темы, поэтому мы не можем просто взять и закэшировать его. У модуля форума будет по одному файлу на каждую группу, в каждом будет массив, который модуль будет перебирать и отмечать прочтённость топика данным конкретным юзером, а потом отдавать смарти. Хм, что же у нас не для разных групп... Ах, да - Лента. У неё будет один файл для всех групп, в котором просто готовый html.
Я не решил как чистить кеш, либо плагином по эвентам, либо классом логирования, благо всё что можно сделать во фронте, отображается в Ленте.
Ещё важный момент с датами: они просто будут окружаться спаном с timestamp, по которому в закэшированом html легко поменять "5 минут назад", на "вчера во столько то" и ещё с помощью яваскрипта можно будет оживить эти самые "n минут назад" на сайте).
v

UPDATE: патч.
Аякс подбор города профиля | Настраиваемый скрытый текст
Комментарии (5)
Роман 2 декабря 2012 в 19:56 +1
Огромный + за оптимизацию
Anonim 3 декабря 2012 в 14:20 0
Здесь полностью инструкция приведена по установке? Или сначала нужно установить "то"?
lokanaft 3 декабря 2012 в 14:23 0
Полностью.
qwest 3 декабря 2012 в 18:19 0
Скажите а на демо только эта оптимизация или есть еще?
Очень шустро открываются странички.
lokanaft 3 декабря 2012 в 18:35 0
Хостинг такой.