Запрос отношения «многие ко многим» и некоторых других данных с помощью Laravel (Lumen/Eloquent)

У меня три стола.

Таблица 1: posts // Хранение сообщений пользователя в Twitter
Таблица 2: tags // Хранение тегов, используемых в сообщениях Twitter
Таблица 3: post_tag // сводная таблица

Теперь я хочу ввести tag, например. 'yolo' и получить все посты с этим хэштегом. Это довольно легко, и я сделал это так

Сообщение класса:

/**
* Get the tags associated with the post
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function tags()
{
    return $this->belongsToMany('App\Models\Tag')->get(['tag']);
}

Тег класса:

/**
 * Get the posts associated with the given tag
 *
 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
 */
public function posts()
{
    return $this->belongsToMany('App\Models\Post');
}

Доступ к данным:

/** @var Tag $tag */
try {
    $tag    = Tag::where('tag', urldecode($passedTag))->firstOrFail();
    return response()->json(['posts' => $tag->posts->toArray()]);
} catch (ModelNotFoundException $e) {
    return response()->json(['errors' => 'No query results for: '. $passedTag]);
}

Однако то, что я хочу дополнительно сделать, это также получить все другие теги, которые использовались для публикации. Например. пост имеет теги yolo и swag. Теперь, когда пользователь вводит yolo, я хочу, чтобы приложение показывало все сообщения, помеченные yolo, а также отображало все другие теги, которые есть у сообщения, в этом примере swag. я сделал вот так

/** @var Tag $tag */
try {
    $tag    = Tag::where('tag', urldecode($passedTag))->firstOrFail();
    $posts  = $tag->posts;
    $fData  = [];
    foreach ($posts as $index=>$post) {
        /** @var Post $post */
        $fData[$index] = $post->toArray();
        $fData[$index]['tags'] = $post->tags();
    }
    return response()->json(['posts' => $fData]);
} catch (ModelNotFoundException $e) {
    return response()->json(['errors' => 'No query results for: '. $passedTag]);
}

но поскольку я новичок в Laravel (Lumen), я уверен, что есть более быстрый способ сделать это. Любые советы, как бы я сделал это более производительным? Спасибо! :)


person Musterknabe    schedule 29.05.2015    source источник


Ответы (1)


Вы можете Eager загрузить все необходимые отношения. Они также будут отображаться в toArray.

/** @var Tag $tag */
try {
    return response()->json([
        'posts' => Tag::with('posts')->with([
            'posts.tags' => function ($query) {
                $query->addSelect(['tag']);
             }
        ])
        ->where('tag', urldecode($passedTag))
        ->firstOrFail()->posts->toArray()
    ]);
} catch (ModelNotFoundException $e) {
    return response()->json(['errors' => 'No query results for: '. $passedTag]);
}

Нетерпеливая загрузка означает, что вы можете предварительно загрузить любые отношения объекта во время извлечения самого объекта. Это не только уменьшит нагрузку на запросы, но и предоставит вам все необходимые данные без дополнительных циклов или обращений к базе данных. Как видите, вы можете вложить нетерпеливую загрузку, чтобы загрузить отношения отношений - posts.tags

ИЗМЕНИТЬ

Эта нетерпеливая загрузка отношения доставит вам только столбец тегов из связанных тегов:

'posts.tags' => function ($query) {
    $query->addSelect(['tag']);
}
person Sh1d0w    schedule 29.05.2015
comment
Спасибо, не знал, что так можно связать. Однако при использовании firstOrFail() я получаю сообщение об ошибке Fatal error: Call to undefined method Illuminate\Database\Eloquent\Collection::addEagerConstraints() - person Musterknabe; 29.05.2015
comment
@Musterknabe Хм, я тестирую это в атм Laravel 5, и он работает отлично, но, возможно, в Lumen есть проблема. Дайте мне минуту, чтобы установить Lumen и проверить это. - person Sh1d0w; 29.05.2015
comment
Потрясающий. Спасибо. Да, может быть, это как-то связано с LUmen. - person Musterknabe; 29.05.2015
comment
Просто чтобы было понятнее. У меня возникла эта проблема только тогда, когда у меня был такой код: Tag::with('posts', 'posts.tags'), но не тогда, когда я получил его следующим образом: Tag::with('posts') - person Musterknabe; 29.05.2015
comment
Это работает так, как вы предложили, когда у меня есть метод tags () следующим образом: return $this->belongsToMany('App\Models\Tag');, но не так return $this->belongsToMany('App\Models\Tag')->get(['tag']);, однако я хочу получить только тег. не все - person Musterknabe; 29.05.2015
comment
Да, это неправильно $this->belongsToMany('App\Models\Tag')->get(['tag']), так должно быть $this->belongsToMany('App\Models\Tag') Потому что в вашем посте много тегов. Это неправильный способ определения отношений, как у вас. В настоящее время то, что вы сделали, возвращает коллекцию в качестве результата, но она должна возвращать отношение. - person Sh1d0w; 29.05.2015
comment
Привет, я отправил ответ с двумя фрагментами кода - person Musterknabe; 29.05.2015
comment
Да, это неправильно, однако я хочу отображать только поле тега класса Tag, а не все остальные поля. Я знаю, что мог бы добавить материал hidden, однако я хочу скрыть его только для этого отношения, а не вообще - person Musterknabe; 29.05.2015
comment
Хорошо, исправлено! Использование return $this->belongsToMany('App\Models\Tag')->select(['tag']); вместо получения работ! :) Большое спасибо за Вашу помощь - person Musterknabe; 29.05.2015
comment
@Musterknabe Я изменил свой ответ, чтобы получить только столбец tag из тегов, как вам нужно. Пожалуйста, дайте мне знать, если изменение работает для вас :) - person Sh1d0w; 29.05.2015
comment
Спасибо. Ваш подход еще лучше, потому что он ограничивает его только конкретным методом, а не глобально для отношения! Круто, спасибо :) - person Musterknabe; 29.05.2015