Laravel7如何避免外键为NULL时执行Eloquent关联查询
解决Laravel预加载关联时外键为NULL仍执行查询的问题
针对你遇到的Invoices模型中location、vehicle、employee关联外键为NULL时仍触发不必要查询的问题,提供以下两种可行方案:
方案一:自定义预加载约束+手动补全NULL关联
在预加载时添加whereNotNull过滤掉外键为NULL的记录,之后遍历结果给外键为NULL的关联手动设置为null:
// 预加载时仅筛选外键非空的记录 $invoices = Invoice::with([ 'location' => fn($query) => $query->whereNotNull('invoices.location_id'), 'vehicle' => fn($query) => $query->whereNotNull('invoices.vehicle_id'), 'employee' => fn($query) => $query->whereNotNull('invoices.employee_id') ])->get(); // 遍历补全外键为空的关联为null foreach ($invoices as $invoice) { is_null($invoice->location_id) && $invoice->setRelation('location', null); is_null($invoice->vehicle_id) && $invoice->setRelation('vehicle', null); is_null($invoice->employee_id) && $invoice->setRelation('employee', null); }
这个方案无需修改关联定义,适合快速临时解决问题。
方案二:自定义关联类(长期优雅解决方案)
通过重写Laravel默认的HasOne关联逻辑,让预加载时仅收集非NULL的外键值,从根源避免无效查询:
- 创建自定义关联类:
namespace App\Relations; use Illuminate\Database\Eloquent\Relations\HasOne; class HasOneNullable extends HasOne { /** * 仅返回非空的外键值用于预加载 */ protected function getEagerKeys() { return collect(parent::getEagerKeys())->filter()->values()->all(); } }
- 在Invoice模型中替换默认的HasOne关联:
namespace App\Models; use App\Relations\HasOneNullable; use Illuminate\Database\Eloquent\Model; class Invoice extends Model { public function location() { return $this->newHasOneNullable( $this->newQuery(), $this, 'locations.id', // 关联模型的主键 'location_id' // 当前模型的外键 ); } public function vehicle() { return $this->newHasOneNullable( $this->newQuery(), $this, 'vehicles.id', 'vehicle_id' ); } public function employee() { return $this->newHasOneNullable( $this->newQuery(), $this, 'employees.id', 'employee_id' ); } /** * 实例化自定义的HasOneNullable关联 */ protected function newHasOneNullable($query, $parent, $foreignKey, $localKey) { return new HasOneNullable($query, $parent, $foreignKey, $localKey); } }
之后正常使用Invoice::with(['location', 'vehicle', 'employee'])->get()即可,外键为NULL的关联会自动保持为null,且不会触发无效查询。
如果是belongsTo关联,同理创建BelongsToNullable类,重写getEagerKeys方法即可,逻辑一致。
内容的提问来源于stack exchange,提问作者Linesofcode
相关产品推荐
相关产品推荐

