cloudcreativity / laravel-json-api

JSON API (jsonapi.org) package for Laravel applications.
http://laravel-json-api.readthedocs.io/en/latest/
Apache License 2.0
778 stars 109 forks source link

Custom Controller with fetching of multiple resources #606

Closed ranferi closed 3 years ago

ranferi commented 3 years ago

Hi, I'm trying to implement a custom controller that will return a custom json with zero or more resources of my API.

In my API, there's resources like Brands, Colors, Materials, etc. and I created a controller just like the documentation says:

class CatalogsController extends JsonApiController
{
    public function index(StoreInterface $store, FetchResources $request): Response
    {  }
}

to send zero or multiples resources in this custom json:

{
   "data": [{
      "brands": []
   },
   {
      "materials": []
   }
   ]
}

I added my custom controller and action to the api route: $api->get('/catalogs', [CatalogsController::class, 'index']);

This endpoint may receive a array with the resource names (in the request) that want to get back: ['brands', 'materials' ]

So far I only have this code in my action 'index' (there's more logic to get the singular of the resoures from the request array) :


$model = "App\Models\Brand";
$resource = $model::all();
return $this->reply()->content($resource);

which returns correctly all the data from Brands.

UPDATE: this code reflect better what I want to achieve:

$catalogs = ['brands'];
$json = [];

foreach ($catalogs as $catalog ) {
    $modelName = Str::ucfirst(Str::singular($catalog));
    $model = "App\Models\Catalogs\\$modelName";
    $resource = $model::all();
    $json[$catalog] = $this->reply()->content($resource);
}

return['data' => $json];

with this response (cut to be brief):

 "data": {
        "brands": {
            "headers": {},
            "original":"{\"data\":[{\"type\":\"brands\",\"id\":\"1\",\"attributes\":{\"name\":\"#FR2\",\"brandEcommerceId\":495,\"active\":1,\"createdAt\":\"2020-07-08T00:38:02.000000Z\",

How can I get the data from a resource and added to a custom Json using the laravel-json-api package?

lindyhopchris commented 3 years ago
$catalogs = ['brands'];
$models = collect();

foreach ($catalogs as $catalog ) {
    $modelName = Str::ucfirst(Str::singular($catalog));
    $model = "App\Models\Catalogs\\$modelName";
    $models->merge($model::all());
}

return $this->reply()->content($models);
ranferi commented 3 years ago

Thanks @lindyhopchris! now I have the data I want on the response. The only think I have to modified is $models->merge($model::all()); to $models = $models->merge($model::all());. Reading the docs the reply() method can help to shape the response but there's still no documentation on the chapter about responses. Is there a way to modify the response to look like the structure I talk in my post?

"data": [{
      "colors": [
          {
            "type": "colors",
            "id": "1",
            "attributes": {
                "name": "Black",
                "active": 0,
                "createdAt": "2019-05-03T01:06:58.000000Z",
                "updatedAt": "2019-05-03T01:06:58.000000Z"
            },
            "links": {
                "self": "http://api.test/api/v1/colors/1"
            }
        }
      ]
   }, {
      "brands": []
   }
]
ben221199 commented 3 years ago

Is it correct that this isn't valid JSON:API?

lindyhopchris commented 3 years ago

@ranferi the structure you want isn't valid JSON:API.

ranferi commented 3 years ago

Hi. Yeah I know. Another third party team ask me for this specific API endpoint. All my other endpoints follows the JSONAPI specifications. I made negotiations to return the data as the JSONAPI (with ID, type, attributes) but they want at least the names of every catalog as the properties just like the json structure I post. That's why I was asking if there's a way to change the returned json

lindyhopchris commented 3 years ago

You'd need to encode it yourself, as obviously this package can only encode to the specifcation.

To encode a single model to its JSON:API representation, use:

$encoder = json_api()->encoder();
$resource = $encoder->serializeData($model)['data'];
lindyhopchris commented 3 years ago

PS: if you have multiple APIs, you'll need to provide the name of the API to the json_api() helper.

ranferi commented 3 years ago

@lindyhopchris Thank you for your help!!

lindyhopchris commented 3 years ago

No problem!