* */ /** * Модуль для работы с комментариями * * @package application.modules.comment * @since 1.0 */ class ModuleComment extends Module { /** * Объект маппера * * @var ModuleComment_MapperComment */ protected $oMapper; /** * Объект текущего пользователя * * @var ModuleUser_EntityUser|null */ protected $oUserCurrent = null; /** * Инициализация * */ public function Init() { $this->oMapper = Engine::GetMapper(__CLASS__); $this->oUserCurrent = $this->User_GetUserCurrent(); } /** * Получить коммент по айдишнику * * @param int $sId ID комментария * @return ModuleComment_EntityComment|null */ public function GetCommentById($sId) { if (!is_numeric($sId)) { return null; } $aComments = $this->GetCommentsAdditionalData($sId); if (isset($aComments[$sId])) { return $aComments[$sId]; } return null; } /** * Получает уникальный коммент, это помогает спастись от дублей комментов * * @param int $sTargetId ID владельца комментария * @param string $sTargetType Тип владельца комментария * @param int $sUserId ID пользователя * @param int $sCommentPid ID родительского комментария * @param string $sHash Хеш строка текста комментария * @return ModuleComment_EntityComment|null */ public function GetCommentUnique($sTargetId, $sTargetType, $sUserId, $sCommentPid, $sHash) { $sId = $this->oMapper->GetCommentUnique($sTargetId, $sTargetType, $sUserId, $sCommentPid, $sHash); return $this->GetCommentById($sId); } /** * Получить все комменты * * @param string $sTargetType Тип владельца комментария * @param int $iPage Номер страницы * @param int $iPerPage Количество элементов на страницу * @param array $aExcludeTarget Список ID владельцев, которые необходимо исключить из выдачи * @param array $aExcludeParentTarget Список ID родителей владельцев, которые необходимо исключить из выдачи, например, исключить комментарии топиков к определенным блогам(закрытым) * @return array('collection'=>array,'count'=>int) */ public function GetCommentsAll( $sTargetType, $iPage, $iPerPage, $aExcludeTarget = array(), $aExcludeParentTarget = array() ) { $s = serialize($aExcludeTarget) . serialize($aExcludeParentTarget); if (false === ($data = $this->Cache_Get("comment_all_{$sTargetType}_{$iPage}_{$iPerPage}_{$s}"))) { $data = array( 'collection' => $this->oMapper->GetCommentsAll($sTargetType, $iCount, $iPage, $iPerPage, $aExcludeTarget, $aExcludeParentTarget), 'count' => $iCount ); $this->Cache_Set($data, "comment_all_{$sTargetType}_{$iPage}_{$iPerPage}_{$s}", array("comment_new_{$sTargetType}", "comment_update_status_{$sTargetType}"), 60 * 60 * 24 * 1); } $data['collection'] = $this->GetCommentsAdditionalData($data['collection'], array('target', 'favourite', 'user' => array())); return $data; } /** * Получает дополнительные данные(объекты) для комментов по их ID * * @param array $aCommentId Список ID комментов * @param array|null $aAllowData Список типов дополнительных данных, которые нужно получить для комментариев * @return array */ public function GetCommentsAdditionalData($aCommentId, $aAllowData = null) { if (is_null($aAllowData)) { $aAllowData = array('vote', 'target', 'favourite', 'user' => array()); } func_array_simpleflip($aAllowData); if (!is_array($aCommentId)) { $aCommentId = array($aCommentId); } /** * Получаем комменты */ $aComments = $this->GetCommentsByArrayId($aCommentId); /** * Формируем ID дополнительных данных, которые нужно получить */ $aUserId = array(); $aTargetId = array('topic' => array(), 'talk' => array()); foreach ($aComments as $oComment) { if (isset($aAllowData['user'])) { $aUserId[] = $oComment->getUserId(); } if (isset($aAllowData['target'])) { $aTargetId[$oComment->getTargetType()][] = $oComment->getTargetId(); } } /** * Получаем дополнительные данные */ $aUsers = isset($aAllowData['user']) && is_array($aAllowData['user']) ? $this->User_GetUsersAdditionalData($aUserId, $aAllowData['user']) : $this->User_GetUsersAdditionalData($aUserId); /** * В зависимости от типа target_type достаем данные */ $aTargets = array(); //$aTargets['topic']=isset($aAllowData['target']) && is_array($aAllowData['target']) ? $this->Topic_GetTopicsAdditionalData($aTargetId['topic'],$aAllowData['target']) : $this->Topic_GetTopicsAdditionalData($aTargetId['topic']); $aTargets['topic'] = $this->Topic_GetTopicsAdditionalData($aTargetId['topic'], array('blog' => array('owner' => array()), 'user' => array())); $aVote = array(); if (isset($aAllowData['vote']) and $this->oUserCurrent) { $aVote = $this->Vote_GetVoteByArray($aCommentId, 'comment', $this->oUserCurrent->getId()); } if (isset($aAllowData['favourite']) and $this->oUserCurrent) { $aFavouriteComments = $this->Favourite_GetFavouritesByArray($aCommentId, 'comment', $this->oUserCurrent->getId()); } /** * Добавляем данные к результату */ foreach ($aComments as $oComment) { if (isset($aUsers[$oComment->getUserId()])) { $oComment->setUser($aUsers[$oComment->getUserId()]); } else { $oComment->setUser(null); // или $oComment->setUser(new ModuleUser_EntityUser()); } if (isset($aTargets[$oComment->getTargetType()][$oComment->getTargetId()])) { $oComment->setTarget($aTargets[$oComment->getTargetType()][$oComment->getTargetId()]); } else { $oComment->setTarget(null); } if (isset($aVote[$oComment->getId()])) { $oComment->setVote($aVote[$oComment->getId()]); } else { $oComment->setVote(null); } if (isset($aFavouriteComments[$oComment->getId()])) { $oComment->setIsFavourite(true); } else { $oComment->setIsFavourite(false); } } return $aComments; } /** * Список комментов по ID * * @param array $aCommentId Список ID комментариев * @return array */ public function GetCommentsByArrayId($aCommentId) { if (!$aCommentId) { return array(); } if (Config::Get('sys.cache.solid')) { return $this->GetCommentsByArrayIdSolid($aCommentId); } if (!is_array($aCommentId)) { $aCommentId = array($aCommentId); } $aCommentId = array_unique($aCommentId); $aComments = array(); $aCommentIdNotNeedQuery = array(); /** * Делаем мульти-запрос к кешу */ $aCacheKeys = func_build_cache_keys($aCommentId, 'comment_'); if (false !== ($data = $this->Cache_Get($aCacheKeys))) { /** * Проверяем что досталось из кеша */ foreach ($aCacheKeys as $sValue => $sKey) { if (array_key_exists($sKey, $data)) { if ($data[$sKey]) { $aComments[$data[$sKey]->getId()] = $data[$sKey]; } else { $aCommentIdNotNeedQuery[] = $sValue; } } } } /** * Смотрим каких комментов не было в кеше и делаем запрос в БД */ $aCommentIdNeedQuery = array_diff($aCommentId, array_keys($aComments)); $aCommentIdNeedQuery = array_diff($aCommentIdNeedQuery, $aCommentIdNotNeedQuery); $aCommentIdNeedStore = $aCommentIdNeedQuery; if ($data = $this->oMapper->GetCommentsByArrayId($aCommentIdNeedQuery)) { foreach ($data as $oComment) { /** * Добавляем к результату и сохраняем в кеш */ $aComments[$oComment->getId()] = $oComment; $this->Cache_Set($oComment, "comment_{$oComment->getId()}", array(), 60 * 60 * 24 * 4); $aCommentIdNeedStore = array_diff($aCommentIdNeedStore, array($oComment->getId())); } } /** * Сохраняем в кеш запросы не вернувшие результата */ foreach ($aCommentIdNeedStore as $sId) { $this->Cache_Set(null, "comment_{$sId}", array(), 60 * 60 * 24 * 4); } /** * Сортируем результат согласно входящему массиву */ $aComments = func_array_sort_by_keys($aComments, $aCommentId); return $aComments; } /** * Получает список комментариев по ID используя единый кеш * * @param array $aCommentId Список ID комментариев * @return array */ public function GetCommentsByArrayIdSolid($aCommentId) { if (!is_array($aCommentId)) { $aCommentId = array($aCommentId); } $aCommentId = array_unique($aCommentId); $aComments = array(); $s = join(',', $aCommentId); if (false === ($data = $this->Cache_Get("comment_id_{$s}"))) { $data = $this->oMapper->GetCommentsByArrayId($aCommentId); foreach ($data as $oComment) { $aComments[$oComment->getId()] = $oComment; } $this->Cache_Set($aComments, "comment_id_{$s}", array("comment_update"), 60 * 60 * 24 * 1); return $aComments; } return $data; } /** * Получить все комменты сгрупированные по типу(для вывода прямого эфира) * * @param string $sTargetType Тип владельца комментария * @param int $iLimit Количество элементов * @return array */ public function GetCommentsOnline($sTargetType, $iLimit) { /** * Исключаем из выборки идентификаторы закрытых блогов (target_parent_id) */ $aCloseBlogs = ($this->oUserCurrent) ? $this->Blog_GetInaccessibleBlogsByUser($this->oUserCurrent) : $this->Blog_GetInaccessibleBlogsByUser(); $s = serialize($aCloseBlogs); if (false === ($data = $this->Cache_Get("comment_online_{$sTargetType}_{$s}_{$iLimit}"))) { $data = $this->oMapper->GetCommentsOnline($sTargetType, $aCloseBlogs, $iLimit); $this->Cache_Set($data, "comment_online_{$sTargetType}_{$s}_{$iLimit}", array("comment_online_update_{$sTargetType}"), 60 * 60 * 24 * 1); } $data = $this->GetCommentsAdditionalData($data); return $data; } /** * Получить комменты по юзеру * * @param int $sId ID пользователя * @param string $sTargetType Тип владельца комментария * @param int $iPage Номер страницы * @param int $iPerPage Количество элементов на страницу * @return array */ public function GetCommentsByUserId($sId, $sTargetType, $iPage, $iPerPage) { /** * Исключаем из выборки идентификаторы закрытых блогов */ $aCloseBlogs = ($this->oUserCurrent && $sId == $this->oUserCurrent->getId()) ? array() : $this->Blog_GetInaccessibleBlogsByUser(); $s = serialize($aCloseBlogs); if (false === ($data = $this->Cache_Get("comment_user_{$sId}_{$sTargetType}_{$iPage}_{$iPerPage}_{$s}"))) { $data = array( 'collection' => $this->oMapper->GetCommentsByUserId($sId, $sTargetType, $iCount, $iPage, $iPerPage, array(), $aCloseBlogs), 'count' => $iCount ); $this->Cache_Set($data, "comment_user_{$sId}_{$sTargetType}_{$iPage}_{$iPerPage}_{$s}", array("comment_new_user_{$sId}_{$sTargetType}", "comment_update_status_{$sTargetType}"), 60 * 60 * 24 * 2); } $data['collection'] = $this->GetCommentsAdditionalData($data['collection']); return $data; } /** * Получает количество комментариев одного пользователя * * @param id $sId ID пользователя * @param string $sTargetType Тип владельца комментария * @return int */ public function GetCountCommentsByUserId($sId, $sTargetType) { /** * Исключаем из выборки идентификаторы закрытых блогов */ $aCloseBlogs = ($this->oUserCurrent && $sId == $this->oUserCurrent->getId()) ? array() : $this->Blog_GetInaccessibleBlogsByUser(); $s = serialize($aCloseBlogs); if (false === ($data = $this->Cache_Get("comment_count_user_{$sId}_{$sTargetType}_{$s}"))) { $data = $this->oMapper->GetCountCommentsByUserId($sId, $sTargetType, array(), $aCloseBlogs); $this->Cache_Set($data, "comment_count_user_{$sId}_{$sTargetType}_{$s}", array("comment_new_user_{$sId}_{$sTargetType}", "comment_update_status_{$sTargetType}"), 60 * 60 * 24 * 2); } return $data; } /** * Получить комменты по рейтингу и дате * * @param string $sDate Дата за которую выводить рейтинг, т.к. кеширование происходит по дате, то дату лучше передавать с точностью до часа (минуты и секунды как 00:00) * @param string $sTargetType Тип владельца комментария * @param int $iLimit Количество элементов * @return array */ public function GetCommentsRatingByDate($sDate, $sTargetType, $iLimit = 20) { /** * Выбираем топики, комметарии к которым являются недоступными для пользователя */ $aCloseBlogs = ($this->oUserCurrent) ? $this->Blog_GetInaccessibleBlogsByUser($this->oUserCurrent) : $this->Blog_GetInaccessibleBlogsByUser(); $s = serialize($aCloseBlogs); /** * Т.к. время передаётся с точностью 1 час то можно по нему замутить кеширование */ if (false === ($data = $this->Cache_Get("comment_rating_{$sDate}_{$sTargetType}_{$iLimit}_{$s}"))) { $data = $this->oMapper->GetCommentsRatingByDate($sDate, $sTargetType, $iLimit, array(), $aCloseBlogs); $this->Cache_Set($data, "comment_rating_{$sDate}_{$sTargetType}_{$iLimit}_{$s}", array( "comment_new_{$sTargetType}", "comment_update_status_{$sTargetType}", "comment_update_rating_{$sTargetType}" ), 60 * 60 * 24 * 2); } $data = $this->GetCommentsAdditionalData($data); return $data; } /** * Получить комменты по владельцу * * @param int $sId ID владельца коммента * @param string $sTargetType Тип владельца комментария * @param int $iPage Номер страницы * @param int $iPerPage Количество элементов на страницу * @return array('comments'=>array,'iMaxIdComment'=>int) */ public function GetCommentsByTargetId($sId, $sTargetType, $iPage = 1, $iPerPage = 0) { if (Config::Get('module.comment.use_nested')) { return $this->GetCommentsTreeByTargetId($sId, $sTargetType, $iPage, $iPerPage); } if (false === ($aCommentsRec = $this->Cache_Get("comment_target_{$sId}_{$sTargetType}"))) { $aCommentsRow = $this->oMapper->GetCommentsByTargetId($sId, $sTargetType); if (count($aCommentsRow)) { $aCommentsRec = $this->BuildCommentsRecursive($aCommentsRow); } $this->Cache_Set($aCommentsRec, "comment_target_{$sId}_{$sTargetType}", array("comment_new_{$sTargetType}_{$sId}"), 60 * 60 * 24 * 2); } if (!isset($aCommentsRec['comments'])) { return array('comments' => array(), 'iMaxIdComment' => 0); } $aComments = $aCommentsRec; $aComments['comments'] = $this->GetCommentsAdditionalData(array_keys($aCommentsRec['comments'])); foreach ($aComments['comments'] as $oComment) { $oComment->setLevel($aCommentsRec['comments'][$oComment->getId()]); } return $aComments; } /** * Получает комменты используя nested set * * @param int $sId ID владельца коммента * @param string $sTargetType Тип владельца комментария * @param int $iPage Номер страницы * @param int $iPerPage Количество элементов на страницу * @return array('comments'=>array,'iMaxIdComment'=>int,'count'=>int) */ public function GetCommentsTreeByTargetId($sId, $sTargetType, $iPage = 1, $iPerPage = 0) { if (!Config::Get('module.comment.nested_page_reverse') and $iPerPage and $iCountPage = ceil($this->GetCountCommentsRootByTargetId($sId, $sTargetType) / $iPerPage) ) { $iPage = $iCountPage - $iPage + 1; } $iPage = $iPage < 1 ? 1 : $iPage; if (false === ($aReturn = $this->Cache_Get("comment_tree_target_{$sId}_{$sTargetType}_{$iPage}_{$iPerPage}"))) { /** * Нужно или нет использовать постраничное разбиение комментариев */ if ($iPerPage) { $aComments = $this->oMapper->GetCommentsTreePageByTargetId($sId, $sTargetType, $iCount, $iPage, $iPerPage); } else { $aComments = $this->oMapper->GetCommentsTreeByTargetId($sId, $sTargetType); $iCount = count($aComments); } $iMaxIdComment = count($aComments) ? max($aComments) : 0; $aReturn = array('comments' => $aComments, 'iMaxIdComment' => $iMaxIdComment, 'count' => $iCount); $this->Cache_Set($aReturn, "comment_tree_target_{$sId}_{$sTargetType}_{$iPage}_{$iPerPage}", array("comment_new_{$sTargetType}_{$sId}"), 60 * 60 * 24 * 2); } $aReturn['comments'] = $this->GetCommentsAdditionalData($aReturn['comments']); return $aReturn; } /** * Возвращает количество дочерних комментариев у корневого коммента * * @param int $sId ID владельца коммента * @param string $sTargetType Тип владельца комментария * @return int */ public function GetCountCommentsRootByTargetId($sId, $sTargetType) { return $this->oMapper->GetCountCommentsRootByTargetId($sId, $sTargetType); } /** * Возвращает номер страницы, на которой расположен комментарий * * @param int $sId ID владельца коммента * @param string $sTargetType Тип владельца комментария * @param ModuleComment_EntityComment $oComment Объект комментария * @return bool|int */ public function GetPageCommentByTargetId($sId, $sTargetType, $oComment) { if (!Config::Get('module.comment.nested_per_page')) { return 1; } if (is_numeric($oComment)) { if (!($oComment = $this->GetCommentById($oComment))) { return false; } if ($oComment->getTargetId() != $sId or $oComment->getTargetType() != $sTargetType) { return false; } } /** * Получаем корневого родителя */ if ($oComment->getPid()) { if (!($oCommentRoot = $this->oMapper->GetCommentRootByTargetIdAndChildren($sId, $sTargetType, $oComment->getLeft())) ) { return false; } } else { $oCommentRoot = $oComment; } $iCount = ceil($this->oMapper->GetCountCommentsAfterByTargetId($sId, $sTargetType, $oCommentRoot->getLeft()) / Config::Get('module.comment.nested_per_page')); if (!Config::Get('module.comment.nested_page_reverse') and $iCountPage = ceil($this->GetCountCommentsRootByTargetId($sId, $sTargetType) / Config::Get('module.comment.nested_per_page')) ) { $iCount = $iCountPage - $iCount + 1; } return $iCount ? $iCount : 1; } /** * Добавляет коммент * * @param ModuleComment_EntityComment $oComment Объект комментария * @return bool|ModuleComment_EntityComment */ public function AddComment(ModuleComment_EntityComment $oComment) { if (Config::Get('module.comment.use_nested')) { $sId = $this->oMapper->AddCommentTree($oComment); $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_update")); } else { $sId = $this->oMapper->AddComment($oComment); } if ($sId) { if ($oComment->getTargetType() == 'topic') { $this->Topic_increaseTopicCountComment($oComment->getTargetId()); } //чистим зависимые кеши $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array( "comment_new", "comment_new_{$oComment->getTargetType()}", "comment_new_user_{$oComment->getUserId()}_{$oComment->getTargetType()}", "comment_new_{$oComment->getTargetType()}_{$oComment->getTargetId()}" )); $oComment->setId($sId); return $oComment; } return false; } /** * Обновляет коммент * * @param ModuleComment_EntityComment $oComment Объект комментария * @return bool */ public function UpdateComment(ModuleComment_EntityComment $oComment) { if ($this->oMapper->UpdateComment($oComment)) { //чистим зависимые кеши $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_update", "comment_update_{$oComment->getTargetType()}_{$oComment->getTargetId()}")); $this->Cache_Delete("comment_{$oComment->getId()}"); return true; } return false; } /** * Обновляет рейтинг у коммента * * @param ModuleComment_EntityComment $oComment Объект комментария * @return bool */ public function UpdateCommentRating(ModuleComment_EntityComment $oComment) { if ($this->oMapper->UpdateComment($oComment)) { //чистим зависимые кеши $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_update_rating_{$oComment->getTargetType()}")); $this->Cache_Delete("comment_{$oComment->getId()}"); return true; } return false; } /** * Обновляет статус у коммента - delete или publish * * @param ModuleComment_EntityComment $oComment Объект комментария * @return bool */ public function UpdateCommentStatus(ModuleComment_EntityComment $oComment) { if ($this->oMapper->UpdateComment($oComment)) { /** * Если комментарий удаляется, удаляем его из прямого эфира */ if ($oComment->getDelete()) { $this->DeleteCommentOnlineByArrayId($oComment->getId(), $oComment->getTargetType()); } /** * Обновляем избранное */ $this->Favourite_SetFavouriteTargetPublish($oComment->getId(), 'comment', !$oComment->getDelete()); /** * Чистим зависимые кеши */ if (Config::Get('sys.cache.solid')) { $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_update")); } $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_update_status_{$oComment->getTargetType()}")); $this->Cache_Delete("comment_{$oComment->getId()}"); return true; } return false; } /** * Устанавливает publish у коммента * * @param int $sTargetId ID владельца коммента * @param string $sTargetType Тип владельца комментария * @param int $iPublish Статус отображать комментарии или нет * @return bool */ public function SetCommentsPublish($sTargetId, $sTargetType, $iPublish) { if (!$aComments = $this->GetCommentsByTargetId($sTargetId, $sTargetType)) { return false; } if (!isset($aComments['comments']) or count($aComments['comments']) == 0) { return; } $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_update_status_{$sTargetType}")); /** * Если статус публикации успешно изменен, то меняем статус в отметке "избранное". * Если комментарии снимаются с публикации, удаляем их из прямого эфира. */ if ($this->oMapper->SetCommentsPublish($sTargetId, $sTargetType, $iPublish)) { $this->Favourite_SetFavouriteTargetPublish(array_keys($aComments['comments']), 'comment', $iPublish); if ($iPublish != 1) { $this->DeleteCommentOnlineByTargetId($sTargetId, $sTargetType); } return true; } return false; } /** * Удаляет коммент из прямого эфира * * @param int $sTargetId ID владельца коммента * @param string $sTargetType Тип владельца комментария * @return bool */ public function DeleteCommentOnlineByTargetId($sTargetId, $sTargetType) { $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_online_update_{$sTargetType}")); return $this->oMapper->DeleteCommentOnlineByTargetId($sTargetId, $sTargetType); } /** * Добавляет новый коммент в прямой эфир * * @param ModuleComment_EntityCommentOnline $oCommentOnline Объект онлайн комментария * @return bool|int */ public function AddCommentOnline(ModuleComment_EntityCommentOnline $oCommentOnline) { $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_online_update_{$oCommentOnline->getTargetType()}")); return $this->oMapper->AddCommentOnline($oCommentOnline); } /** * Получить новые комменты для владельца * * @param int $sId ID владельца коммента * @param string $sTargetType Тип владельца комментария * @param int $sIdCommentLast ID последнего прочитанного комментария * @return array('comments'=>array,'iMaxIdComment'=>int) */ public function GetCommentsNewByTargetId($sId, $sTargetType, $sIdCommentLast) { if (false === ($aComments = $this->Cache_Get("comment_target_{$sId}_{$sTargetType}_{$sIdCommentLast}"))) { $aComments = $this->oMapper->GetCommentsNewByTargetId($sId, $sTargetType, $sIdCommentLast); $this->Cache_Set($aComments, "comment_target_{$sId}_{$sTargetType}_{$sIdCommentLast}", array("comment_new_{$sTargetType}_{$sId}"), 60 * 60 * 24 * 1); } if (count($aComments) == 0) { return array('comments' => array(), 'iMaxIdComment' => 0); } $iMaxIdComment = max($aComments); $aCmts = $this->GetCommentsAdditionalData($aComments); $oViewerLocal = $this->Viewer_GetLocalViewer(); $oViewerLocal->Assign('oUserCurrent', $this->User_GetUserCurrent()); $oViewerLocal->Assign('oneComment', true, true); if ($sTargetType != 'topic') { $oViewerLocal->Assign('bNoCommentFavourites', true); } elseif ($sTargetType == 'topic') { $oViewerLocal->Assign('useFavourite', true, true); $oViewerLocal->Assign('useVote', true, true); $oViewerLocal->Assign('useScroll', true, true); $oViewerLocal->Assign('useEdit', true, true); $oViewerLocal->Assign('dateReadLast', '1', true); } $aCmt = array(); foreach ($aCmts as $oComment) { $oViewerLocal->Assign('comment', $oComment, true); $oViewerLocal->Assign('isDeleted', $oComment->getDelete(), true); $sText = $oViewerLocal->Fetch($this->GetTemplateCommentByTarget($sId, $sTargetType)); $aCmt[] = array( 'html' => $sText, 'obj' => $oComment, ); } return array('comments' => $aCmt, 'iMaxIdComment' => $iMaxIdComment); } /** * Возвращает шаблон комментария для рендеринга * Плагин может переопределить данный метод и вернуть свой шаблон в зависимости от типа * * @param int $iTargetId ID объекта комментирования * @param string $sTargetType Типа объекта комментирования * @return string */ public function GetTemplateCommentByTarget($iTargetId, $sTargetType) { return "component@comment.comment"; } /** * Строит дерево комментариев * * @param array $aComments Список комментариев * @param bool $bBegin Флаг начала построения дерева, для инициализации параметров внутри метода * @return array('comments'=>array,'iMaxIdComment'=>int) */ protected function BuildCommentsRecursive($aComments, $bBegin = true) { static $aResultCommnets; static $iLevel; static $iMaxIdComment; if ($bBegin) { $aResultCommnets = array(); $iLevel = 0; $iMaxIdComment = 0; } foreach ($aComments as $aComment) { $aTemp = $aComment; if ($aComment['comment_id'] > $iMaxIdComment) { $iMaxIdComment = $aComment['comment_id']; } $aTemp['level'] = $iLevel; unset($aTemp['childNodes']); $aResultCommnets[$aTemp['comment_id']] = $aTemp['level']; if (isset($aComment['childNodes']) and count($aComment['childNodes']) > 0) { $iLevel++; $this->BuildCommentsRecursive($aComment['childNodes'], false); } } $iLevel--; return array('comments' => $aResultCommnets, 'iMaxIdComment' => $iMaxIdComment); } /** * Получает привязку комментария к ибранному(добавлен ли коммент в избранное у юзера) * * @param int $sCommentId ID комментария * @param int $sUserId ID пользователя * @return ModuleFavourite_EntityFavourite|null */ public function GetFavouriteComment($sCommentId, $sUserId) { return $this->Favourite_GetFavourite($sCommentId, 'comment', $sUserId); } /** * Получить список избранного по списку айдишников * * @param array $aCommentId Список ID комментов * @param int $sUserId ID пользователя * @return array */ public function GetFavouriteCommentsByArray($aCommentId, $sUserId) { return $this->Favourite_GetFavouritesByArray($aCommentId, 'comment', $sUserId); } /** * Получить список избранного по списку айдишников, но используя единый кеш * * @param array $aCommentId Список ID комментов * @param int $sUserId ID пользователя * @return array */ public function GetFavouriteCommentsByArraySolid($aCommentId, $sUserId) { return $this->Favourite_GetFavouritesByArraySolid($aCommentId, 'comment', $sUserId); } /** * Получает список комментариев из избранного пользователя * * @param int $sUserId ID пользователя * @param int $iCurrPage Номер страницы * @param int $iPerPage Количество элементов на страницу * @return array */ public function GetCommentsFavouriteByUserId($sUserId, $iCurrPage, $iPerPage) { $aCloseTopics = array(); /** * Получаем список идентификаторов избранных комментов */ $data = ($this->oUserCurrent && $sUserId == $this->oUserCurrent->getId()) ? $this->Favourite_GetFavouritesByUserId($sUserId, 'comment', $iCurrPage, $iPerPage, $aCloseTopics) : $this->Favourite_GetFavouriteOpenCommentsByUserId($sUserId, $iCurrPage, $iPerPage); /** * Получаем комменты по переданому массиву айдишников */ $data['collection'] = $this->GetCommentsAdditionalData($data['collection']); return $data; } /** * Возвращает число комментариев в избранном * * @param int $sUserId ID пользователя * @return int */ public function GetCountCommentsFavouriteByUserId($sUserId) { return ($this->oUserCurrent && $sUserId == $this->oUserCurrent->getId()) ? $this->Favourite_GetCountFavouritesByUserId($sUserId, 'comment') : $this->Favourite_GetCountFavouriteOpenCommentsByUserId($sUserId); } /** * Добавляет комментарий в избранное * * @param ModuleFavourite_EntityFavourite $oFavourite Объект избранного * @return bool|ModuleFavourite_EntityFavourite */ public function AddFavouriteComment(ModuleFavourite_EntityFavourite $oFavourite) { if (($oFavourite->getTargetType() == 'comment') && ($oComment = $this->Comment_GetCommentById($oFavourite->getTargetId())) && in_array($oComment->getTargetType(), Config::get('module.comment.favourite_target_allow')) ) { return $this->Favourite_AddFavourite($oFavourite); } return false; } /** * Удаляет комментарий из избранного * * @param ModuleFavourite_EntityFavourite $oFavourite Объект избранного * @return bool */ public function DeleteFavouriteComment(ModuleFavourite_EntityFavourite $oFavourite) { if (($oFavourite->getTargetType() == 'comment') && ($oComment = $this->Comment_GetCommentById($oFavourite->getTargetId())) && in_array($oComment->getTargetType(), Config::get('module.comment.favourite_target_allow')) ) { return $this->Favourite_DeleteFavourite($oFavourite); } return false; } /** * Удаляет комментарии из избранного по списку * * @param array $aCommentId Список ID комментариев * @return bool */ public function DeleteFavouriteCommentsByArrayId($aCommentId) { return $this->Favourite_DeleteFavouriteByTargetId($aCommentId, 'comment'); } /** * Удаляет комментарии из базы данных * * @param array|int $aTargetId Список ID владельцев * @param string $sTargetType Тип владельцев * @return bool */ public function DeleteCommentByTargetId($aTargetId, $sTargetType) { if (!is_array($aTargetId)) { $aTargetId = array($aTargetId); } /** * Получаем список идентификаторов удаляемых комментариев */ $aCommentsId = array(); foreach ($aTargetId as $sTargetId) { $aComments = $this->GetCommentsByTargetId($sTargetId, $sTargetType); $aCommentsId = array_merge($aCommentsId, array_keys($aComments['comments'])); } /** * Если ни одного комментария не найдено, выходим */ if (!count($aCommentsId)) { return true; } /** * Чистим зависимые кеши */ if (Config::Get('sys.cache.solid')) { $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_update", "comment_target_{$sTargetId}_{$sTargetType}")); } else { $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_target_{$sTargetId}_{$sTargetType}")); /** * Удаляем кеш для каждого комментария */ foreach ($aCommentsId as $iCommentId) { $this->Cache_Delete("comment_{$iCommentId}"); } } if ($this->oMapper->DeleteCommentByTargetId($aTargetId, $sTargetType)) { /** * Удаляем комментарии из избранного */ $this->DeleteFavouriteCommentsByArrayId($aCommentsId); /** * Удаляем комментарии к топику из прямого эфира */ $this->DeleteCommentOnlineByArrayId($aCommentsId, $sTargetType); /** * Удаляем голосование за комментарии */ $this->Vote_DeleteVoteByTarget($aCommentsId, 'comment'); return true; } return false; } /** * Удаляет коммент из прямого эфира по массиву переданных идентификаторов * * @param array|int $aCommentId * @param string $sTargetType Тип владельцев * @return bool */ public function DeleteCommentOnlineByArrayId($aCommentId, $sTargetType) { if (!is_array($aCommentId)) { $aCommentId = array($aCommentId); } /** * Чистим кеш */ $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_online_update_{$sTargetType}")); return $this->oMapper->DeleteCommentOnlineByArrayId($aCommentId, $sTargetType); } /** * Меняем target parent по массиву идентификаторов * * @param int $sParentId Новый ID родителя владельца * @param string $sTargetType Тип владельца * @param array|int $aTargetId Список ID владельцев * @return bool */ public function UpdateTargetParentByTargetId($sParentId, $sTargetType, $aTargetId) { if (!is_array($aTargetId)) { $aTargetId = array($aTargetId); } // чистим зависимые кеши $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_new_{$sTargetType}")); return $this->oMapper->UpdateTargetParentByTargetId($sParentId, $sTargetType, $aTargetId); } /** * Меняем target parent по массиву идентификаторов в таблице комментариев online * * @param int $sParentId Новый ID родителя владельца * @param string $sTargetType Тип владельца * @param array|int $aTargetId Список ID владельцев * @return bool */ public function UpdateTargetParentByTargetIdOnline($sParentId, $sTargetType, $aTargetId) { if (!is_array($aTargetId)) { $aTargetId = array($aTargetId); } // чистим зависимые кеши $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_online_update_{$sTargetType}")); return $this->oMapper->UpdateTargetParentByTargetIdOnline($sParentId, $sTargetType, $aTargetId); } /** * Меняет target parent на новый * * @param int $sParentId Прежний ID родителя владельца * @param string $sTargetType Тип владельца * @param int $sParentIdNew Новый ID родителя владельца * @return bool */ public function MoveTargetParent($sParentId, $sTargetType, $sParentIdNew) { $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_new_{$sTargetType}")); return $this->oMapper->MoveTargetParent($sParentId, $sTargetType, $sParentIdNew); } /** * Меняет target parent на новый в прямом эфире * * @param int $sParentId Прежний ID родителя владельца * @param string $sTargetType Тип владельца * @param int $sParentIdNew Новый ID родителя владельца * @return bool */ public function MoveTargetParentOnline($sParentId, $sTargetType, $sParentIdNew) { $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array("comment_online_update_{$sTargetType}")); return $this->oMapper->MoveTargetParentOnline($sParentId, $sTargetType, $sParentIdNew); } /** * Перестраивает дерево комментариев * Восстанавливает значения left, right и level * * @param int $aTargetId Список ID владельцев * @param string $sTargetType Тип владельца */ public function RestoreTree($aTargetId = null, $sTargetType = null) { // обработать конкретную сущность if (!is_null($aTargetId) and !is_null($sTargetType)) { $this->oMapper->RestoreTree(null, 0, -1, $aTargetId, $sTargetType); return; } $aType = array(); // обработать все сущности конкретного типа if (!is_null($sTargetType)) { $aType[] = $sTargetType; } else { // обработать все сущности всех типов $aType = $this->oMapper->GetCommentTypes(); } foreach ($aType as $sTargetType) { // для каждого типа получаем порциями ID сущностей $iPage = 1; $iPerPage = 50; while ($aResult = $this->oMapper->GetTargetIdByType($sTargetType, $iPage, $iPerPage)) { foreach ($aResult as $Row) { $this->oMapper->RestoreTree(null, 0, -1, $Row['target_id'], $sTargetType); } $iPage++; } } } /** * Пересчитывает счетчик избранных комментариев * * @return bool */ public function RecalculateFavourite() { return $this->oMapper->RecalculateFavourite(); } /** * Получает список комментариев по фильтру * * @param array $aFilter Фильтр выборки * @param array $aOrder Сортировка * @param int $iCurrPage Номер текущей страницы * @param int $iPerPage Количество элементов на одну страницу * @param array $aAllowData Список типов данных, которые нужно подтянуть к списку комментов * @return array */ public function GetCommentsByFilter($aFilter, $aOrder, $iCurrPage, $iPerPage, $aAllowData = null) { if (is_null($aAllowData)) { $aAllowData = array('target', 'user' => array()); } $aCollection = $this->oMapper->GetCommentsByFilter($aFilter, $aOrder, $iCount, $iCurrPage, $iPerPage); return array('collection' => $this->GetCommentsAdditionalData($aCollection, $aAllowData), 'count' => $iCount); } /** * Алиас для корректной работы ORM * * @param array $aCommentId Список ID комментариев * @return array */ public function GetCommentItemsByArrayId($aCommentId) { return $this->GetCommentsByArrayId($aCommentId); } }