将Laravel资源合并为一个

hdi*_*fen 6 php laravel

我有两个模型资源,我想合并到一个平面数组中,而不必显式定义其他资源的所有属性。

模型1:

id
name
created_at
Run Code Online (Sandbox Code Playgroud)

模型2:

id
alternate_name
child_name
parent_name
sibling_name
created_at
Run Code Online (Sandbox Code Playgroud)

Model1资源

public function toArray($request)
{
    return [
        id => $this->id,
        name => $this->name,
    ]
}
Run Code Online (Sandbox Code Playgroud)

模型2资源

public function toArray($request)
{
    return [
        alternate_name => $this->alternate_name, 
        child_name => $this->child_name, 
        parent_name => $this->parent_name, 
        sibling_name => $this->sibling_name
    ]
}
Run Code Online (Sandbox Code Playgroud)

我希望Model1Resource在平面结构中包含Model2Resource。通过在资源中添加另一个属性,我可以轻松地在子数组中获得Model 2资源:

Model2 => new Model2Resource($this->model2);
Run Code Online (Sandbox Code Playgroud)

但这不是平面结构。理想情况下,我希望返回这样的结构。

[id, name, alternate_name, child_name, parent_name, sibling_name]
Run Code Online (Sandbox Code Playgroud)

我可以通过在Model1Resource中重新定义Model2Resource的所有属性来做到这一点,但这似乎是不必要的。

为了澄清起见,我指的是https://laravel.com/docs/5.5/eloquent-resources#writing-resources。在“关系”部分下,one to many使用演示了一种关系posts。但是,如果结构为one to oneI,我希望能够使它成为平面数组,而不是在其中一个属性中具有数组。

将这两种资源合并成一个平面结构的简单方法是什么?

mix*_*xel 6

为您的资源创建基类:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Model1Resource(这里不需要扩展BaseResource,但我总是从我自己的自定义基类继承所有 API 资源类):

class Model1Resource extends JsonResource {
    public function toArray($request)
    {
        return [
            id => $this->id,
            name => $this->name,
        ];
    }
}
Run Code Online (Sandbox Code Playgroud)

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
        ];
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你想合并多个资源,那么你可以使用:

$this->mergeResources($request, [Model1Resource::class, SomeOtherResource::class]);
Run Code Online (Sandbox Code Playgroud)

如果你想按条件合并:

$this->mergeResourceWhen($this->name !== 'John', $request, Model1Resource::class);
// or merge multiple resources
$this->mergeResourcesWhen($this->name !== 'John', $request, [Model1Resource::class, SomeOtherResource::class]);
Run Code Online (Sandbox Code Playgroud)

默认情况下,合并的资源将使用$this->resource. 要将其他模型传递给合并资源,请使用上述方法的最后一个参数:

$this->mergeResource($request, SomeModelResource::class, SomeModel::find(123));
$this->mergeResourcesWhen($this->name !== 'John', $request, [SomeModelResource::class, SomeOtherResource::class], SomeModel::find(123));
Run Code Online (Sandbox Code Playgroud)

或传递JsonResource实例而不是资源类:

$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)]);
Run Code Online (Sandbox Code Playgroud)


hdi*_*fen 5

因此,经过一番挖掘后,这似乎并不容易实现。我决定最简单的方法是重新定义第一个模型中的输出,并使用该mergeWhen()函数仅在关系存在时进行合并。

return [
    'id' => $this->id,
    'name' => $this->name,
    // Since a resource file is an extension
    // we can use all the relationships we have defined.
    $this->mergeWhen($this->Model2()->exists(), function() {
        return [
            // This code is only executed when the relationship exists.
            'alternate_name' => $this->Model2->alternate_name, 
            'child_name' => $this->Model2->child_name, 
            'parent_name' => $this->Model2->parent_name, 
            'sibling_name' => $this->Model2->sibling_name,
        ];
    }
]
Run Code Online (Sandbox Code Playgroud)