如何正确调用带参数的Eloquent方法earnedByStaff?
问题描述
在StaffController中获取员工交易数据时,调用Transaction模型的earnedByStaff($staff)方法遇到两个错误:
- 链式调用
Transaction::thisMerchant()->earnedByStaff($staff)时,提示Builder::earnedByStaff()方法未定义; - 静态调用
Transaction::earnedByStaff($staff)时,提示非静态方法不能静态调用。
相关代码如下:
StaffController.php
$transactions = Transaction::thisMerchant() ->earnedByStaff($staff) ->where(function ($q) use ($staffID) { $q->where('staff_id', $staffID) ->orWhere(function ($qq) use ($staffID) { $qq ->whereNull('staff_id') ->whereHas('items', function ($qqq) use ($staffID) { $qqq->where('staff_id', $staffID); }); }) ->orWhereHas('associateCommissions', function ($qq) use ($staffID) { $qq->where('staff_id', $staffID); }); }) ->when($start_date && $end_date, function ($q) use ( $start_date, $end_date ) { $q->whereBetween(\DB::raw('date(date)'), [ $start_date . ' 00:00:00', $end_date . ' 23:59:59', ]); }) ->with('client') ->orderBy('date', 'DESC') ->where('is_void', null) ->where('is_draft', null) ->with('items.sellable.sellable') ->with('associateCommissions') ->paginate(10);
Transaction.php
public function earnedFromTransaction($staff_id) { $staff = Staff::find($staff_id); if ($this->is_void == '1') { return 0; } if ($this->provider() == 'Empty') { return 0; } if ($this->provider() == 'Split') { $amounts = Titem::where('transaction_id', $this->id) ->where('staff_id', $staff_id) ->whereHas('product', function ($q) { $q->whereNull('commission'); }) ->whereHas('transaction', function ($q) { $q->where('is_void', null); }) ->sum('frozen_staff_commission'); $discounts = Titem::where('transaction_id', $this->id) ->where('staff_id', $staff_id) ->whereHas('product', function ($q) { $q->whereNull('commission'); }) ->whereHas('transaction', function ($q) { $q->where('is_void', null); }) ->sum('discount'); // When the frozen staff rate is zero, just give sellable commission $titem_commissions = Titem::where('transaction_id', $this->id) ->where('staff_id', $staff_id) ->where('frozen_staff_commission', '<=', 0) ->whereHas('product', function ($q) { $q->whereNotNull('commission'); }) ->sum('commission'); $total = floatval($amounts) + floatval($titem_commissions) - floatval($discounts); return $total; } return floatval($this->frozen_staff_commission); } public function earnedByStaff(Staff $staff) { return StaffCommissionEngine::earnedFromTransaction($this, $staff); }
解决方案
错误原因
- 第一种错误:
Transaction::thisMerchant()返回的是查询构造器(Builder)实例,而earnedByStaff是Transaction模型的实例方法,只能在单个Transaction对象上调用,没法直接链式调用在构造器上。 - 第二种错误:
earnedByStaff是非静态方法,必须通过模型实例调用,不能用::静态调用。
根据实际需求分两种场景处理:
场景1:给每笔交易计算对应员工的佣金
如果只是想获取交易数据后,算出该员工在每笔交易里赚的佣金,不需要过滤交易,先查交易再处理:
- 先获取交易分页数据:
$transactions = Transaction::thisMerchant() ->where(function ($q) use ($staffID) { // 原有where逻辑保持不变 $q->where('staff_id', $staffID) ->orWhere(function ($qq) use ($staffID) { $qq ->whereNull('staff_id') ->whereHas('items', function ($qqq) use ($staffID) { $qqq->where('staff_id', $staffID); }); }) ->orWhereHas('associateCommissions', function ($qq) use ($staffID) { $qq->where('staff_id', $staffID); }); }) ->when($start_date && $end_date, function ($q) use ($start_date, $end_date) { $q->whereBetween(\DB::raw('date(date)'), [ $start_date . ' 00:00:00', $end_date . ' 23:59:59', ]); }) ->with('client', 'items.sellable.sellable', 'associateCommissions') ->orderBy('date', 'DESC') ->where('is_void', null) ->where('is_draft', null) ->paginate(10);
- 遍历交易集合,调用方法计算佣金:
用集合的map方法给每个交易新增佣金字段:
$transactions->getCollection()->map(function ($transaction) use ($staff) { $transaction->staff_earned = $transaction->earnedByStaff($staff); return $transaction; });
场景2:筛选出该员工能赚佣金的交易
如果要过滤出该员工有佣金收入的交易,需要把earnedByStaff改成Laravel查询作用域,这样就能在构造器上链式调用:
- 修改
Transaction模型,添加作用域方法:
// 方法名必须以scope开头,符合Laravel作用域规范 public function scopeEarnedByStaff($query, Staff $staff) { // 这里根据StaffCommissionEngine的佣金计算逻辑,编写对应的查询条件 // 示例(需结合实际业务调整): return $query->where(function ($q) use ($staff) { // 筛选员工参与的交易 $q->where('staff_id', $staff->id) ->orWhereHas('items', function ($qq) use ($staff) { $qq->where('staff_id', $staff->id); }) ->orWhereHas('associateCommissions', function ($qq) use ($staff) { $qq->where('staff_id', $staff->id); }); // 排除无效交易 $q->where('is_void', null)->where('is_draft', null); }); }
- 在控制器中调用作用域:
$transactions = Transaction::thisMerchant() ->earnedByStaff($staff) // 现在可以正常链式调用 ->when($start_date && $end_date, function ($q) use ($start_date, $end_date) { $q->whereBetween(\DB::raw('date(date)'), [ $start_date . ' 00:00:00', $end_date . ' 23:59:59', ]); }) ->with('client', 'items.sellable.sellable', 'associateCommissions') ->orderBy('date', 'DESC') ->paginate(10);
补充说明
- 原
earnedByStaff是实例方法,用于计算单条交易的佣金;查询作用域用于筛选交易集合,根据实际需求选择即可。 - 如果
StaffCommissionEngine::earnedFromTransaction的逻辑没法直接转成数据库查询条件,优先用场景1的方式处理。
内容的提问来源于stack exchange,提问作者davidkihara
相关产品推荐
相关产品推荐

