Reputation: 41
I use CakePHP 3.7.7, and I have a working belongsToMany association between my Posts and Tags models. In my Posts view I managed to list all the associated Tags via contain, and everything works as desired.
However, below the main post content I need to show some "related posts" suggestions.
I have been searching for an answer, and I think the solution might be by using matching, but I haven't been able to get the desired results for my query.
My Models:
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'
]);
}
}
And my controller:
class PostsController extends AppController
{
public function view($slug = null)
{
$post = $this->Posts->findBySlug($slug)
->contain(['Tags'])
->first();
$this->set('post', $post);
}
}
I tried adding this in my view function:
$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);
...but that doesn't work. I keep getting an error notice:
Notice (8): Trying to get property of non-object
So I'm obviously not being able to get a correct array with the tags ids corresponding to those related to the current post.
How can I make it work? Or what would a better alternative be?
Upvotes: 0
Views: 54
Reputation: 60463
Assuming $post
is a Post
entity, there is no Tags
property, so $post->Tags
will return null
, hence the error when you try to access the id
property on the returned value.
By default the property name of a belongsToMany
association is the pluralized, lower cased, underscored variant of the association name, so in your case tags
. However it would be an array, so of course you cannot access an id
property on it either.
If you want to find related posts based on the tags they share, then you'll either need a list of all tag IDs (not just a single one), or you have to make your query a little more complex and for example match against a subquery that fetches the currents posts tags. There's some other stuff wrong with your code, for example you don't have a concrete PostsTags
association (so you can't contain or match against it), you're passing the wrong variable to the closure, you need to group by the posts primary key to avoid duplicates, and you probably want to exclude the post that you already have.
Here's a quick and dirty example using the already queried tags, first extract all IDs, then query the posts based on those IDs, excluding the current 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);
In your view you'd have to check whether $relatedPosts
is null
before working with it!
A subquery for fetching the tag IDs could for example look like this:
$tagIds = $this->Posts->Tags
->junction()
->find()
->select(['PostsTags.tag_id'])
->where([
'PostsTags.post_id' => $post->id
]);
See also
Upvotes: 1