CakePHP - пытается упорядочить посты по релевантности тегам, которые делятся с основным постом и датой создания - PullRequest
2 голосов
/ 27 марта 2012

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

Модели:

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).Таким образом, похоже, что значение релевантности для каждого сообщения принимает 0 или 1 в зависимости от тега LAST сообщений, а не накапливается на основе ВСЕХ тегов.

1 Ответ

1 голос
/ 10 апреля 2012

Прежде всего, я бы взял всю логику из контроллера. Учтите это:

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

Очевидно, что было бы здорово использовать логику базы данных для извлечения записей (как вы пытаетесь это сделать), чтобы это было как можно быстрее и чтобы вы минимизировали объем данных, которые вы извлекаете, но я не могу посмотрите, как это сделать для того, чего вы пытаетесь достичь.

Этот метод должен работать нормально и не зависит от базы данных. :)

...