将Laravel Resources合并为一个

时间:2018-02-07 17:40:56

标签: php laravel

我有两个模型资源,我希望将其合并到一个平面数组中,而不必明确定义其他资源的所有属性。

模型1:

id
name
created_at

模型2:

id
alternate_name
child_name
parent_name
sibling_name
created_at

Model1Resource

public function toArray($request)
{
    return [
        id => $this->id,
        name => $this->name,
    ]
}

模型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
    ]
}

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

Model2 => new Model2Resource($this->model2);

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

[id, name, alternate_name, child_name, parent_name, sibling_name]

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

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

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

2 个答案:

答案 0 :(得分:1)

因此,经过一番挖掘,这似乎并不容易。我决定最简单的方法是重新定义第一个模型中的输出,并使用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
        ];
    }
]

答案 1 :(得分:0)

为您的资源创建基类:

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(这里不需要扩展BaseResource,但我总是从我自己的自定义基类继承所有API资源类):

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
        ];
    }
}

如果要合并多个资源,则可以使用:

$this->mergeResources($request, [Model1Resource::class, SomeOtherResource::class]);

如果要按条件合并它:

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

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

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

或传递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)]);