Хак: топ топиков из коллективных и персональных блогов отдельно

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

Вряд ли это кому-нибудь нужно, но выкладываю инструкцию, как можно повторить мою реализацию.
Расписываю все изменения подробно, чтобы всем было понятно, что за что отвечает :-)
Можно было по-быстрому поправить файлы шаблонов, я же предпочёл сделать всё более-менее чисто, дополнив ядро новыми функциями.
(тут я немного лукавлю — у себя в проектах я не дополнил движок функциями, а заменил своими старые :-) Вы можете поступить так же, тогда вы фактически не увеличите размер движка, а только дополните функциональность его core-функций. Делайте это, только если Вы действительно знаете, что делаете :-)

Итак, начнём.


/classes/modules/topic/mapper/Topic.mapper.class.php

Первым делом, добавляем в маппер core-модуля «topic» функцию, которая дублирует функционал core-функции «GetTopicsRatingByDate», а так же включает в себя возможность выборки по типам топиков. Последний аргумент ($Type) — массив с необходимыми типами топиков.
public function GetTopicsRatingByDateType($sDate,$iLimit,$Type=array('personal','open')) {
		$typestring="'".implode("','",$Type)."'"; //array=>string
		
		$iCurrentUserId=-1;
		if (is_object($this->oUserCurrent)) {
			$iCurrentUserId=$this->oUserCurrent->getId();
		}		

		/**
		 * оптимизирован
		 */
		$sql = "SELECT
					t_fast.*,
					tc.*,
					u.user_login as user_login,
					IF(tv.topic_id IS NULL,0,1) as user_is_vote,
					tv.vote_delta as user_vote_delta,
					IF(tqv.topic_id IS NULL,0,1) as user_question_is_vote,
					IF(ft.topic_id IS NULL,0,1) as topic_is_favourite
				FROM (
					SELECT 
						t.*,
						b.blog_title as blog_title,
						b.blog_type as blog_type,
						b.blog_url as blog_url										
					FROM 
						".DB_TABLE_TOPIC." as t,					
						".DB_TABLE_BLOG." as b 
					WHERE 					
						t.topic_publish = 1
						AND
						t.topic_date_add >= ? 								
						AND
						t.topic_rating >= 0
						AND
						t.blog_id=b.blog_id
						AND					
						b.blog_type in (".$typestring.")											
					ORDER by t.topic_rating desc, t.topic_id desc
					LIMIT 0, ?d 	
					) AS t_fast
					JOIN ".DB_TABLE_USER." AS u ON t_fast.user_id=u.user_id
					LEFT JOIN (
								SELECT
									topic_id,
									vote_delta												
								FROM ".DB_TABLE_TOPIC_VOTE." 
								WHERE user_voter_id = ?d
								) AS tv ON t_fast.topic_id=tv.topic_id
					LEFT JOIN (
						SELECT
							topic_id												
						FROM ".DB_TABLE_FAVOURITE_TOPIC." 
						WHERE user_id = ?d
					) AS ft ON t_fast.topic_id=ft.topic_id
					LEFT JOIN (
								SELECT
									topic_id																			
								FROM ".DB_TABLE_TOPIC_QUESTION_VOTE." 
								WHERE user_voter_id = ?d
								) AS tqv ON t_fast.topic_id=tqv.topic_id
					JOIN ".DB_TABLE_TOPIC_CONTENT." AS tc ON t_fast.topic_id=tc.topic_id 
					order by t_fast.topic_rating desc
					";
		
		$aTopics=array();
		if ($aRows=$this->oDb->select($sql,$sDate,$iLimit,$iCurrentUserId,$iCurrentUserId,$iCurrentUserId)) {
			foreach ($aRows as $aTopic) {
				$aTopics[]=new TopicEntity_Topic($aTopic);
			}
		}
		return $aTopics;
	}



/classes/modules/topic/Topic.class.php

Теперь, добавляем в модуль «topic» функцию, которая дублирует функционал ф-ии «GetTopicsRatingByDate», только включает в себя возможность выборки по типу топиков (и при этом правильно работает с кэшем). Последний аргумент ($Type) — либо строка с 1м типом топика, либо массив с >=1 типов.
public function GetTopicsRatingByDateType($sDate,$iLimit=20,$Type=array('personal','open')) {
		is_array($Type)?$typestring=implode('',sort($Type)):$Type=array($typestring=$Type); //new
		
		$s2=-1;		
		if ($this->oUserCurrent) {
			$s2=$this->oUserCurrent->getId();
		}
		if (false === ($data = $this->Cache_Get("topic_rating_{$typestring}_{$sDate}_{$s2}_{$iLimit}"))) {			
			$data = $this->oMapperTopic->GetTopicsRatingByDate($sDate,$iLimit,$Type);
			$this->Cache_Set($data, "topic_rating_{$typestring}_{$sDate}_{$s2}_{$iLimit}", array('topic_update'), 60*5);
		}
		return $data;		
	}



