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); 
} 

显然,这将是巨大的使用数据库的逻辑来获取记录(如你正在试图做的),所以它的尽可能快和这样可以最大限度地减少检索的数据量,但我无法知道如何为您实现的目标做到这一点。

该方法应该可以正常工作,并且不是数据库特定的。 :)