Laravel框架下如何将Controller中的$id变量传递到Model?
The core issue here is that eager loading relationships (via ->with()) doesn't support passing parameters directly to relationship methods—Laravel calls these methods without arguments by default. To fix this, we can keep your base relationship logic in the model and dynamically apply the $id filter directly in the controller's eager load closure.
Step 1: Update the User Model Relationship
First, modify your shares_load_more relationship to remove the $id parameter, keeping all your common constraints intact:
// MODEL (User.php) public function shares_load_more() { return $this->hasMany(Share::class, 'user_id', 'id') ->select('id', 'link_id', 'user_id', 'shared_in', 'content', 'created_at') ->take(2) ->orderBy('id', 'desc') ->where('type', '=', 0); }
Step 2: Apply the $id Filter in the Controller's Eager Load
In your controller, use a closure inside the ->with() method to inject the $id parameter and add the additional where clause. Make sure to include the nested relationships (links.tag, links.page) as part of the eager load:
// CONTROLLER public function user_load_more($id, $friendly_url) { $user = User::where('friendly_url', '=', $friendly_url) ->with([ 'shares_load_more' => function ($query) use ($id) { // Apply the dynamic ID filter here $query->where('id', '<', $id); }, 'shares_load_more.links.tag', 'userProfile.get_following' ]) ->with(['shares_load_more.links.page' => function ($query) { $query->select('id', 'name', 'friendly_url'); }]) ->orderBy('id', 'desc') ->first(); return view("site.list.user.links", compact("user")); }
Why This Works
- The base relationship logic (select fields, limit, ordering, type filter) stays encapsulated in the model, keeping your code DRY.
- The
use ($id)statement in the closure lets you access the controller's$idparameter within the query builder context. - Eager loading still works as expected, loading all related data in a single query (or minimal queries) instead of triggering N+1 issues.
Alternative Approach (If You Prefer Model-Encapsulated Logic)
If you want to keep the $id filter logic in the model, you can create a custom method that extends the base relationship:
// MODEL (User.php) public function shares_load_more_with_id($id) { return $this->shares_load_more()->where('id', '<', $id); }
Then in the controller, you can load this after fetching the user (note: this will run an additional query instead of eager loading):
// CONTROLLER $user = User::where('friendly_url', '=', $friendly_url) ->with('shares_load_more.links.tag', 'userProfile.get_following') ->with(['shares_load_more.links.page' => function ($query) { $query->select('id', 'name', 'friendly_url'); }]) ->orderBy('id', 'desc') ->first(); // Load filtered shares using the model method $filteredShares = $user->shares_load_more_with_id($id)->get(); // Pass both to the view return view("site.list.user.links", compact("user", "filteredShares"));
The first approach (eager load with closure) is generally preferred for performance, as it avoids extra database queries.
内容的提问来源于stack exchange,提问作者Valter Sousa Cardoso

