Laravel 8中关联模型在API Resource中的数据返回问题及优化方案咨询
Let's break down what's causing your error and refactor your code into a cleaner, more Laravel-idiomatic solution:
Root Cause of the Error
Your lastPrice relationship in the Products model is a hasMany—this returns a collection of ProductPrice models, not a single instance. But in your Products Resource, you used lastPrice::make($this->lastPrice) which is designed for individual models. When the Resource tried to access $this->id, it was trying to fetch that property from the collection itself (which doesn't have an id), hence the error.
Step 1: Fix the Products Resource
Update your Products Resource to use the collection() method for the lastPrice relationship, since it returns multiple items:
public function toArray($request) { return [ 'id' => $this->id, 'prices' => lastPrice::collection($this->lastPrice), // Use collection() instead of make() 'status' => $this->status, 'slug' => $this->slug, 'title' => $this->title, 'description' => $this->description, 'txt' => $this->txt, 'lang' => $this->lang, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, ]; }
Step 2: Clean Up the lastPrice Resource
Instead of manual loops and direct database calls, leverage Laravel's relationship features and nested Resources. We'll also keep your caching logic but integrate it more cleanly:
First, (optional but recommended) rename your ProductPrice relationship to follow Laravel conventions for readability:
// ProductPrice model public function publisher() { // Renamed from getPublisher return $this->belongsTo(ProductsPublisher::class, 'publisher_id'); }
Then update your lastPrice Resource:
public function toArray($request) { // Cache the publisher data directly using the relationship $publisher = Cache::remember( 'publisher_' . $this->publisher_id, env('CACHE_TIME_LONG'), fn() => $this->publisher ); return [ 'id' => $this->id, 'main_price' => $this->main_price, 'off_price' => $this->off_price, 'created_at' => $this->created_at, 'publisher' => SinglePublisher::make($publisher), ]; }
If you want to keep the original getPublisher relationship name, just replace $this->publisher with $this->getPublisher.
Step 3: Optimize with Eager Loading
Prevent N+1 database queries by eager loading the lastPrice relationship and its associated publisher in your controller:
$products = Product::where('id', $id) ->where('slug', $slug) ->where('status', 'confirm') ->with(['lastPrice.publisher']) // Eager load related data upfront ->first();
Why This Is More Elegant
- No manual iteration: Laravel Resources handle collection looping automatically when using
collection(). - Encapsulated logic: Caching and relationship access are kept within the Resource where they belong.
- Better performance: Eager loading cuts down on unnecessary database calls.
- Readable code: Follows Laravel conventions, making it easier for other developers to understand.
内容的提问来源于stack exchange,提问作者Soroush Tayyebi

