如何在4个表中使用Laravel的hasManyThrough

时间:2016-03-25 21:29:59

标签: php laravel laravel-5 laravel-5.2

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

User
Accounts
Contacts
Orders

关系如下:

$用户>的hasMany( '账户') - >的hasMany( '联系人') - >的hasMany( '订货');

/** 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;

3 个答案:

答案 0 :(得分:5)

首先很难确定查询构建器如何将事物切片在一起。我知道我想用原始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方法是否位于现有relations的{​​{1}}数组中。如果不是,那么它会调用我们的 next 函数来填充关系,最后返回我们刚创建的关系。

这就是实际查询Orders的函数:

User model

在上面,我们告诉Orders表加入它的联系人(这是在这种情况下给定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; } 所有权的既定路线)。然后,通过联系人,我们加入了帐户,然后通过我们的belongsTo()列与现有owner_id相匹配,从我们的用户那里获得的帐户,因此我们不需要做任何事情进一步

接下来,我们需要通过从$user->id构建器实例化hasMany的实例来手动创建我们的关系。

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

HasMany

$hasMany = new Illuminate\Database\Eloquent\Relations\HasMany(User::query(), $this, 'accounts.owner_id', 'id'); 期望以下内容为其构造函数:

HasOneOrMany

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

一旦我们从上面的Builder $query, Model $parent, $foreignKey, $localKey 声明中创建了Relation的实例,我们就需要将关系结果与其众多父母进行匹配。我们使用HasMany方法执行此操作,该方法接受3个参数:

matchMany()

所以在这种情况下,模型数组将是我们当前雄辩模型(用户)的数组实例,它可以包装在一个数组中以实现我们的效果。

第二个参数将是我们的array $models, Collection $results, $relation 函数中的初始$orders查询的结果。

最后,第三个参数将是我们希望用来获取这种关系实例的关系orders();对我们来说是string

现在您可以更正使用Eloquent或Lazy Loading来获取我们用户的订单。

order

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

答案 1 :(得分:3)

您可以通过用户与function findNumber(arr, number){ var result = false; arr.forEach(function(item, index){ if(item === number) result = true; }); return result;

联系join
Orders

在同样的情况下,它对我有用,欢迎所有反馈

答案 2 :(得分:2)

我创建了一个HasManyThrough级的无限关系:Repository on GitHub

安装后,您可以像这样使用它:

class User extends Model {
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function orders() {
        return $this->hasManyDeep(Order::class, [Account::class, Contact::class]);
    }
}