У меня есть 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
больше не содержит оригинальный пост. Обратите внимание или используйте другое имя переменной для соответствующих сообщений.
благодаря @arilia , Но я хочу разделить эти два вопроса. Я думаю, это одно и то же, но с использованием 'matching()'. Это правильно? Но в этом случае есть проблема: этот код может занимать дважды тот же пост? –
Я не понимаю: вы сказали в OP, что хотите только один запрос, и теперь вы хотите два. – arilia
Предположим, что у моего сообщения есть теги 'a' и' b'. У другого сообщения есть те же теги. Эта же статья (вторая) будет получена дважды, для каждого тега? –