如何在4个表中使用Laravel的hasManyThrough [英] How to use Laravel's hasManyThrough across 4 tables

查看:361
本文介绍了如何在4个表中使用Laravel的hasManyThrough的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有4个表,其结构和流程如下:

I have 4 tables with a structure and flow like this:

User
Accounts
Contacts
Orders

关系如下:

$ user-> hasMany('accounts')-> hasMany('contacts')-> hasMany('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张桌子.

A classic hasManyThrough is not possible because we have 4 tables.

如何创建关系,以便单个用户可以访问其订单,而无需链接每个模型的方法,例如:

How can I create a relationship so that a single user can have it's orders accessed without method chaining each model, such as:

User::find(8)->orders;

推荐答案

一开始很难准确地确定查询构建器如何将事物切成薄片.我知道我想如何使用原始SQL语句来格式化查询,但是鉴于其他已定义的关系,我想将Eloquent用于此特定任务.

It was quite difficult at first to identify exactly how the query builder slices things together. I knew how I wanted to format the query with a raw SQL statement, but I wanted to use Eloquent for this particular task given the other defined relationships.

我能够通过重载属性并手动创建关系来解决此问题.但是,实际的查询和hasMany也需要手动构建.让我们看看我是如何实现的.请记住,目标是通过2个has-many关系使用户离开Orders.

I was able to resolve this by overloading the attribute and manually creating the relation. However, the actual query and hasMany needs to be built manually as well. Let's take a look at how I achieved this. Remember, the goal is to get Orders off of the user through 2 has-Many relationships.

首先,属性的重载.

public function getOrdersAttribute()
{
    if( ! array_key_exists('orders', $this->relations)) $this->orders();

    return $this->getRelation('orders');
}

以上函数的想法是捕获延迟加载的->orders的调用时间.例如$user->orders.这会检查orders方法是否在现有User modelrelations数组中.如果不是,那么它将调用我们的 next 函数以填充关系,并最终返回我们刚刚创建的关系.

The idea in the above function is to capture when a lazy loaded ->orders is called. such as $user->orders. This checks to see if the orders method is in the relations array for the existing User model. If it's not, then it calls our next function in order to populate the relatioship, and finally returns the relationship we've just created.

这是实际查询订单的函数的样子:

This is what the function that actually queries the Orders looks like:

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;
}

在上面,我们告诉Orders表加入它的联系人(在这种情况下,这是建立的路径,由于拥有belongsTo()的所有权).然后从联系人中加入帐户,然后从帐户中将owner_id列与现有的$user->id进行匹配,从而可以从用户那里获得该帐户,因此我们不需要做任何其他事情.

In the above, we tell the Orders table to join it's contacts (which is the established route given the ownership of belongsTo() in this scenario). Then from the contacts, we join the accounts, then from the accounts we can get there from our user by matching our owner_id column against our existing $user->id, so we don't need to do anything further.

接下来,我们需要通过实例化Eloquent Relationship构建器中的hasMany实例来手动创建我们的关系.

Next, we need to manually create our relationship by instantiating an instance of hasMany from the Eloquent Relationship builder.

鉴于HasMany方法实际上扩展了HasOneOrMany抽象类,我们可以通过将参数直接传递给HasMany来到达HasOneOrMany抽象类,如下所示:

Given that the HasMany method actually extends the HasOneOrMany abstract class, we can reach the HasOneOrMany abstract class by passing our arguments directly to HasMany, like below:

$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(User::query(), $this, 'accounts.owner_id', 'id');

HasOneOrMany希望其构造函数具有以下特征:

The HasOneOrMany expects the following to it's constructor:

Builder $query,
Model $parent,
$foreignKey, 
$localKey

因此对于我们的构建器查询,我们已经传递了一个我们希望与之建立关系的模型实例,第二个参数是我们的Model($ this)的实例,第三个参数是来自的外键约束我们的Current-> 2nd模型,最后一个参数是当前模型与Current-> 2nd模型的外键约束匹配的列.

So for our builder query, we've passed an instance of our model that we wish to establish the relationship with, the 2nd argument being an instance of our Model ($this), the 3rd argument being the foreign key constraint from our Current->2nd model, and finally the last argument being the column to match from our current model against the foreign key constraint on our Current->2nd model.

一旦我们从上面的HasMany声明中创建了Relation的实例,则需要将关系的结果与他们的许多父项进行匹配.我们使用matchMany()方法执行此操作,该方法接受3个参数:

Once we've created our instance of Relation from our HasMany declaration above, we then need to match the results of the relationship to their many parents. We do this with the matchMany() method which accepts 3 arguments:

array $models,
Collection $results,
$relation

因此,在这种情况下,模型数组将是我们当前雄辩的模型(用户)的数组实例,可以将其包装在数组中以达到我们的效果.

So in this case, the array of models would be an array instance of our current eloquent model (user) which can be wrapped in an array to achieve our effect.

第二个参数将是我们在orders()函数中进行初始$orders查询的结果.

The 2nd argument would be the result of our intitial $orders query in our orders() function.

最后,第三个参数将是关系string,我们希望使用该关系来获取此关系的实例;对我们来说是order.

Finally, the 3rd argument will be the relation string that we wish to use to fetch our instance of this relationship; which for us is order.

现在,您可以正确地使用口才"或延迟加载"来为我们的用户获取订单.

Now you can correct use either Eloquent or Lazy Loading to fetch our orders for our user.

User::find(8)->orders();

$user->orders;

希望这对面临类似问题的其他人很有帮助.

Hopefully this is helpful for someone else facing a similar issue.

这篇关于如何在4个表中使用Laravel的hasManyThrough的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