/classes/actions/ActionTop.class.php

Редактируем экшн «Top». Регистрируем в нём 2 эвента: «personal» и «theme».
protected function RegisterEvent() {		
		$this->AddEvent('blog','EventBlog');	
		$this->AddEvent('topic','EventTopic');
		$this->AddEvent('comment','EventComment');
		$this->AddEvent('personal','EventPersonal'); // Только личные
		$this->AddEvent('theme','EventTheme');	// Только тематические
	}

Добавляем функции эвентов:
protected function EventPersonal() {
		$this->sMenuSubItemSelect='personal';	
		$iTimeDelta=$this->GetTimeDelta();				
		$sDate=date("Y-m-d H:00:00",time()-$iTimeDelta);				
		$sType='personal'; // тип топиков
		$aTopics=$this->Topic_GetTopicsRatingByDateType($sDate,20,$sType);		
		$this->Viewer_Assign('aTopics',$aTopics);
		$this->Viewer_AddHtmlTitle('Топ топиков в личных блогах');					
	}

protected function EventTheme() {
		$this->sMenuSubItemSelect='theme';	
		$iTimeDelta=$this->GetTimeDelta();				
		$sDate=date("Y-m-d H:00:00",time()-$iTimeDelta);	
		$sType='open'; // тип топиков
		$aTopics=$this->Topic_GetTopicsRatingByDateType($sDate,20,$sType);		
		$this->Viewer_Assign('aTopics',$aTopics);
		$this->Viewer_AddHtmlTitle('Топ топиков в тематических блогах');					
	}



Добавляем 2 шаблона для новых эвентов. Создаём 2 новых файла:
/templates/skin/new/actions/ActionTop/theme.tpl
/templates/skin/new/actions/ActionTop/personal.tpl
и редактируем их содержимое. Это практически клоны файла «topic.tpl», лежащего в той же папке.

Содержимое theme.tpl
{include file='header.tpl' menu="blog" }


	<div class="topic top">
		<h1>{$aLang.top_topics_theme}</h1>				
		<ul class="block-nav">
			<li {if $aParams[0] and $aParams[0]=='24h'}class="active"{/if}><strong></strong><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/theme/24h/">{$aLang.blog_menu_top_period_24h}</a></li>
			<li {if $aParams[0] and $aParams[0]=='7d'}class="active"{/if}><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/theme/7d/">{$aLang.blog_menu_top_period_7d}</a></li>
			<li {if $aParams[0] and $aParams[0]=='30d'}class="active"{/if}><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/theme/30d/">{$aLang.blog_menu_top_period_30d}</a></li>
			<li {if $aParams[0] and $aParams[0]=='all'}class="active"{/if}><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/theme/all/">{$aLang.blog_menu_top_period_all}</a><em></em></li>
		</ul>
	</div>

	{include file='topic_list.tpl'}


{include file='footer.tpl'}


Содержимое personal.tpl
{include file='header.tpl' menu="blog" }


	<div class="topic top">
		<h1>{$aLang.top_topics_personal}</h1>				
		<ul class="block-nav">
			<li {if $aParams[0] and $aParams[0]=='24h'}class="active"{/if}><strong></strong><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/personal/24h/">{$aLang.blog_menu_top_period_24h}</a></li>
			<li {if $aParams[0] and $aParams[0]=='7d'}class="active"{/if}><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/personal/7d/">{$aLang.blog_menu_top_period_7d}</a></li>
			<li {if $aParams[0] and $aParams[0]=='30d'}class="active"{/if}><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/personal/30d/">{$aLang.blog_menu_top_period_30d}</a></li>
			<li {if $aParams[0] and $aParams[0]=='all'}class="active"{/if}><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/personal/all/">{$aLang.blog_menu_top_period_all}</a><em></em></li>
		</ul>
	</div>

	{include file='topic_list.tpl'}


{include file='footer.tpl'}



/templates/skin/new/menu.blog.tpl

