Service is central part of any API that concentrate all features(operations) realted to some application entity. Service define list of actions (operations) that performed on associated entity. For example, for RESTful service there could be described 4 default operations using HTTP verbs for each of CRUD operations.
Each Service could be an separate class located in src/Service folder, or request could be passed through default FallbackService that implements default behavior. Fallback service defined by setting Api.ServiceFallback and by default this is \CakeDC\Api\Service\FallbackService.
To create a new Service first extend the CakeDC\Api\Service\Service class. The name of your service class should be the name of the Service, followed by "Service" suffix.
namespace App\Service;
use CakeDC\Api\Service\Service;
class FooService extends Service {
// your code
}
The filename should follow the same nomenclature. However, the location of the file itself depends upon your versioning strategy.
Service layer contemplates a versioned API if it is enabled by settings. Enabling it allow you to scale your Services by versioning them as your applications grows. The benefit of this isn't so obvious when developing your application internally, yet becomes very important if you expose your service as an external API.
The version of your Service is determined by convention, this being the name of the subdirectory under app/Service/
, or in a plugin. For example, if the first version of your ExampleService
were to be "v1", you'd create file app/Service/v1/ExampleService.php
in App\Service\v1
namespace.
You can then create a new version of your Service by simply creating a new class under app/Service/v2/ExampleService.php
. Services may also be loaded from plugins using dot notation.
Returning back to newly created FooService class. It must have declared loadRoutes
method that defines this service behavior. Inside this method we should not use default Router class, because we should not rewrite current cakephp router states during our service url analyze. Instead api plugin provide ApiRouter
.
Imagine we want to have /foo/publish action that should accept HTTP POST requests.
In this case we define next loadRoutes
function.
public function loadRoutes()
{
ApiRouter::scope('/', $this->routerDefaultOptions(), function (RouteBuilder $routes) {
$routes->extensions($this->_extensions);
$options = [
'map' => [
'publish' => ['action' => 'publish', 'method' => 'POST', 'path' => ''],
]
];
$routes->resources($this->name(), $options);
});
}
There provided two strategies to load actions.
First strategy is based on service location namespace. In this case all actions supposed to located in Action sub-namespace.
So if we have App\Service\FooService
service and want to add publish
action then we should create file App\Service\Action\FooPublishAction
, where Action
that belongs to namespace App\Service\Action
and class defined is 3 parts: Foo is a camelized service name, Publish is a camelized action name, and Action is a suffix.
Second loading strategy could be achieved used $_actionsClassMap
property. It contains map of action names and full class names, eg.
protected $_actionsClassMap = [
'index' => '\CakeDC\Api\Service\Action\CrudIndexAction',
'view' => '\CakeDC\Api\Service\Action\CrudViewAction',
];
Each service action defined extending Action class.
Action has execution life cycle.
There are two ways to action business logic.
First way is define action
method in user's action class, where argument are named same as input api endpoint parameters. (like in was enterprise plugin).
In other case action logic should located in execute
method. In this case method does not accept any parameters and user should interact with
If action require input data validation it must overload validates()
method that returns boolean result of validation or could throw ValidationException
. Such methods like index, or view obviously don't require any validation in it's lifecycle, and returns true
by default.
Please note, that action validation is not the same as model level validation. The action validation purpose is to validate action input data and check it correctnes and consistence, and in case it is invalid prevent stop action execution.
Action.beforeExecute Action.beforeProcess Action.onAuth Action.beforeValidate Action.afterProcess
Crud service defines actions and parameters for RESTful crud API.
Nested Crud service gentting parent params from routing system and if it is presents loads Nested extension for all actions.
Falback service is default implemeention of Nested Crud that defines routes for 1-level deep nesting.
Listing service returns list of all available in system services.
Any action are decorated by some functionality it is implements during it life flow. Such decorators called extensions and provided with api plugin.
Different extension could return additional info that extends returned by API data. In this case extension append payload data into Result object that used by renderers to build final output.
This way such extensions like pagination or hateoas inteact with caller.
A Request parser provides the logic required by a Service to resolve requests input data.
Each Service class defines it's request parser in the parserClass
options property, and by default populated from Api.parser setting.
A Renderer provides the logic required by a Service to process the response and handle errors..
Each Service class defines it's renderer in the rendererClass
options property, , and by default populated from Api.renderer setting.
Suported next renderers:
- Json - json object.
- JSend json object in JSend format.
- Raw - returns data as it is provided.
- Xml - format result data as xml.
Each JSend object on top level has result and data items. Additionally all metadata appended here too.
Links is a information how current api endpoint related with other endpoints.
If we will talk about crud actions there defined next links:
- index - have links to add action.
- add - have links to index action.
- edit - have links to edit, delete and index actions.
- view - have links to edit, delete and index actions. Have links to index action of all nested services.
By default the Service automatically handles many of the common errors in a request. The following are the status codes returned by the API.
- 401 Unauthorized: If authentication is required but fails for the request.
- 403 Forbidden: If a private method is requested or the action is blacklisted.
- 404 Not Found: If the method is not defined on the Service class.
- 405 Method Not Allowed: If an invalid HTTP method is used for the request.
- 409 Conflict: If required arguments are missing from the request.
- 500 Internal Server Error: If an error is thrown and not handled by the Service.