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

Ohg*_*why 5 php laravel laravel-5 laravel-5.2

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

User
Accounts
Contacts
Orders
Run Code Online (Sandbox Code Playgroud)

关系如下:

$用户>的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');
    }
}
Run Code Online (Sandbox Code Playgroud)

经典hasManyThrough是不可能的,因为我们有4个表.

如何创建关系,以便单个用户可以在不使用方法链接每个模型的情况下访问其订单,例如:

User::find(8)->orders;
Run Code Online (Sandbox Code Playgroud)

Jon*_*eir 6

我创建了一个HasManyThrough无限级别的关系:GitHub 上的存储库

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

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

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


Ohg*_*why 5

一开始很难确定查询构建器如何将事物切片在一起.我知道我希望如何使用原始SQL语句格式化查询,但我想在给定其他已定义关系的情况下将Eloquent用于此特定任务.

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

首先,重载属性.

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

    return $this->getRelation('orders');
}
Run Code Online (Sandbox Code Playgroud)

上述函数的想法是捕获何时->orders调用延迟加载.如$user->orders.这将检查orders方法是否在relations现有数组中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;
}
Run Code Online (Sandbox Code Playgroud)

在上面,我们告诉Orders表加入它的联系人(这是belongsTo()在这种情况下给定所有权的既定路线).然后,从联系人中,我们加入帐户,然后从我们的用户通过将我们的owner_id列与现有列匹配来从我们的帐户到达帐户$user->id,因此我们不需要做任何进一步的操作.

接下来,我们需要通过实例的实例手动创建我们的关系hasManyEloquent Relationship建设者.

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

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

HasOneOrMany预期以下,以它的构造函数:

Builder $query,
Model $parent,
$foreignKey, 
$localKey
Run Code Online (Sandbox Code Playgroud)

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

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

array $models,
Collection $results,
$relation
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

$user->orders;
Run Code Online (Sandbox Code Playgroud)

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


小智 5

你可以从用户通过对接触,然后joinOrders

public function orders(){
   return $this->hasManyThrough('Contact', 'Account', 'owner_id')->join('orders','contact.id','=','orders.contact_ID')->select('orders.*');
}
Run Code Online (Sandbox Code Playgroud)

在相同情况下对我有效,欢迎所有反馈

  • 这种方法的问题在于返回的模型将是具有Contact的所有属性的Contact的实例。 (3认同)