Search code examples
laravellaravel-resource

Laravel custom attributes loads relationships even when attribute is not asked


I have a custom attribute that calculates the squad name (to make our frontend team lives easier).

This requires a relation to be loaded and even if the attribute is not being called/asked (this happens with spatie query builder, an allowedAppends array on the model being passed to the query builder and a GET param with the required append(s)) it still loads the relationship.

// Model
public function getSquadNameAttribute()
{
    $this->loadMissing('slots');
    // Note:  This model's slots is guaranteed to all have the same squad name (hence the first() on slots).
    $firstSlot = $this->slots->first()->loadMissing('shift.squad');
    return ($firstSlot) ? $firstSlot->shift->squad->name : null;
}

// Resource
public function toArray($request)
{
    return [
        'id'         => $this->id,
        'squad_name' => $this->when(array_key_exists('squad_name', $this->resource->toArray()), $this->squad_name),

        'slots'      => SlotResource::collection($this->whenLoaded('slots')),
    ];
}

Note: squad_name does not get returned if it's not being asked in the above example, the relationship is however still being loaded regardless

A possible solution I found was to edit the resource and includes if's but this heavily reduces the readability of the code and I'm personally not a fan.

public function toArray($request)
{
    $collection = [
        'id'    => $this->id,

        'slots' => SlotResource::collection($this->whenLoaded('slots')),
    ];

    if (array_key_exists('squad_name', $this->resource->toArray())) {
        $collection['squad_name'] = $this->squad_name;
    }
    
    return $collection;
}

Is there another way to avoid the relationship being loaded if the attribute is not asked without having spam my resource with multiple if's?


Solution

  • The easiest and most reliable way I have found was to make a function in a helper class that checks this for me.

    This way you can also customize it to your needs.

    -- RequestHelper class

    public static function inAppends(string $value)
    {
        $appends = strpos(request()->append, ',') !== false ? preg_split('/, ?/', request()->append) : [request()->append];
        return in_array($value, $appends);
    }
    

    -- Resource

    'squad_name' => $this->when(RequestHelper::inAppends('squad_name'), function () {
        return $this->squad_name;
    }),