Laravel Eloquent条件查询及关联加载:社交网站用户关系获取
Got it, let's break this down into actionable steps to get exactly what you need. You want to fetch relationship records where the current user is either the sender (from_id) or receiver (to_id), filter by a specific status, and eager load the corresponding user details based on which side the current user is on. Here's how to implement it:
Step 1: Define Relationships in Your Model
First, let's assume your relationship table corresponds to a Friendship model (rename this if your model has a different name). Add these two belongsTo relationships to link to the User model:
// app/Models/Friendship.php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Friendship extends Model { protected $fillable = ['from_id', 'to_id', 'status']; // Relationship to the user who sent the request public function sender(): BelongsTo { return $this->belongsTo(User::class, 'from_id'); } // Relationship to the user who received the request public function receiver(): BelongsTo { return $this->belongsTo(User::class, 'to_id'); } // Optional: Add a dynamic attribute to get the "friend" user directly public function getFriendAttribute() { return $this->from_id === auth()->id() ? $this->receiver : $this->sender; } }
The getFriendAttribute dynamic attribute is a nice touch—it lets you access the corresponding friend user directly via $friendship->friend, no extra logic needed later.
Step 2: Build the Eloquent Query
Now, let's construct the query to filter by status, include the current user as either sender or receiver, and eager load the necessary user details:
use Illuminate\Support\Facades\Auth; use App\Models\Friendship; // Replace with your desired status (e.g., 'Accepted', 'Sent') $targetStatus = 'Accepted'; $friendships = Friendship::where('status', $targetStatus) // Critical: Wrap the OR conditions in a closure to avoid logic errors ->where(function ($query) { $query->where('from_id', Auth::id()) ->orWhere('to_id', Auth::id()); }) // Eager load both sender and receiver (we'll pick the right one via the dynamic attribute) ->with(['sender' => function ($query) { // Only select the user fields you need to optimize performance $query->select('id', 'name', 'email', 'avatar'); }, 'receiver' => function ($query) { $query->select('id', 'name', 'email', 'avatar'); }]) ->get();
How It Works:
- Status Filter: We first narrow down records to only those with your specified
status. - User Check: The closure around
from_id/to_idensures we're only fetching records where the current user is part of the relationship—this is critical because without the closure, theORcondition would break the status filter. - Eager Loading: We preload both
senderandreceiverto avoid N+1 database queries. Using a closure inwith()lets us select only the user fields we actually need, which boosts performance. - Accessing the Friend: Thanks to the dynamic
friendattribute, you can loop through the results and get the corresponding user easily:foreach ($friendships as $friendship) { // Get the friend's details $friendName = $friendship->friend->name; $friendEmail = $friendship->friend->email; }
Alternative: If You Don't Want the Dynamic Attribute
If you prefer not to use the dynamic attribute, you can transform the collection after fetching it:
$friendships = Friendship::where('status', $targetStatus) ->where(function ($query) { $query->where('from_id', Auth::id()) ->orWhere('to_id', Auth::id()); }) ->with(['sender', 'receiver']) ->get() ->map(function ($friendship) { // Assign the correct user to a "friend" key $friendship->friend = $friendship->from_id === Auth::id() ? $friendship->receiver : $friendship->sender; // Optional: Remove unused relationships to clean up the data unset($friendship->sender, $friendship->receiver); return $friendship; });
This achieves the same result, just with an extra collection transformation step.
内容的提问来源于stack exchange,提问作者Atinder Singh

