CakePHP 3.x:查询相关帖子

CakePHP 3.x:查询相关帖子,cakephp,cakephp-3.0,Cakephp,Cakephp 3.0,我有帖子和标签模型,与“属于许多人”关联 因此,在我的数据库中,我有posts和tags表和posts\u tags表,其中包含post\u id和tag\u id字段。tags表有post\u count字段,显示属于该标签的帖子数量 当我得到一个帖子时,我也会得到与之相关的标签 $this->Posts->find() ->contain(['Tags']) ->where(['Posts.id' => $id]) ->first(

我有
帖子
标签
模型,与“属于许多人”关联

因此,在我的数据库中,我有
posts
tags
表和
posts\u tags
表,其中包含
post\u id
tag\u id
字段。
tags
表有
post\u count
字段,显示属于该标签的帖子数量

当我得到一个帖子时,我也会得到与之相关的标签

$this->Posts->find()
    ->contain(['Tags'])
    ->where(['Posts.id' => $id])
    ->first());
现在,对于每个标记,我想得到一个包含该标记的帖子,不包括初始帖子、排序的创建日期(
created
field)。 重要的是,对于每个标签,都会获得一个帖子,而不是那些已经获得的帖子

我可以使用一个
foreach
,通过排除初始帖子的id和已经获取的id,为每个标记获取一个包含它的帖子

我想知道我是否可以用一个查询来实现这一点,并且有一个示例可以使用

谢谢


编辑
临时解决方案,对每个标记使用查询

首先,我得到了主要的帖子:

$post = $this->Posts->find()
    ->contain(['Tags'])
    ->where(['Posts.id' => $id])
    ->first();
在这种情况下,post是通过其ID恢复的,但您可以采用不同的方法。您也可以使用缓存。
重要的是
$post
变量

现在(这里有一个使用缓存的好主意…):

注意:在此代码之后,
$post
变量不再包含原始post。请注意,或为相关帖子使用不同的变量名。

试试这个(未测试)


但是这样你可能会得到两次帖子(我现在知道你不想要这个)

谢谢@arilia。但我想把这两个问题分开。我想这是一样的,但是使用
matching()
。是这样吗?但是在这种情况下有一个问题:这个代码可能会占用同一个帖子的两倍?我不明白:你在OP中说你只需要一个查询,现在你需要两个。假设我的帖子有
a
b
标签。另一个帖子有相同的标签。同一篇文章(第二篇)将获得两次,每个标签?我正在尝试你的代码。似乎
limit()
有问题,它总是只得到一条记录,每个标记都应该有一条Post。不是原始帖子的最新帖子(或者更可能是最老的,我猜,只要我是由asc创建的)。是的:如果一篇文章有a和b标签,你会得到两次,除非有另一篇老文章有b标签
//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);
$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());