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

schwartzmj's avatar

Help building a query or using Eloquent

I'm having some issues creating a query and structuring my data for a view.

I have a User model with role family that hasMany Applications

The view I'm displaying to the user is a table with the following table headings: Name, Email, and Year 2020 Application Status

Originally I was looping over every family and querying the database for every single row of the table in the view, which was causing hundreds of database queries. This is when I realized I should probably just query the database for all of the data I need in the controller and then send it to the view.

My goal is to query the database for the following:

  1. ALL Users with the role of "family"
  2. The current year's application for EACH family.
  3. If the family does NOT have a current year application, I want that field to still be there, but just null or some other falsy value.

One issue I'm having is that it looks like SQL "joins" merge all of the data together, so I have User data and Application within the same $family object. Is there a way to get the Application data to just be a key in the User data, like $family->application?

In my view I want to loop over each family and spit out a <tr> with the following data: $family->name, $family->email, $family->application->status.

$family->application can be null, so it'll be more like this for that <td>:

<td>
	@if($family->application)
		{{ $family->application->status }}
	@else
		Not Created
	@endif
</td>

Here's a few methods of retrieving data that I've tried which aren't quite what I'm going for:

                $data = DB::table('users')
                    ->where('role', 'family')
                    ->join('applications', function ($join) {
                        $join->on('users.id', '=', 'applications.family_id')
                            ->whereYear('applications.created_at', now()->year)
                    })
                    ->get();
                $family_users = DB::table('users')->where('role', 'family')->get();

                $applications_current_year = DB::Table('applications')
                    ->whereYear('created_at', now()->year)->get();

                return view('livewire.families-index')
                    ->with(compact('family_users', 'applications_current_year'));

                $this->families = User::where('role', 'family')
                    ->with([
                        'applications' => function ($query) {
                            $query->whereYear('created_at', now()->year);
                        }
                    ])
                    ->get();

0 likes
7 replies
MichalOravec's avatar

The last one is exactly what you need.

$this->families = User::with(['applications' => function ($query) {
    $query->whereYear('created_at', now()->year);
}])->where('role', 'family')->get();
<td>
    @if ($family->applications->isNotEmpty())
        @foreach ($family->applications as $application)
            {{ $application->status }}
        @endforeach
    @else
        Not Created
    @endif
</td>

You relationship for applications is hasMany so you everytime get a collection.

schwartzmj's avatar

How can I make it so it is just $family->application instead of $family->applications ?

I only want to get 1 application from the current year (they can only make one per year) and not the entire collection.

The issue I was having was that, in the view, I then have to go $family->getApplicationCurrentYear()->status which causes another database query

MarianoMoreyra's avatar

Hi @schwartzmj

You are Eager Loading the relationship there, so you shouldn't need to go to database again.

Maybe it's not the nicest way, but if you can only get 1 application per year, then you can just take the first() element from the collection:

<td>
    @if ($family->applications->isNotEmpty())
            {{ $family->applications->first()->status }}
    @else
        Not Created
    @endif
</td>

or maybe you can make it shorter like this:

<td>
        {{ $family->applications->first()->status ?? "Not Created" }}
</td>
MichalOravec's avatar
Level 75

Create another relationship in the User model

public function application() 
{
    return $this->hasOne(Application::class, 'family_id')->latest();
}
$this->families = User::with(['application' => function ($query) {
    $query->whereYear('created_at', now()->year);
}])->where('role', 'family')->get();
<td>
    @if ($family->application)
        {{ $family->application->status }}
    @else
        Not Created
    @endif
</td>
schwartzmj's avatar

I think this is pretty much what I'm looking for. I'm learning a lot today. Thank you again!

Thank you also @marianomoreyra for giving me some other methods to think about.

schwartzmj's avatar

@michaloravec The solution you gave is working, but I'm curious how it is working.

latest() sorts the collection, so I'm still pointing hasOne to a collection. Does hasOne just select the first item in the collection if it is a collection?

Is there a way I can explicitly point it at a single item instead of a collection?

Is this the line where it grabs the first item if you give it a collection?

If I wanted to grab the second or third one for some reason, I'm guessing I could apply an offset?

Please or to participate in this conversation.