Редактируем шаблон меню. Тут уже всё по своему усмотрению.
Вот мой файл, где я убрал отображение топов блогов и смеси топиков, а вместо них поставил топы коллективных и персональных топиков отдельно:
<ul class="menu">
		
			<li {if $sMenuItemSelect=='index'}class="active"{/if}>
				<a href="{$DIR_WEB_ROOT}/">{$aLang.blog_menu_all}</a> {if $iCountTopicsNew>0}+{$iCountTopicsNew}{/if}
				{if $sMenuItemSelect=='index'}
					<ul class="sub-menu" >
						<li {if $sMenuSubItemSelect=='good'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/">{$aLang.blog_menu_all_good}</a></div></li>						
						{if $iCountTopicsNew>0}<li {if $sMenuSubItemSelect=='new'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_NEW}/">{$aLang.blog_menu_all_new}</a> +{$iCountTopicsNew}</div></li>{/if}
					</ul>
				{/if}
			</li>
			
			<li {if $sMenuItemSelect=='blog'}class="active"{/if}>
				<a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_BLOG}/">{$aLang.blog_menu_collective}</a> {if $iCountTopicsCollectiveNew>0}+{$iCountTopicsCollectiveNew}{/if}
				{if $sMenuItemSelect=='blog'}
					<ul class="sub-menu" >											
						<li {if $sMenuSubItemSelect=='good'}class="active"{/if}><div><a href="{$sMenuSubBlogUrl}">{$aLang.blog_menu_collective_good}</a></div></li>
						{if $iCountTopicsBlogNew>0}<li {if $sMenuSubItemSelect=='new'}class="active"{/if}><div><a href="{$sMenuSubBlogUrl}new/">{$aLang.blog_menu_collective_new}</a> +{$iCountTopicsBlogNew}</div></li>{/if}
						<li {if $sMenuSubItemSelect=='bad'}class="active"{/if}><div><a href="{$sMenuSubBlogUrl}bad/">{$aLang.blog_menu_collective_bad}</a></div></li>
					</ul>
				{/if}
			</li>
			
			<li {if $sMenuItemSelect=='log'}class="active"{/if}>
				<a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_PERSONAL_BLOG}/">{$aLang.blog_menu_personal}</a> {if $iCountTopicsPersonalNew>0}+{$iCountTopicsPersonalNew}{/if}
				{if $sMenuItemSelect=='log'}
					<ul class="sub-menu" style="left: -50px;">											
						<li {if $sMenuSubItemSelect=='good'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_PERSONAL_BLOG}/">{$aLang.blog_menu_personal_good}</a></div></li>
						{if $iCountTopicsPersonalNew>0}<li {if $sMenuSubItemSelect=='new'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_PERSONAL_BLOG}/new/">{$aLang.blog_menu_personal_new}</a> +{$iCountTopicsPersonalNew}</div></li>{/if}
						<li {if $sMenuSubItemSelect=='bad'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_PERSONAL_BLOG}/bad/">{$aLang.blog_menu_personal_bad}</a></div></li>
					</ul>
				{/if}
			</li>
			
			<li {if $sMenuItemSelect=='top'}class="active"{/if}>
				<a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/">{$aLang.blog_menu_top}</a>
				{if $sMenuItemSelect=='top'}
					<ul class="sub-menu" style="left: -80px;">											
				<!--	<li {if $sMenuSubItemSelect=='blog'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/blog/">{$aLang.blog_menu_top_blog}</a></div></li> -->
				<!--	<li {if $sMenuSubItemSelect=='topic'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/topic/">{$aLang.blog_menu_top_topic}</a></div></li> -->
						<li {if $sMenuSubItemSelect=='theme'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/theme/">{$aLang.blog_menu_collective}</a></div></li>
						<li {if $sMenuSubItemSelect=='personal'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/personal/">{$aLang.blog_menu_personal}</a></div></li> 
						<li {if $sMenuSubItemSelect=='comment'}class="active"{/if}><div><a href="{$DIR_WEB_ROOT}/{$ROUTE_PAGE_TOP}/comment/">{$aLang.blog_menu_top_comment}</a></div></li>
					</ul>
				{/if}
			</li>
								
		</ul>



/templates/language/russian.php

Остались только языковые константы. Добавляем:
'top_topics_personal' => 'TOP личных топиков',
	'top_topics_theme' => 'TOP тематических топиков',



Всё.

4 комментария

avatar
Спасибо Вам за труды, думаю будет очень даже полезно… Почему кстати нет в каталоге?
avatar
Не за что :-)
В каталог не добавлял, т.к. думал, что вряд ли хак будет пользоваться популярностью. Если это не так — добавим *)

На самом деле, хак актуален ровно до тех пор, пока разработчики не внесут в движок возможность сортировки выбранных топиков. Тогда можно будет всё это реализовать гораздо легче, без изменений в маппере.
avatar
Популярностью не популярностью, но мне предлженный Вами вариан сортировки «топа» кажется всё-таки более логичным… По крайней мере в текущей версии движка.
В каталог обязательно нужно, пусть он лучше будет там лишним напоминанием разработчикам о необходимости реализации такой сортировки… Сам вобщем-то, проковырялся пол дня, пока не понял в чём прикол и не нашёл здесь Ваши посты :) Конечно вносить изменения в ядро не лучший путь, особенно для новичков, но всё-таки лучше так чем вобще никак… :)
avatar
Классный и полезный хак.
Подскажите, пожалуйста, вроде делаю все как написано, но выводятся все равно и персональные и коллективные вместе.
  • qoox
  • 0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.