framework icon indicating copy to clipboard operation
framework copied to clipboard

[9.x] Get JsonResource from the container

Open reindert-vetter opened this issue 2 years ago • 0 comments

It's not always possible to resolve the JsonResource from the container. This is because the JsonResource expects a argument in the constructor. This argument can be anything (mixed).

I often do this because both my FormRequest and JsonResource vary by version. An example where $resource could be ProductResourceV1 or ProductResourcev2:

    public function index(ProductIndexRequest $request, ProductResource $resource): ResourceCollection
    {
        $products = Product::query()
            ->deleted($request->isDeleted())
            ->orderBy($request->sort(), $request->direction())
            ->search($request->search())
            ->with($resource->withRelationships($request->includes()));

        return $resource::collection($products->paginate($request->limit()));
    }

Illuminate\Contracts\Container\BindingResolutionException : Unresolvable dependency resolving [Parameter #0 [ <required> $resource ]] in class App\Http\Resources\ProductResource

Workarounds:

Option 1. Make parameter optional:

abstract class ProductResource extends JsonResource
{
    /**
     * @param Product[]|Product $resource Optional to allow dependency injection.
     */
    public function __construct($resource = null)
    {
        parent::__construct($resource);
    }

Option 2. Make sure the binding adds a null value:

app()->bind(ProductResource::class, function(Container $container){
    return new ProductV2Resource(null);
});

Resolving the resource is very common in a package I use: https://github.com/reindert-vetter/api-version-control . I normally use option 1, but because more and more users are also using this package, it would be great if this hack is no longer needed.

reindert-vetter avatar Aug 10 '22 08:08 reindert-vetter

I don't feel this is a good idea for core. The JsonResource is a value object.

If you need varying implementations, you should just create varying implementations you should create and inject a Factory and have the logic to determine which resource to create abstracted behind the factory.

public function index(ProductIndexRequest $request, ProductResourceFactory $factory)
{
    $products = Product::query()
        // ...
        ->with($resource->withRelationships($request->includes()));

    return $factory->collection($products->paginate($request->limit()));
}

timacdonald avatar Aug 11 '22 00:08 timacdonald

It's never meant for JsonResource to be resolved from core. Please see the docs: https://laravel.com/docs/9.x/eloquent-resources. Also see Tim's suggesting above.

driesvints avatar Aug 11 '22 06:08 driesvints