Services
This section assumes that you have a basic understanding of the Pionia framework. If you are new to Pionia, you can start with the tutorial.
Introduction
Services in Pionia Framework are central containers of business logic. This is where most of the work happens.
Pionia has tried to reduce your work from other areas so that you mainly focus on this essential area.
Services are in actual PHP code, just php classes that extend the Pionia\Http\Services\Service
.
As you might already know, a class can have multiple methods. In Pionia we call these Actions
. Therefore, henceforth, the terms service
and actions
will be used for the same meaning throughout the same guide.
Creating a service
You can create a service using our pionia console or manually. All services, as a convention, MUST be located in the services
folder.
We recommend to name your services after your database tables. Example, if your table is called users
, you can name your service UserService
.
If you are using our ‘pionia console’, then you can just name your service user
. These are just conventions!
Remember generic services target a base table.
Therefore, you shall be asked the database table name you want to target. This is required.
However, starting from version 1.1.7, you can target relationships too!
You can read more about this in the Generic Services Section.
Service Registration
Creating a service is not enough in Pionia. You also need to register it in any switch to make it discoverable by the kernel. Service registration happens in the associated switch.
In the switches folder, find the switch you want to use for this service. You can add your service as below.
public function registerServices(): Arrayable
{
return arr([
'user' => new UserService(),
"todo" => TodoService::class, // this is okay
'auth' => AuthService::class, // and this too
]);
}
The keys
of are the names you shall use in your proceeding requests to access/identify this service. Therefore, it must be unique per switch!
A single service can be registered in multiple switches. This is useful when you want to use the same service in different api versions.
Targeting a service in the request
In the request, you can target a service by determining the SERVICE
key with your service name as the key
defined in the registerServices
method.
{
SERVICE: "user";
// rest of your request data.
}
For details about request and responses, you can check the request and response section.
Actions
You can read more about actions in the actions section.
Service Security
You can mark an entire service as requiring authentication by setting the $serviceRequiresAuth
parameter to true
.
class TodoService extends Service
{
public bool $serviceRequiresAuth = true; // all actions in this service require authentication.
// your other actions here
}
If the flag is set to true, all actions in the service will require authentication. This means that only authenticated users will be able to access the service.
Specific actions
You can also mark specific actions in a service as requiring authentication. Use the $actionsRequiringAuth
parameter and add action names of actions that should be reached by authenticated users only.
This, unlike $serviceRequiresAuth
, will only protect the actions listed in the array not the entire service.
class TodoService extends Service
{
public bool $actionsRequiringAuth = ['getTodo'];
// your other actions here
}
Error Handling
According to Moonlight architecture, all requests should return a 200 Ok status code. This is because the client should
be able to know if the request was successful or not by checking the returnCode
in the response body.
All normal responses set this internally and are always returning a 200 status code. By convention and by default, all requests
that are successful return 0 as the returnCode
. This implies that the server can define multiple other return codes
for other scenarios.
In Pionia, we have a global exception handler that catches all exceptions thrown anywhere in the code. This is to ensure that the client always gets the same response format.
All exceptions thrown are caught will raise a 500 status code and the message of the exception will be sent back to the client
as the returnMessage
.
Therefore, in your services and actions, you can throw exceptions as you see fit. And you don’t need to catch them at all!
protected function getTodoAction($data): BaseResponse
{
$this->mustAuthenticate();
if(blank($data->get('id'))){
throw new \Exception('Todo id is required'); // will be caught globally!
}
// rest of actions logic
return response(200, 'Todo fetched successfully', $todo);
}
Please note that all exceptions are caught globally and sent back to the client. Therefore, you do not need to catch exceptions in your services. Developers need to set clean, descriptive exception messages in their exceptions to help the client understand what went wrong.
Deactivating actions in a service
BaseRestService provides a parameter $deactivatedActions
that can be used to register all deactivated actions in a service.
This is useful when you want to deactivate an action in a service without deleting it.
class TodoService extends Service
{
public array $deactivatedActions = ['getTodo']; // one or more actions to deactivate.
}
Deactivated actions will not be called by the switcher. Therefore, they will not be accessible by the client.