如何在 HABTM 关系中实现 "related posts"?

How to implement "related posts" in a HABTM relationship?

我使用 CakePHP 3.7.7,并且我的 Posts 和 Tags 模型之间有一个有效的 belongsToMany 关联。在我的帖子视图中,我设法通过 contain 列出了所有关联的标签,并且一切正常。

但是,在主要 post 内容下方,我需要显示一些 "related posts" 建议。

我一直在寻找答案,我认为解决方案可能是使用 匹配,但我无法获得查询所需的结果。

我的模特:

class PostsTable extends Table
{
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('posts');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');

        $this->belongsToMany('Tags', [
            'foreignKey' => 'post_id',
            'targetForeignKey' => 'tag_id',
            'joinTable' => 'posts_tags',
    'dependant' => false
        ]);
    }
}

class TagsTable extends Table
{
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('Tags');
        $this->setDisplayField('title');
        $this->setPrimaryKey('id');

        $this->belongsTo('Tags', [
            'foreignKey' => 'tag_id',
            'joinType' => 'INNER'
        ]);

    $this->belongsToMany('Posts');
    }
}

class PostsTagsTable extends Table
{
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('posts_tags');
        $this->setDisplayField('id');
        $this->setPrimaryKey('id');

        $this->belongsTo('Posts', [
            'foreignKey' => 'post_id',
            'joinType' => 'INNER'
        ]);
        $this->belongsTo('Tags', [
            'foreignKey' => 'tag_id',
            'joinType' => 'INNER'
        ]);
    }
}

我的控制器:

class PostsController extends AppController
{
    public function view($slug = null)
    {
        $post = $this->Posts->findBySlug($slug)
        ->contain(['Tags'])
        ->first();

        $this->set('post', $post);

    }
}

我试着在我的视图函数中添加这个:

$relId = $post->Tags->id;

$related = $this->Posts->find('all')
    ->contain(['PostsTags','Tags'])
    ->matching('PostsTags', function(\Cake\ORM\Query $query) use ($post) {
        return $query->where([
            'PostsTags.tag_id' => $relPost
            ]);
        })
    ->limit(3)
    ->execute();

$this->set('relatedPosts', $related);

...但这不起作用。我不断收到错误通知:

Notice (8): Trying to get property of non-object

所以我显然无法获得正确的数组,其中的标签 ID 与当前 post 相关的标签 ID 相对应。

我怎样才能让它发挥作用?或者更好的选择是什么?

假设$post是一个Post实体,没有Tags属性,所以$post->Tags会returnnull,因此当您尝试访问 returned 值上的 id 属性 时出现错误。

默认情况下,belongsToMany 关联的 属性 名称是关联名称的复数、小写、带下划线的变体,因此在您的情况下 tags。但是它是一个数组,所以当然你也不能访问它上面的 id 属性。

如果您想根据他们共享的标签找到相关的 post,那么您要么需要所有标签 ID 的列表(不仅仅是一个标签 ID),要么必须制作您的查询稍微复杂一点,例如匹配获取当前 posts 标签的子查询。您的代码还有其他一些问题,例如您没有具体的 PostsTags 关联(因此您不能包含或匹配它),您将错误的变量传递给闭包,您需要按 posts 主键分组以避免重复,您可能想排除已有的 post。

这是一个使用已经查询过的标签的快速而肮脏的示例,首先提取所有 ID,然后根据这些 ID 查询 post,排除当前的 post:

$tagIds = collection($post->tags)->extract('id')->toArray();

if (!empty($tagIds)) {
    $relatedPosts = $this->Posts
        ->find()
        ->matching('Tags', function(\Cake\ORM\Query $query) use ($tagIds) {
            return $query->where([
                'Tags.id IN' => $tagIds
            ]);
        })
        ->where([
            'Posts.id !=' => $post->id
        ])
        ->group('Posts.id')
        ->limit(3);
} else {
    $relatedPosts = null;
}

$this->set('relatedPosts', $relatedPosts);

在您看来,您必须在使用它之前检查 $relatedPosts 是否为 null

获取标签 ID 的子查询例如如下所示:

$tagIds = $this->Posts->Tags
    ->junction()
    ->find()
    ->select(['PostsTags.tag_id'])
    ->where([
        'PostsTags.post_id' => $post->id
    ]);

另见