Laravel中如何获取指定订阅对应的首个订单?
解决Laravel中获取客户订阅对应首个订单的问题
模型结构
Order: - id - amount - plan_id - client_id (注:根据关联代码推断模型存在该字段) Plan: - id - name - price Subscription: - id - plan_id - starts_at - ends_at - subscriber_type - subscriber_id Client: - id - name
其中subscriber_type固定为App\Models\Client,subscriber_id关联客户ID。
现有模型关联代码
Plan.php
class Plan extends Model implements Sortable { public function subscriptions(): HasMany { return $this->hasMany(Subscription::class); } public function orders(): HasMany { return $this->hasMany(Order::class); } // 其他代码... }
Subscription.php
class Subscription extends Model{ public function plan(): BelongsTo { return $this->belongsTo(Plan::class); } }
Client.php
class Client extends Model{ public function subscriptions(): MorphMany { return $this->morphMany(Subscription::class, 'subscriber', 'subscriber_type', 'subscriber_id'); } }
Order.php
class Order extends Model { use HasFactory; public function client(): BelongsTo { return $this->belongsTo(Client::class); } }
需求与问题
创建订阅时会同步生成对应plan_id的订单。现在需要获取某客户的所有订阅时,同时拿到每个订阅对应的该客户的首个订单。
尝试过两种方法,但都存在问题:
- 第一种方法返回了该套餐下该客户的所有订单,不符合仅需首个订单的需求:
$client->subscriptions()->with(['plan.orders' => function ($query) use ($client) { $query->where('client_id', $client->id) ->orderBy('created_at'); }]);
- 第二种方法仅首个订阅结果中返回了订单,后续订阅的
orders为空数组:
$client->subscriptions()->with(['plan.orders' => function ($query) use ($client) { $query->where('client_id', $client->id) ->orderBy('created_at') ->first(); }]);
解决方案
方案一:调整关联查询约束
问题出在使用->first(),它会直接执行查询并返回单个模型,导致后续订阅的关联查询复用了空结果。改用->limit(1)来约束每个关联仅返回第一条记录:
$subscriptions = $client->subscriptions()->with(['plan.orders' => function ($query) use ($client) { $query->where('client_id', $client->id) ->orderBy('created_at') ->limit(1); }])->get();
此时每个订阅的plan.orders会是一个仅包含首个订单的集合。
方案二:给Subscription添加专属关联
如果需要更直观地获取单个订单模型,可以在Subscription模型中定义一个直接关联到对应首个订单的hasOne关联:
// Subscription.php public function firstOrder(): HasOne { return $this->hasOne(Order::class, 'plan_id', 'plan_id') ->where('client_id', $this->subscriber_id) ->orderBy('created_at'); }
使用时直接关联该字段即可:
$subscriptions = $client->subscriptions()->with('firstOrder')->get();
每个订阅的firstOrder字段会直接返回对应的首个订单模型,无需嵌套到plan下。
内容的提问来源于stack exchange,提问作者DeveloperX
相关产品推荐
相关产品推荐

