首页 > 解决方案 > Laravel 5:有条件的雄辩关系方法

问题描述

正常的关系方法通常没有条件,并且往往看起来像这样:

class StripeCustomer extends Model
{
    public function user()
    {
         return $this->belongsTo(User::class, 'stripe_customer_id');
    }
}

在我的模型中,我在关系方法中有一个条件,如下所示:

class StripeCustomer extends Model
{
    public function user()
    {
        if ($this->type === 'normal') {
            return $this->hasOne(User::class, 'stripe_customer_id');
        } else {
            return $this->hasOne(User::class, 'stripe_customer_charity_id');
        }
    }
}

Laravel 是否像上面那样支持 Eloquent 中的条件关系。许多常用的方法仍然像这样工作:

StripeCustomer::get()->first()->user;
StripeCustomer::get()->first()->user()->get();

但是以下工作是否可以预见:

Foo::with('user')->get();

这里的问题是我不确定“with”运算符在 Eloquent 内部是如何工作的。

我认为它也不起作用的一个原因是user()需要为每个模型执行该方法。但是,当我dump(...)在方法的开头添加a时,我发现它只运行了一次,表明它with()不起作用。

标签: laravellaravel-5eloquent

解决方案


不,它不适用于with(). 当您尝试执行以下代码时,您认为会发生什么:

Foo::with('user')->get();

答案是 Laravel 将创建新的实例Foo并尝试调用user()以获取关系对象。这个新实例没有任何type( (new Foo)->typewill be null),因此您的方法user()将始终返回$this->hasOne(Bar::class, 'b_id'),并且此关系对象将用于构造查询。

正如您所看到的,这显然不是您想要的,因为只有 B 类用户会急切地加载所有Foo行。在这种情况下,您需要做的是为用户创建两个关系(每种类型一个)和访问器(get/set):

class Foo extends Model
{
    public function userA()
    {
        return $this->hasOne(Bar::class, 'a_id');
    }

    public function userB()
    {
        return $this->hasOne(Bar::class, 'b_id');
    }

    public function getUserAttribute()
    {
        if ($this->type === 'a') {
            return $this->userA;
        } else {
            return $this->userB;
        }
    }

    public function setUserAttribute($user)
    {
        if ($this->type === 'a') {
            $this->userA()->associate($user);
        } else {
            $this->userB()->associate($user);
        }
    }
}

然后,您可以使用with()这两种关系来利用急切加载:

$fooRows = Foo::with('userA', 'userB')->get();
...
foreach ($fooRows as $row) {
    $row->user;
}

编辑: 由于您在问题中编辑了代码,因此我的答案中的示例代码不再代表您的情况,但我希望您了解总体思路。


推荐阅读