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

danieltovario's avatar

API Resource with Polymorphic Relationships

Hi,

I have a situation where my User and Group models can share a one-to-one polymorphic relationship with a Calendar model. I need my Calendar API Resource to include either the User or Group it's associated with. How do I do this?

After reading up this post, this is my set up:


<?php
// The models

class Calendar extends Model
{
    /
    public function calendarable()
    {
        return $this->morphTo();
    }
}

class User extends Model
{
    
    public function calendar()
    {
        return $this->morphOne('App\Calendar', 'calendarable');
    }
}

class Group extends Model
{

    public function calendar()
    {
        return $this->morphOne('App\Calendar', 'calendarable');
    }
}

<?php
// Calendar API Resource


class CalendarResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'calendarable_id' => $this->calendarable_id,
            'owner' => $this->when($this->relationLoaded('calendarable'), function () {
            switch (true) {
                case $this->calendarable instanceof \App\User:
                    return new UserResource($this->calendarable);
                    
                case $this->calendarable instanceof \App\Group:
                    return new GroupResource($this->calendarable);
            }
        })
        ];
    }
}

The above loads the appropriate resource class, but it's giving me a Maximum stack depth exceeded error.

More background: The Calendar and User model used to share a one-to-one relationship and in the Calendar API resource, 'owner' => new UserResource($this->whenLoaded('user')) would properly load the resource class. The Groups model is new so I converted the existing Calendar and User relation to the polymorphic relationship described above.

Any help will be greatly appreciated!

0 likes
6 replies
Sinnbeck's avatar

What isn't working? Getting an error or an unexpected result?

danieltovario's avatar

After looking through my logs, I see that the relationship is being loaded, but it's giving me an Maximum stack depth exceeded error, specifically at the Illuminate\\Http\\JsonResponse->setData(Array) method.

More background: The Calendar and User model used to share a one-to-one relationship and in the Calendar API resource, 'owner' => new UserResource($this->whenLoaded('user')) would properly load the resource class. The Groups model is new so I converted the existing Calendar and User relation to the polymorphic relationship described in my OP.

Any insight would be very helpful!

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

Well a bit of debugging is a good place to start.

Comment out the owner section and just do this.

'owner' => $this->calenderable, 

Is the error gone?

1 like
danieltovario's avatar

It is! Thanks a bunch, man!

I can't believe I was stuck on this for so long trying to load the relation... Level 1 vs Level 50, eh? :D

Sinnbeck's avatar

Well you have a great start already. Polymorphic relationships are hard. Now take a look at those resources to find out what is causing it

skcin7's avatar

I'm in the same situation now too. Here's how I have it solved now for me:

use App\Http\Resources\Game as GameResource;
use App\Http\Resources\System as SystemResource;

class ReleaseResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        switch($this->releaseable_type) {
            case 'SYSTEM':
                $releaseableResource = SystemResource::class;
            case 'GAME':
                $releaseableResource = GameResource::class;
        }

        return [
            'id' => $this->id,
            'releaseable_id' => $this->releaseable_id,
            'releaseable_type' => $this->releaseable_type,
            'name' => $this->name,

            'releaseable' => $releaseableResource::make($this->whenLoaded('releaseable')),
        ];
    }
}

So basically I have an extra switch statement before the return statement that checks the value of releaseable_type, and loads the correct API resource class into a variable that I can use later as part of the return statement. This works, and I'm happy with it. Still, I can't help but feel like this is a slightly "yucky" way to achieve this, and there might be a "better"/"cleaner" way? Not that having the extra switch statement there is the biggest deal in the world, but I'd prefer it not to be if possible LOL. That being said, it works, so this is how I have it done in my code, until I can find a better way.

I hope this helps somebody somehow!

2 likes

Please or to participate in this conversation.