Как реализовать «связанные посты» в отношениях HABTM? - PullRequest
0 голосов
/ 05 июля 2019

Я использую CakePHP 3.7.7, и у меня есть рабочая ассоциация между моими сообщениями и тегами.В моем представлении «Сообщения» мне удалось перечислить все связанные теги через , содержащие , и все работает как нужно.

Однако ниже основного содержимого сообщения мне нужно показать некоторые «связанные сообщения» предложения.

Я искал ответ, и я думаю, что решение могло бы быть с использованием сопоставления , но я не смог получить желаемые результаты для моего запроса.

Мои модели:

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): пытаюсь получить свойство необъекта

Так что, очевидно, я не могу получить правильноемассив с идентификаторами тегов, соответствующими тем, которые относятся к текущему сообщению.

Как я могу заставить его работать?Или какой будет лучшая альтернатива?

1 Ответ

1 голос
/ 06 июля 2019

Предполагая, что $post является сущностью Post, свойства Tags не существует, поэтому $post->Tags вернет null, поэтому возникает ошибка при попытке доступа к свойству id возвращаемого значения.

По умолчанию имя свойства ассоциации belongsToMany представляет собой вариант имени ассоциации с множественным числом символов в нижнем регистре и подчеркиванием, поэтому в вашем случае tags.Однако это будет массив, поэтому, конечно, вы не сможете получить к нему доступ и к свойству id.

Если вы хотите найти похожие посты на основе тегов, которые они разделяют, то вам либо понадобится списокиз всех идентификаторов тегов (не только одного), или вы должны сделать свой запрос немного более сложным и, например, сопоставить с подзапросом, который выбирает текущие теги записей.В вашем коде есть и другие проблемы, например, у вас нет конкретной ассоциации PostsTags (поэтому вы не можете содержать ее или сопоставить с ней), вы передаете неправильную переменную в замыкание, вам нужно сгруппироватьпо первичному ключу постов, чтобы избежать дубликатов, и вы, вероятно, захотите исключить пост, который у вас уже есть.

Вот быстрый и грязный пример с использованием уже запрошенных тегов, сначала извлеките все идентификаторы, затем запросите посты на основена этих идентификаторах, исключая текущее сообщение:

$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, прежде чем работать с ним!

Подзапрос для извлеченияидентификаторы тегов могут, например, выглядеть следующим образом:

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

См. также

...