如何跨 4 个表使用 Laravel 的 hasManyThrough
How to use Laravel's hasManyThrough across 4 tables
我有 4 个表,其结构和流程如下:
User
Accounts
Contacts
Orders
关系如下:
$user->hasMany('accounts')->hasMany('contacts')->hasMany('orders');
/** User Model **/
class User extend Eloquent {
public function accounts(){
return $this->hasMany('Accounts');
}
public function contacts(){
return $this->hasManyThrough('Contact', 'Account', 'owner_id');
}
//how do I get this?
public function orders(){
}
}
/** Account Model */
class Account extends Eloquent {
public function $user(){
return $this->belongsTo('User');
}
public function contacts(){
return $this->hasMany('Contact');
}
}
/** Contact Model **/
class Contact extends Eloquent {
public function account(){
return $this->belongsTo('Account');
}
public function orders(){
return $this->hasMany('Order');
}
}
/** Order Model **/
class Order extends Eloquent {
public function contact(){
return $this->belongsTo('contact');
}
}
经典的hasManyThrough
是不可能的,因为我们有4张桌子。
如何创建关系,以便单个用户可以访问其订单,而无需链接每个模型的方法,例如:
User::find(8)->orders;
一开始很难确定查询构建器是如何将事物切分在一起的。我知道我想如何使用原始 SQL 语句格式化查询,但我想在给定其他已定义关系的情况下将 Eloquent 用于此特定任务。
我能够通过重载属性并手动创建关系来解决这个问题。但是,实际的查询和 hasMany 也需要手动构建。让我们来看看我是如何做到这一点的。请记住,我们的目标是通过 2 个有很多的关系让 Orders
离开用户。
首先,属性的重载。
public function getOrdersAttribute()
{
if( ! array_key_exists('orders', $this->relations)) $this->orders();
return $this->getRelation('orders');
}
上述函数的思路是在调用惰性加载 ->orders
时进行捕获。比如$user->orders
。这将检查 orders
方法是否在现有 User model
的 relations
数组中。如果不是,那么它会调用我们的 next 函数来填充关系,最后 returns 我们刚刚创建的关系。
实际查询订单的函数如下所示:
public function orders()
{
$orders = Order::join('contacts', 'orders.contact_id', '=', 'contacts.id')
->join('accounts', 'contacts.account_id', '=', 'accounts.id')
->where('accounts.owner_id', $this->getkey())
->get();
$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(User::query(), $this, 'accounts.owner_id', 'id');
$hasMany->matchMany(array($this), $orders, 'orders');
return $this;
}
在上面,我们告诉订单 table 加入它的联系人(在这种情况下,这是给定 belongsTo()
所有权的既定路线)。然后我们从联系人中加入帐户,然后从帐户中我们可以通过将我们的 owner_id
列与我们现有的 $user->id
相匹配来从我们的用户那里获得,所以我们不需要做任何进一步的事情。
接下来,我们需要通过从 Eloquent Relationship
构建器实例化 hasMany
的实例来手动创建我们的关系。
鉴于 HasMany
方法实际上扩展了 HasOneOrMany
抽象 class,我们可以通过将参数直接传递给 HasOneOrMany
抽象 class HasMany
,如下图:
$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(User::query(), $this, 'accounts.owner_id', 'id');
HasOneOrMany
期望其构造函数具有以下内容:
Builder $query,
Model $parent,
$foreignKey,
$localKey
所以对于我们的构建器查询,我们传递了一个我们希望与之建立关系的模型实例,第二个参数是我们模型的一个实例($this),第三个参数是外键Current->2nd 模型的约束,最后一个参数是当前模型与 Current->2nd 模型的外键约束相匹配的列。
一旦我们从上面的 HasMany
声明中创建了 Relation
的实例,我们就需要将关系的结果与它们的许多父项相匹配。我们使用接受 3 个参数的 matchMany()
方法来做到这一点:
array $models,
Collection $results,
$relation
所以在这种情况下,模型数组将是我们当前 eloquent 模型(用户)的数组实例,可以将其包装在数组中以实现我们的效果。
第二个参数将是我们在 orders()
函数中初始 $orders
查询的结果。
最后,第三个参数将是我们希望用来获取此关系实例的关系 string
;这对我们来说是 order
.
现在您可以正确使用 Eloquent 或延迟加载来为我们的用户获取订单。
User::find(8)->orders();
$user->orders;
希望这对面临类似问题的其他人有所帮助。
您可以通过用户联系然后 join
与 Orders
public function orders(){
return $this->hasManyThrough('Contact', 'Account', 'owner_id')->join('orders','contact.id','=','orders.contact_ID')->select('orders.*');
}
对我来说同样有效,欢迎大家反馈
我创建了 HasManyThrough
无限级别的关系:Repository on GitHub
安装完成后可以这样使用:
class User extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function orders() {
return $this->hasManyDeep(Order::class, [Account::class, Contact::class]);
}
}
我有 4 个表,其结构和流程如下:
User
Accounts
Contacts
Orders
关系如下:
$user->hasMany('accounts')->hasMany('contacts')->hasMany('orders');
/** User Model **/
class User extend Eloquent {
public function accounts(){
return $this->hasMany('Accounts');
}
public function contacts(){
return $this->hasManyThrough('Contact', 'Account', 'owner_id');
}
//how do I get this?
public function orders(){
}
}
/** Account Model */
class Account extends Eloquent {
public function $user(){
return $this->belongsTo('User');
}
public function contacts(){
return $this->hasMany('Contact');
}
}
/** Contact Model **/
class Contact extends Eloquent {
public function account(){
return $this->belongsTo('Account');
}
public function orders(){
return $this->hasMany('Order');
}
}
/** Order Model **/
class Order extends Eloquent {
public function contact(){
return $this->belongsTo('contact');
}
}
经典的hasManyThrough
是不可能的,因为我们有4张桌子。
如何创建关系,以便单个用户可以访问其订单,而无需链接每个模型的方法,例如:
User::find(8)->orders;
一开始很难确定查询构建器是如何将事物切分在一起的。我知道我想如何使用原始 SQL 语句格式化查询,但我想在给定其他已定义关系的情况下将 Eloquent 用于此特定任务。
我能够通过重载属性并手动创建关系来解决这个问题。但是,实际的查询和 hasMany 也需要手动构建。让我们来看看我是如何做到这一点的。请记住,我们的目标是通过 2 个有很多的关系让 Orders
离开用户。
首先,属性的重载。
public function getOrdersAttribute()
{
if( ! array_key_exists('orders', $this->relations)) $this->orders();
return $this->getRelation('orders');
}
上述函数的思路是在调用惰性加载 ->orders
时进行捕获。比如$user->orders
。这将检查 orders
方法是否在现有 User model
的 relations
数组中。如果不是,那么它会调用我们的 next 函数来填充关系,最后 returns 我们刚刚创建的关系。
实际查询订单的函数如下所示:
public function orders()
{
$orders = Order::join('contacts', 'orders.contact_id', '=', 'contacts.id')
->join('accounts', 'contacts.account_id', '=', 'accounts.id')
->where('accounts.owner_id', $this->getkey())
->get();
$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(User::query(), $this, 'accounts.owner_id', 'id');
$hasMany->matchMany(array($this), $orders, 'orders');
return $this;
}
在上面,我们告诉订单 table 加入它的联系人(在这种情况下,这是给定 belongsTo()
所有权的既定路线)。然后我们从联系人中加入帐户,然后从帐户中我们可以通过将我们的 owner_id
列与我们现有的 $user->id
相匹配来从我们的用户那里获得,所以我们不需要做任何进一步的事情。
接下来,我们需要通过从 Eloquent Relationship
构建器实例化 hasMany
的实例来手动创建我们的关系。
鉴于 HasMany
方法实际上扩展了 HasOneOrMany
抽象 class,我们可以通过将参数直接传递给 HasOneOrMany
抽象 class HasMany
,如下图:
$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(User::query(), $this, 'accounts.owner_id', 'id');
HasOneOrMany
期望其构造函数具有以下内容:
Builder $query,
Model $parent,
$foreignKey,
$localKey
所以对于我们的构建器查询,我们传递了一个我们希望与之建立关系的模型实例,第二个参数是我们模型的一个实例($this),第三个参数是外键Current->2nd 模型的约束,最后一个参数是当前模型与 Current->2nd 模型的外键约束相匹配的列。
一旦我们从上面的 HasMany
声明中创建了 Relation
的实例,我们就需要将关系的结果与它们的许多父项相匹配。我们使用接受 3 个参数的 matchMany()
方法来做到这一点:
array $models,
Collection $results,
$relation
所以在这种情况下,模型数组将是我们当前 eloquent 模型(用户)的数组实例,可以将其包装在数组中以实现我们的效果。
第二个参数将是我们在 orders()
函数中初始 $orders
查询的结果。
最后,第三个参数将是我们希望用来获取此关系实例的关系 string
;这对我们来说是 order
.
现在您可以正确使用 Eloquent 或延迟加载来为我们的用户获取订单。
User::find(8)->orders();
$user->orders;
希望这对面临类似问题的其他人有所帮助。
您可以通过用户联系然后 join
与 Orders
public function orders(){
return $this->hasManyThrough('Contact', 'Account', 'owner_id')->join('orders','contact.id','=','orders.contact_ID')->select('orders.*');
}
对我来说同样有效,欢迎大家反馈
我创建了 HasManyThrough
无限级别的关系:Repository on GitHub
安装完成后可以这样使用:
class User extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function orders() {
return $this->hasManyDeep(Order::class, [Account::class, Contact::class]);
}
}