You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 05:05:37