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

Resource adapters in subdirectories #605

Closed mjackz closed 3 years ago

mjackz commented 3 years ago

Hi there, I have an application with a ton of models organized in several folders, and wanted to create and group the api resources in folders as well as the models, but it seems im missing something. Dont find anything in the documentation about creating resources inside folders, for example:

php artisan make:json-api:resource Blog/posts v1

creates the resource files into the expected namespace JsonApi/Blog/Posts folder, but in the json-api-v1.php config file dont know how to tell the app where the resource is:

'resources' => [
    'posts' => \App\Models\Blog\Post::class,
]

and im getting No adapter for resource type: posts error. Im changing the created files' namespace to App\JsonApi\Blog\Posts and also tried creating the resource the default way (without using a subdirectory) and then refactoring/moving the created files to a subdirectory and updating the manespace, but it didnt work.

Is there a way to organize or tell the app that the Schema $resourceType is in a subdirectory?

I'm using Laravel 8.37 & json-api 3.3

Thanks, Manuel

pd. great package, u have done a incredible job!

ben221199 commented 3 years ago

I also store my models in subfolders. I remember that I had to override the resolver.

lindyhopchris commented 3 years ago

Yeah if you want to change any of the resolution logic, you need to override the resolver.

That's documented here: https://laravel-json-api.readthedocs.io/en/latest/features/resolvers/

mjackz commented 3 years ago

Hi again, thank you both for your fast response, I've some more question if i may,

I read the resolver docs you mentioned and wrote a custom ResourceResolver:

namespace App\JsonApi;
use Illuminate\Support\Str;
use CloudCreativity\LaravelJsonApi\Resolver\AbstractResolver;

class ResourceResolver extends AbstractResolver
{
    protected function resolve($unit, $resourceType)    {
        $moduleName = 'calc-the-module-name';
        $resourceName = ucfirst(Str::singular($resourceType));
        return "app\\JsonApi\\{$moduleName}\\{$resourceName}\\{$unit}";
    }
}

so at the end i want to organize resources organized like: JsonApi > moduleName > resourceName > Units (adapter, schema, validators etc...) and set the resolver in the json-api-v1 config (I think dont need a Factory because dont need to access config settings):

return [
   'resolver' => \App\JsonApi\ResourceResolver::class,
   'namespace' => null,    // JsonApi
   'by-resource' => true,
   'model-namespace' => 'App\Models',
    'resources' => [
         // Application Module
     'users' => \App\Models\Application\User::class,
     ...
     // Contacts Module
     'companies' => \App\Models\Contact\Company::class,
     'clients' => \App\Models\Contact\Client::class,
         ... ,
   'providers' => [ ],
]

Bur I'm getting this error: Unresolvable dependency resolving [Parameter #0 [ <required> array $resources ]] in class CloudCreativity\LaravelJsonApi\Resolver\AbstractResolver Is there any binding, serviceProvider or setting to set that I'm forgetting? Thanks again

lindyhopchris commented 3 years ago

If you're extending the AbstractResolver that needs the resources passed into its constructor (as indicated by the error message). So you'll need to use a factory to inject the resources from the config.

If you don't need to inject the resources, then you shouldn't be extending the AbstractResolver.

Hope that helps!

ben221199 commented 3 years ago

I don't know if you are allowed to enter a resolver directly instead of a factory. What I know, is that the factory has a constructor without any parameters, so it could be instantiated very easily. After having a factory instance, a method is called on that factory to create the resolver.

If you are allowed to use a resolver directly, you should override your constructor, if you aren't, you need a factory.

lindyhopchris commented 3 years ago

I believe you can enter a resolver without a factory - the factory is only required if you need to create the resolver using the config.

ben221199 commented 3 years ago

Okay, in that case you could override the constructor like this:

public function __construct(){
    parent::__construct([
        //Some hardcoded resources.
    ]);
}