Laravel Eloquent实现Payment与Contract表内连接及搜索功能问题
Laravel Eloquent关联Contract实现Payment搜索功能修复
问题说明
我是Laravel Eloquent新手,正在开发Payment的搜索功能,原本功能正常,但需要关联Contract表,以便在搜索结果中输出$payment->contract->account_no。现需将指定SQL转换为Eloquent写法,添加account_no筛选条件时出现错误,尝试使用DB Facade又无法调用Payment模型中的公共方法。
附相关代码
目标SQL语句
SELECT tbt_contracts.account_no , tbt_payments.payment_type, tbt_payments.payment_mode, tbt_payments.payment_date, tbt_payments.payment_amount, tbt_payments.payment_due_date, tbt_payments.payee_name FROM tbt_payments INNER JOIN tbt_contracts ON tbt_payments.contract_id = tbt_contracts.id WHERE tbt_contracts.account_no LIKE '%test%'
Payment模型关联
public function contract() { return $this->belongsTo(Contract::class); }
Contract模型关联
public function payments() { return $this->hasMany(Payment::class); }
原控制器搜索逻辑(含错误)
$filters = []; //account_no (ERROR) !is_null($request->account_no) ? array_push($filters, ['account_no', 'like','%'.$request->account_no.'%']) : null; //payment_type !is_null($request->payment_type) ? array_push($filters, ['payment_type', 'like','%'.$request->payment_type.'%']) : null; //payment_mode !is_null($request->payment_mode) ? array_push($filters, ['payment_mode', 'like','%'.$request->payment_mode.'%']) : null; //payee_name !is_null($request->payee_name) ? array_push($filters, ['payee_name', 'like','%'.$request->payee_name.'%']) : null; return view('transaction.payment.paymentlist', [ 'payments' => Payment::where($filters)->orderBy('id')->paginate(10), 'payment_modes' => LogicCONF::getDropDownJson('payment_mode.json'), 'payment_types' => LogicCONF::getDropDownJson('payment_type.json'), ]);
解决方案
方法一:使用Eloquent关联筛选(推荐)
这种方法保留Payment模型的所有公共方法,同时避免N+1查询问题:
$filters = []; // 处理Payment表自身的筛选条件 !is_null($request->payment_type) ? array_push($filters, ['payment_type', 'like','%'.$request->payment_type.'%']) : null; !is_null($request->payment_mode) ? array_push($filters, ['payment_mode', 'like','%'.$request->payment_mode.'%']) : null; !is_null($request->payee_name) ? array_push($filters, ['payee_name', 'like','%'.$request->payee_name.'%']) : null; // 初始化查询 $query = Payment::where($filters); // 关联Contract表筛选account_no if (!is_null($request->account_no)) { $query->whereHas('contract', function ($q) use ($request) { $q->where('account_no', 'like', '%'.$request->account_no.'%'); }); } // 预加载contract关联,避免N+1查询 $payments = $query->with('contract')->orderBy('id')->paginate(10); return view('transaction.payment.paymentlist', [ 'payments' => $payments, 'payment_modes' => LogicCONF::getDropDownJson('payment_mode.json'), 'payment_types' => LogicCONF::getDropDownJson('payment_type.json'), ]);
关键说明
whereHas:用于筛选存在指定关联且关联模型满足条件的Payment记录,完美适配跨表筛选需求。with('contract'):预加载关联数据,防止在视图中访问$payment->contract->account_no时重复查询数据库。
方法二:使用Join匹配原SQL逻辑
如果需要完全贴合原SQL的Join写法,也可以这样实现:
$filters = []; // 处理Payment表自身的筛选条件 !is_null($request->payment_type) ? array_push($filters, ['payment_type', 'like','%'.$request->payment_type.'%']) : null; !is_null($request->payment_mode) ? array_push($filters, ['payment_mode', 'like','%'.$request->payment_mode.'%']) : null; !is_null($request->payee_name) ? array_push($filters, ['payee_name', 'like','%'.$request->payee_name.'%']) : null; // 构建Join查询 $query = Payment::select( 'tbt_contracts.account_no', 'tbt_payments.payment_type', 'tbt_payments.payment_mode', 'tbt_payments.payment_date', 'tbt_payments.payment_amount', 'tbt_payments.payment_due_date', 'tbt_payments.payee_name' )->join('tbt_contracts', 'tbt_payments.contract_id', '=', 'tbt_contracts.id') ->where($filters); // 添加account_no筛选 if (!is_null($request->account_no)) { $query->where('tbt_contracts.account_no', 'like', '%'.$request->account_no.'%'); } $payments = $query->orderBy('id')->paginate(10); return view('transaction.payment.paymentlist', [ 'payments' => $payments, 'payment_modes' => LogicCONF::getDropDownJson('payment_mode.json'), 'payment_types' => LogicCONF::getDropDownJson('payment_type.json'), ]);
关键说明
- 直接使用
join方法关联两张表,字段需指定表前缀避免冲突。 - 这种写法和原SQL逻辑完全一致,同时仍能调用Payment模型的公共方法。
内容的提问来源于stack exchange,提问作者Zwell
相关产品推荐
相关产品推荐

