2015-11-26 5 views
1

У меня есть Posts и Tags модели, связанные с ассоциацией «принадлежит многим».CakePHP 3.x: запрос для связанных должностей

Так в моей базе данных у меня есть posts и tags столы и posts_tags стол с post_id и tag_id полей. Таблица tags имеет поле post_count, отображающее количество сообщений, относящихся к этому тегу.

Когда я получаю сообщение, я получаю также теги, связанные с ним.

$this->Posts->find() 
    ->contain(['Tags']) 
    ->where(['Posts.id' => $id]) 
    ->first()); 

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

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

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

Спасибо.


РЕДАКТИРОВАТЬ
Временное решение, которое использует запрос для каждого тега

Во-первых, я главный пост:

$post = $this->Posts->find() 
    ->contain(['Tags']) 
    ->where(['Posts.id' => $id]) 
    ->first(); 

В этом случае пост извлеченного через его идентификатор, но вы можете сделать по-другому. Также вы можете использовать кеш.
Важная вещь является переменной $post.

В настоящее время (здесь не является хорошей идеей, чтобы использовать кэш ...):

//Tries to gets related posts from cache 
$related = Cache::read($cache = sprintf('related_posts_for_%s', $post->id), 'posts'); 

if(empty($related)) { 
    $tags = $post->tags; 

    //Re-orders tags, using the "post_count" field, then based on the popularity of tags 
    usort($tags, function($a, $b) { return $b['post_count'] - $a['post_count']; }); 

    //Gets only the first 5 tags 
    $tags = array_slice($tags, 0 , 5); 

    //This array will be contain the ID to be excluded 
    $exclude = [$post->id]; 

    //Gets a related post for each tag 
    //Reveres the tags order, because the tags less popular have less chance to find a related post 
    foreach(array_reverse($tags) as $tag) { 
     $post = $this->Posts->find('active') 
      ->select(['id', 'title', 'slug']) 
      ->matching('Tags', function($q) use($tag) { 
       return $q->where(['Tags.id' => $tag->id]); 
      }) 
      ->where(['Posts.id NOT IN' => $exclude]) 
      ->first(); 

     //Adds the post to the related posts and its ID to the IDs to be excluded for the next query 
     if(!empty($post)) { 
      $related[] = $post; 
      $exclude[] = $post->id; 
     } 
    } 

    Cache::write($cache, $related, 'posts'); 
} 

//Related posts 
debug($related); 

Примечание: после этого кода, переменная $post больше не содержит оригинальный пост. Обратите внимание или используйте другое имя переменной для соответствующих сообщений.

ответ

1

попробовать это (не тестировал)

$this->Posts->find() 
->contain([ 
    'Tags', 
    'Tags.Posts' => function($q) use($id) { 
     $q->where(['id !=' => $id]) 
      ->order(['created' => 'desc']) 
      ->limit(1); 
     return $q; 
    } 
    ]) 
->where(['Posts.id' => $id]) 
->first()); 

Но в этом случае вы можете получить сообщение в два раза (и теперь я вижу, что вы не хотите этого)

+0

благодаря @arilia , Но я хочу разделить эти два вопроса. Я думаю, это одно и то же, но с использованием 'matching()'. Это правильно? Но в этом случае есть проблема: этот код может занимать дважды тот же пост? –

+0

Я не понимаю: вы сказали в OP, что хотите только один запрос, и теперь вы хотите два. – arilia

+0

Предположим, что у моего сообщения есть теги 'a' и' b'. У другого сообщения есть те же теги. Эта же статья (вторая) будет получена дважды, для каждого тега? –

Смежные вопросы