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

xoctopus's avatar

How to know if an API Resource is being loaded from another API resource?

I have 2 API Resource classes related to each other. The structure of these classes is as follows:

EmployeeResource

class EmployeeResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'gender' => GenderResource::make($this->gender),
        ];
    }
}

GenderResource

class GenderResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'employees' => EmployeeResource::collection($this->whenLoaded('employees')),
        ];
    }
}

I need class EmployeeResource to always return all the attributes of the employee, including their gender. I have a model where I store the different genders that can be associated with an employee. In this case, an employee must contain a gender and a gender can be associated with several employees (a one-to-many relationship). The relationships are created in the respective models (Employee and Gender).

I need when the EmployeeResource resource is returned, it always returns the gender that the employee has. But when the GenderResource resource is returned with the employee relation loaded, it returns the EmployeeResource resource, but without the gender property, since it would be redundant. For example:

class EmployeeController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
		$resource = Employee::all();

        return EmployeeResource::collection(Employee::all());
    }
}

Should return something like this:

{
    "data": [
        {
            "id": "1",
            "name": "Employee 1",
            "gender": {
                "id": "1",
                "name": "Female"
            }
        }
    ]
}

and when I do:

class GenderController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        $resource = Gender::with('employees')->all();

        return GenderResource::collection($resource);
    }
}

I am expecting something like this:

{
    "data": [
        {
            "id": "1",
            "name": "Female",
            "employees": [
                {
                    "id": "1",
                    "name": "Employee 1"
                }
            ],
        }
    ]
}

Note that the employees attribute does not contain the gender attribute. I can't find any way to know in the EmployeeResource resource if it is being loaded from another resource, in this case, GenderResource, because if so, I could apply the when(), unless(), etc. methods.

0 likes
1 reply
LaryAI's avatar
Level 58

One solution to this problem is to add a parameter to the EmployeeResource class constructor that indicates whether the resource is being loaded from another resource or not. If it is being loaded from another resource, then the gender attribute can be excluded from the response. Here's an example implementation:

class EmployeeResource extends JsonResource
{
    private $isLoadedFromAnotherResource;

    public function __construct($resource, $isLoadedFromAnotherResource = false)
    {
        parent::__construct($resource);
        $this->isLoadedFromAnotherResource = $isLoadedFromAnotherResource;
    }

    public function toArray(Request $request): array
    {
        $data = [
            'id' => $this->id,
            'name' => $this->name,
        ];

        if (!$this->isLoadedFromAnotherResource) {
            $data['gender'] = GenderResource::make($this->gender);
        }

        return $data;
    }
}

Then, in the GenderResource class, when creating the EmployeeResource instances, pass true as the second parameter to indicate that they are being loaded from another resource:

class GenderResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'employees' => EmployeeCollection::make($this->whenLoaded('employees', function () {
                return EmployeeResource::collection($this->employees)->map(function ($employee) {
                    return new EmployeeResource($employee, true);
                });
            })),
        ];
    }
}

This way, when the EmployeeResource is being loaded from the GenderResource, the gender attribute will be excluded from the response.

Please or to participate in this conversation.