Laravel-通过带有急切负载的数据透视表一对一关系 [英] Laravel - one-to-one relation through pivot table with eager load
问题描述
我有这种关系
- 运动可以有多个步骤
- 一个步骤可以属于多个动作
因此a必须创建一个数据透视表和一个belongsToMany
关系,但是我的数据透视表具有一些额外的列,例如finished
和order
So a had to create a pivot table and a belongsToMany
relationship, but my pivot table have some extras columns, like finished
and order
我想拥有两种关系,一种关系从运动中获取所有步骤,另一种关系从运动中获取当前步骤(最后完成的步骤)
I want to have two relationships, one to get all steps from a movement and another one to get the current step from the movement (the last finished step)
我知道如何获得所有步骤
I know how to get all steps
public function steps()
{
return $this->belongsToMany(MovementStep::class, 'movement_movement_steps')
->withPivot('order', 'finished')
->orderBy('pivot_order');
}
但是当前步骤如何?我需要这种关系,但是只返回一条记录并能够急于加载它,因为我将其传递给vue.js
But how about the current step? I need this kind of relationship, but returning only one record and be able to eager load it cause I'm passing it to vue.js
public function current_step()
{
return $this->belongsToMany(MovementStep::class, 'movement_movement_steps')
->withPivot('order', 'finished')
->where('finished', true)
->orderBy('pivot_order', 'desc');
}
注意,我想在没有附加程序包的情况下做到这一点
Notice, I'd like to do that without extras packages
alternative solution, but with extra package: Laravel hasOne through a pivot table (not the answer marked as correct, the answer from @cbaconnier)
推荐答案
与@mrhn提供的答案不同的方法是创建自定义关系. Spatie的Brent撰写了有关此问题的优秀文章
A different approach from the answer provided by @mrhn is to create a custom relationship. Brent from Spatie did an excellent article about it
尽管我的答案将执行与 staudenmeir的程序包提供的查询完全相同的查询它使我意识到,无论您使用的是软件包,此答案还是@mrhn答案,您都可以避免n + 1个查询,但仍然可能会导致大量水化模型.
Although my answer will do the exact same queries than the one provided by staudenmeir's package it makes me realized that either you use the package, this answer or @mrhn answer, you may avoid the n+1 queries but you may still ends up will a large amount of hydrated models.
在这种情况下,我认为不可能避免一种或另一种方法.缓存可能是一个答案.
In this scenario, I don't think it's possible to avoid one or the other approach. The cache could be an answer though.
Since I'm not entirely sure about your schema, I will provide my solution using the users-photos example from my previous answer.
User.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function photos()
{
return $this->belongsToMany(Photo::class);
}
public function latestPhoto()
{
return new \App\Relations\LatestPhotoRelation($this);
}
}
LastestPhotoRelation.php
<?php
namespace App\Relations;
use App\Models\User;
use App\Models\Photo;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\Relation;
class LatestPhotoRelation extends Relation
{
/** @var Photo|Builder */
protected $query;
/** @var User */
protected $user;
public function __construct(User $user)
{
parent::__construct(Photo::query(), $user);
}
/**
* @inheritDoc
*/
public function addConstraints()
{
$this->query
->join(
'user_photo',
'user_photo.photo_id',
'=',
'photos.id'
)->latest();
// if you have an ambiguous column name error you can use
// `->latest('movement_movement_steps.created_at');`
}
/**
* @inheritDoc
*/
public function addEagerConstraints(array $users)
{
$this->query
->whereIn(
'user_photo.user_id',
collect($users)->pluck('id')
);
}
/**
* @inheritDoc
*/
public function initRelation(array $users, $relation)
{
foreach ($users as $user) {
$user->setRelation(
$relation,
null
);
}
return $users;
}
/**
* @inheritDoc
*/
public function match(array $users, Collection $photos, $relation)
{
if ($photos->isEmpty()) {
return $users;
}
foreach ($users as $user) {
$user->setRelation(
$relation,
$photos->filter(function (Photo $photo) use ($user) {
return $photo->user_id === $user->id; // `user_id` came with the `join` on `user_photo`
})->first() // Photos are already DESC ordered from the query
);
}
return $users;
}
/**
* @inheritDoc
*/
public function getResults()
{
return $this->query->get();
}
}
用法
$users = \App\Models\User::with('latestPhoto')->limit(5)->get();
与Brent文章的主要区别在于,我们不使用Collection
,而是返回最新的照片Model
.
The main difference from Brent's article, is that instead of using a Collection
we are returning the latest Photo Model
.
这篇关于Laravel-通过带有急切负载的数据透视表一对一关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!