Create base class for you resources:
use Illuminate\Http\Resources\Json\JsonResource;
class BaseResource extends JsonResource {
/**
* @param bool $condition
* @param Request $request
* @param JsonResource|string $instanceOrClass
* @param mixed|null $model
* @return MergeValue|mixed
*/
public function mergeResourceWhen($condition, $request, $instanceOrClass, $model = null)
{
return $this->mergeResourcesWhen($condition, $request, [$instanceOrClass], $model);
}
/**
* @param Request $request
* @param JsonResource|string $instanceOrClass
* @param mixed|null $model
* @return MergeValue|mixed
*/
public function mergeResource($request, $instanceOrClass, $model = null)
{
return $this->mergeResourceWhen(true, $request, $instanceOrClass, $model);
}
/**
* @param bool $condition
* @param Request $request
* @param JsonResource[]|string[] $instancesOrClasses
* @param mixed|null $model
* @return MergeValue|mixed
*/
public function mergeResourcesWhen($condition, $request, $instancesOrClasses, $model = null)
{
return $this->mergeWhen($condition, function () use ($request, $instancesOrClasses, $model) {
return array_merge(...array_map(function ($instanceOrClass) use ($model, $request) {
if ($instanceOrClass instanceof JsonResource) {
if ($model) {
throw new RuntimeException('$model is specified but not used.');
}
} else {
$instanceOrClass = new $instanceOrClass($model ?? $this->resource);
}
return $instanceOrClass->toArray($request);
}, $instancesOrClasses));
});
}
/**
* @param Request $request
* @param JsonResource[]|string[] $instancesOrClasses
* @param mixed|null $model
* @return MergeValue|mixed
*/
public function mergeResources($request, $instancesOrClasses, $model = null)
{
return $this->mergeResourcesWhen(true, $request, $instancesOrClasses, $model);
}
}
Model1Resource
(no need here to extend BaseResource
but I always inherit all my API resource classes from my own custom base class):
class Model1Resource extends JsonResource {
public function toArray($request)
{
return [
id => $this->id,
name => $this->name,
];
}
}
Model2Resource
:
class Model2Resource extends BaseResource {
public function toArray($request)
{
return [
$this->mergeResource($request, Model1Resource::class),
alternate_name => $this->alternate_name,
child_name => $this->child_name,
parent_name => $this->parent_name,
sibling_name => $this->sibling_name
];
}
}
If you want to merge multiple resources then you can use:
$this->mergeResources($request, [Model1Resource::class, SomeOtherResource::class]);
If you want to merge it by condition:
$this->mergeResourceWhen($this->name !== 'John', $request, Model1Resource::class);
// or merge multiple resources
$this->mergeResourcesWhen($this->name !== 'John', $request, [Model1Resource::class, SomeOtherResource::class]);
By default merged resources will use current model available by $this->resource
.
To pass other model to merged resources use last parameter of above methods:
$this->mergeResource($request, SomeModelResource::class, SomeModel::find(123));
$this->mergeResourcesWhen($this->name !== 'John', $request, [SomeModelResource::class, SomeOtherResource::class], SomeModel::find(123));
or pass JsonResource
instance(s) instead of resource class(es):
$someModel = SomeModel::find(123);
$someOtherModel = SomeOtherModel::find(456);
$this->mergeResource($request, new SomeModelResource($someModel));
$this->mergeResourcesWhen($this->name !== 'John', $request, [new SomeModelResource($someModel), new SomeOtherModelResource($someOtherModel)]);