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

craigivemy's avatar

API Resource: Using count() with whenLoaded()

I'm trying to avoid loading a relationship unless it has been eager loaded in my controller. However, I also need the count of that relationship if it has been loaded. The code below works unless I don't eager load, in which case it throws a "call to undefined method missingValue::count error.

API Resource

'playedUpCount'=> $this->whenLoaded('playedUps')->count(),
'playedUps'=> $this->whenLoaded('playedUps')

Controller

$teams = Team::whereIn('id', $teamsInSeasonQuery)
                    ->with(['players' => function($query) use($seasonId) {
                        $query->withTrashed()->where('season_id', $seasonId);
                        $query->with(['playedUps' => function($query) use($seasonId) {
                           $query->where('season_id', $seasonId);
                        }]);
                    }])
                    ->get();

I will be using the API resource from other methods in the controller where I do not want to be loading the relationship, yet as I say, when I do this it throws the undefined method error.

0 likes
10 replies
craigivemy's avatar

@jbloomstrom withCount() doesn't load the relationship though, just a count.

"If you want to count the number of results from a relationship without actually loading them you may use the withCount method..."

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

Give this a go

'playedUpCount'=> $this->whenLoaded('playedUps', function() {
    return $this->playedUps->count()
}),
1 like
RomainB's avatar

Searching to do the same thing: get the count only if it exists.

And Actually the way @sinnbeck says and marked as the answer does the exact same thing that @jbloomstrom and has been judged as "unappropriate":

'playedUpCount'=> $this->whenLoaded('playedUps', function() { // If the playedUps relation has ben loaded from the controller
    return $this->playedUps->count() // Then it count the collection's items
}),

That's definitively not a COUNT() from the database and does the exact same thing that @jbloomstrom has said: it loads the entire relation's collection, and then does just a count on the collection's elements

To do the same, I have done like this:

class ShopResource extends \Illuminate\Http\Resources\Json\Resource
{
    public function toArray($request)
    {
        $resource = [
            'id' => $this->id,
            'name' => $this->name,
            'description' => $this->description,
            'lat' => $this->price,
            'lng' => $this->quantity,
            'photos' => $this->whenLoaded('photos'),
            'products' => ProductResource::collection($this->whenLoaded('products'))
        ];

        if ($this->products_count) {
            $resource['products_count'] = $this->products_count;
        }

        return $resource;
    }
}

And then from the controller:

// Get the count directly from the relationship query
$shops = Shop::withCount('products');
return new JsonResponse(
    ShopResource::collection($shops->get())
);
Sinnbeck's avatar

@romainb I agree :) It does make more sense to do both in database instead of php. A quick benchmark would most likely show that your version is faster when working with many records. You might want to actually load the products in you example as well, to make it complete

$shops = Shop::withCount('products')->with('products');
return new JsonResponse(
    ShopResource::collection($shops->get())
);
1 like
danielsdeboer's avatar

Could you not do something like so:

'products_count' => $this->when(
    $this->products_count !== null, 
    fn () => $this->products_count
)
1 like
RomainB's avatar

Yeah, this is a more appropriate syntax. You don't even need to use the callback, and I'd prefer to use isset() instead of null comparison:

'products_count' => $this->when(
    isset($this->products_count),
    $this->products_count
)

Just tested in a project, it works perfectly well :)

6 likes
FredVanelli's avatar

When using withCount() or loadCount(), Laravel will automatically add FIELDNAME_count as a primary attribute, NOT as a relation. So you can just do this:

$teams = Team::withCount('playedUps')->otherQueryCommands()->get();
return TeamResource::collection($teams);

And in your resource you only need this:

public function toArray($request)
{
    return parent::toArray($request); // All primary attributes
}

playedUps_count will be automatically added/removed based on withCount('playedUps')

If you need to add more conditional relations to the resource:

public function toArray($request)
{
    return array_merge(
        parent::toArray($request), // All primary attributes
        [
            'playedUps' => $this->whenLoaded('playedUps'),
            'otherRelation' => $this->whenLoaded('otherRelation'),
        ]
    );
}
jovialcore's avatar

Old posts though but I will add this: you can instead add the count attributte to the model:

class Players extends Eloquent
{
    protected $withCount = [
        'playedups',
    ];
}

then in your resource

public function toArray($request)
    {
        return [

            'id' => $this->id,
            'name' =>  $this->name,         
            'projects_count' => $this->playedups_count

        ];
       
    }

Please or to participate in this conversation.