如何在HABTM关系中实现“相关职位”?

时间:2019-07-05 19:33:41

标签: cakephp associations query-builder cakephp-3.7

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

但是,在主要帖子内容下方,我需要显示一些“相关帖子”建议。

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

我的模特:

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

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

  

通知(8):试图获取非对象的属性

因此,我显然无法使用与当前帖子相关的标签id来获得正确的数组。

我如何使其起作用?还是会有更好的选择?

1 个答案:

答案 0 :(得分:1)

假设$postPost实体,则没有Tags属性,因此$post->Tags将返回null,因此在您尝试访问时会出错返回值的id属性。

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

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

这是一个使用已查询标签的快速且肮脏的示例,首先提取所有ID,然后根据这些ID查询帖子,但不包括当前帖子:

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

另请参见