2012-03-27 1 views
2

Ich versuche, acht der relevantesten Beiträge in der Reihenfolge der Relevanz anzuzeigen, basierend auf Tags, die mit dem aktuellen Beitrag geteilt werden Datum erstellt ...CakePHP - Versuche, Beiträge nach Relevanz für Tags zu sortieren, die mit Hauptpost und erstelltem Datum geteilt werden

Modelle:

Tag habtm Post 
Post habtm Tag 

DB:

posts(id, slug, ...) 
tags(id, tag, ...) 
posts_tags(post_id, tag_id) 

Im Controller-Aktion:

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

Es funktioniert fast, obwohl der Relevanzwert behandelt wird, als ob jeder Beitrag nur ein Tag (nur 0 oder 1) hat. Es scheint also so zu sein, dass der Relevanzwert für jeden Post entweder 0 oder 1, abhängig vom LAST-Tag des Posts, annimmt, anstatt auf der Basis von ALL-Tags kumulativ zu sein.

Antwort

1

Zuerst würde ich die gesamte Logik aus dem Controller nehmen. Stellen Sie sich Folgendes vor:

$post = $this->Post->find('first', array('conditions' => array('slug' => $slug))); 
$this->set('post', $post); 
$this->set('morePosts', $this->Post->findRelevant($post)); 

Jetzt ist Ihr Controller leicht zu lesen und macht seinen Job. Sie beginnen im Wesentlichen mit der Beschreibung der gewünschten Daten, indem Sie eine imaginäre Modellfunktion benennen. Anschließend schreiben Sie den Modellcode, um diese Anforderung zu erfüllen.

Also hier ist ein Stich in dem Modellcode:

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

Offensichtlich wäre es toll, die Datenbanklogik zu verwenden, um die Datensätze zu holen (wie Sie zu tun versuchen), so dass es so schnell wie möglich ist, und so dass Sie die Menge der Daten, die Sie abrufen, minimieren, aber ich kann nicht sehen, wie Sie das für das tun, was Sie erreichen möchten.

Diese Methode sollte gut funktionieren und ist nicht datenbankspezifisch. :)