This package uses your API's namespace
and by-resource
configuration settings to automatically
detect the fully-qualified name of your JSON API classes. This is described in the
API configuration chapter.
We work out the fully qualified name using a resolver. The strings that the resolver returns are then checked for whether either the class exists or whether there is a binding in the service container for the given name.
The resolver is also used when using the package's generators, as we ask the resolver for the fully qualified namespace of the class that we are generating.
Our default implementation will work for the majority of applications. However we do recognise that developers may prefer a different namespacing pattern than our implementation. This chapter therefore describes how to replace our implementation with your own.
A resolver is a class that implements our
CloudCreativity\LaravelJsonApi\Contracts\Resolver\ResolverInterface
.
To help you write your own implementation we also provide an abstract class that you can extend.
We recommend extending the abstract resolver.
In this example, we will use an application that has decided to organiser its namespaces in modules.
It may have a User
module that had the following structure:
- app/Modules
- User
- Models
- User.php
- Web
- Controllers
- ...
- Requests
- ...
- Post
...
In such a structure, we may want to store our JSON API classes in each module, under an Api
namespace.
For example:
- app/Modules
- User
- Api
- Adapter.php
- Schema.php
- ...
The following resolver would implement this strategy:
<?php
namespace App\Modules;
use CloudCreativity\LaravelJsonApi\Resolver\AbstractResolver;
class ModuleResolver extends AbstractResolver
{
/**
* @param string $unit
* @param $resourceType
* @return string
*/
protected function resolve($unit, $resourceType)
{
$module = ucfirst(str_singular($resourceType));
return "App\\Modules\\{$module}\\Api\\{$unit}";
}
}
The resolve
method receive the unit type that it is resolving and the JSON API resource type name.
The unit type can be:
Adapter
Authorizer
Schema
Validators
The above implementation would therefore return App\Modules\User\Api\Adapter
when the unit is
Adapter
and the resource type is users
.
When returning a string from the resolve
method you do not need to test whether it
exists. The purpose of the method is to return the expected class name or container binding name.
Although resolvers can return container binding names rather than class names, this will mean the generators will not work.
If you do not need to access any configuration for your API when creating your resolver,
you can use your resolver by adding its fully qualified class name (or a container binding
name) to your API's resolver
configuration setting.
For example:
// config/json-api-v1.php
return [
'resolver' => \App\Modules\ModuleResolver::class,
// ...
];
If you need access to your API's configuration when creating a resolver, or if you need
to calculate any resolver settings, you can create the resolver via a factory. For example,
if you have extended our abstract resolver you will need to provide the resources
value
from the config when constructing your resolver.
The factory is an invokable class that receives the API name and the API's config. For the above example, our factory would be:
namespace App\Modules;
class CreateModuleResolver
{
public function __invoke($apiName, array $config)
{
return new ModuleResolver($config['resources']);
}
}
Resolver factories are constructed via the service container so you can use constructor injection for any dependencies.
Then all we would need to do is change the resolver
value in our API's configuration so that
it uses our factory:
// config/json-api-v1.php
return [
'resolver' => \App\Modules\CreateModuleResolver::class,
// ...
];
It is worth noting that controller names are not detected via the resolver. This is because Laravel
routing uses the namespace
route group option, and all JSON API routes are implemented using route
groups. More details on group namespaces can be found in the
Laravel routing documentation.
For our example above, our routing might look like this:
// routes/api.php
// Assumes the namespace in our RouteServiceProvider is `App\Modules`.
JsonApi::register('default', [], function ($api) {
Route::namespace('User\Api\Controllers')->group(function () use ($api) {
// App\Modules\User\Api\Controllers\UserController
$api->resource('users', ['controller' => true]);
$api->resource('user-profiles');
});
Route::namespace('Posts\Api\Controllers')->group(function () use ($api) {
$api->resource('posts');
// App\Modules\Posts\Api\Controllers\PostCommentsController
$api->resource('comments', ['controller' => 'PostCommentsController']);
});
});
Remember that if no controller is specified, the package's JsonApiController
will be used. For
more details, see the controllers chapter.