Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

wulfheart's avatar

hasOne ofMany Relationship returns Collection

I have the following relationship:

public function opponent(User $user): HasOne
    {
        return $this->hasOne(Participant::class)->ofMany([
            'id' => 'max',
        ], function (Builder $query) use ($user) {
            $query->where('user_id', '!=', $user->id);
        });
    }

I wrote a simple test for it.

        $quiz = Quiz::factory()->created()->create();
        $participant = Participant::factory()->forQuiz($quiz)->isInviter()->create();
        $participantTwo = Participant::factory()->forQuiz($quiz)->isInvited()->create();

        $shouldBeParticipantTwo = $quiz->opponent($participant->user)->get();
        $shouldBeParticipant = $quiz->opponent($participantTwo->user)->get();

        $this->assertEquals($participant->user->id, $shouldBeParticipant->user->id);
        $this->assertEquals($participantTwo->user->id, $shouldBeParticipantTwo->user->id);

However it turns out that the opponent relation returns a Collection, even when using $this->hasOne(Participant::class)->latestOfMany(). Do you know how I can fix this?

Thank you in advance.

0 likes
1 reply
LaryAI's avatar
Level 58

The issue is that the get() method is being called on the relationship, which returns a collection. Instead, use the first() method to retrieve a single model instance. Here's an updated version of the test code:

$quiz = Quiz::factory()->created()->create();
$participant = Participant::factory()->forQuiz($quiz)->isInviter()->create();
$participantTwo = Participant::factory()->forQuiz($quiz)->isInvited()->create();

$shouldBeParticipantTwo = $quiz->opponent($participant->user)->first();
$shouldBeParticipant = $quiz->opponent($participantTwo->user)->first();

$this->assertEquals($participant->user->id, $shouldBeParticipant->user->id);
$this->assertEquals($participantTwo->user->id, $shouldBeParticipantTwo->user->id);

Please or to participate in this conversation.