CakePHP - 尝试通过与主要帖子和创建日期共享的标记相关来订购帖子
问题描述:
我试图根据与正在查看的当前帖子共享的标签以及相关的标签,按相关性顺序显示八个最相关的帖子创建日期...CakePHP - 尝试通过与主要帖子和创建日期共享的标记相关来订购帖子
型号:
Tag habtm Post
Post habtm Tag
DB:
posts(id, slug, ...)
tags(id, tag, ...)
posts_tags(post_id, tag_id)
在控制器动作:
$post = $this->Post->find('first', array('conditions' => array('slug' => $slug)));
$this->set('post', $post);
$tags = $post['Tag'];
$relOrd = '';
foreach($tags as $tag){
$tagId = $tag['id'];
$relOrd .= " + (CASE WHEN PostsTag.tag_id = ".$tagId." THEN 1 ELSE 0 END)";
}
$relOrd = '(' . substr($relOrd, 3) . ') AS Relevance';
$morePosts = $this->Post->find('all', array(
'joins' => array(
array(
'table' => 'posts_tags',
'alias' => 'PostsTag',
'type' => 'LEFT',
'conditions' => array(
'PostsTag.post_id = Post.id',
)
)
),
'group' => 'Post.id',
'fields' => array($relOrd, 'Post.*'),
'order' => array('Relevance' => 'DESC', 'Post.created' => 'DESC'),
'limit' => 8,
));
$this->log($morePosts);
$this->set('morePosts', $morePosts);
虽然相关性值被视为每个帖子只有一个标签(仅为0或1),但它几乎可以工作。因此,根据帖子的LAST标签,每个帖子的相关值似乎取0或1,而不是基于所有标签进行累计。
答
首先,我将所有的逻辑从控制器中取出。考虑到这一点:
$post = $this->Post->find('first', array('conditions' => array('slug' => $slug)));
$this->set('post', $post);
$this->set('morePosts', $this->Post->findRelevant($post));
现在你的控制器很容易阅读,它的工作。本质上,你首先通过命名一个想象的模型函数来描述你想要的数据,然后编写模型代码来完成这个请求。
因此,这里是在模型的代码刺:
var $actsAs = array('Containable');
function findRelevant($post, $limit = 8) {
// create an array of ids of the tags from this post
$tags = array();
foreach($post['Tag'] as $num => $tag) {
$tags[$tag['id']] = $tag['id'];
}
// find other posts that have any of those tags
$relevant = $this->find('all', array(
'conditions' => array('Post.id <>' => $post['Post']['id']),
'order' => 'Post.created desc',
'contain' => array('Tag' => array('conditions' => array(
'Tag.id' => $tags
))),
));
// count the number of tags of each post and call it relevance
// (this number is essentially the number of tags in common
// with the original post because we used contain to get only
// the tags from the original post)
foreach($relevant as &$p) {
$p['Post']['relevance'] = count($p['Tag']);
}
// sort by relevance
$relevant = Set::sort($relevant, '{n}.Post.relevance', 'desc');
// limit the number of posts returned (defaults to 8)
return array_splice($relevant, 0, $limit);
}
显然,这将是巨大的使用数据库的逻辑来获取记录(如你正在试图做的),所以它的尽可能快和这样可以最大限度地减少检索的数据量,但我无法知道如何为您实现的目标做到这一点。
该方法应该可以正常工作,并且不是数据库特定的。 :)