Services
Services are used to group endpoints. Since services are classes, they can have properties and methods.
A service can define any number of endpoints, and services may also contain child services, which will inherit hooks from the parent service.
You can also define service-wide request hooks, which will apply to every endpoint of the service and its child services. Read more about request hooks.
Example service
Create a new service by extending the Service class. Override the init() method to define one or more endpoints with handlers for different request methods. Read more about endpoints.
<?php namespace ProcessWire;
use PwJsonApi\{Service, Response};
class HelloWorldService extends Service
{
protected string $word = 'world';
public function __construct()
{
parent::__construct();
$this->setBasePath('/greet');
}
protected function init()
{
// Listen to the base path (/greet)
$this->addEndpoint('/')->get(function () {
return (new Response())->with([
'message' => 'Nothing to see here!',
]);
});
// Listen to path /greet/hello-world with GET handler
$this->addEndpoint('/hello-world')->get(function () {
return new Response($this->getData());
});
}
public function getData(): array
{
return [
'hello' => $this->word,
];
}
}Base path
Like the main instance, the service can also define its base path. Set the base path in the constructor so that extending classes can override it independently of the endpoint configuration.
public function __construct()
{
parent::__construct();
$this->setBasePath('/greet');
}Child services
In many cases, a flat service tree is enough, but for larger APIs you can define child services to further categorize the endpoints. Child services will inherit all hooks from parent service(s), and the base paths of the parent services will apply.
Child services can be defined in the service init() method by calling addService().
// In init()
$this->addService(new AnotherService());This relationship can also be defined on the fly while adding the service to the main instance, allowing you to nest services that normally would not be related to each other. See example of adding a child service on the fly.
Extending a service
Since services are classes, you can extend them to reuse or replace functionality. The init() method is intentionally separate from the constructor, giving the extending class full control over which endpoints are registered.
Set the base path in the constructor so it can be overridden independently of the endpoint configuration.
class GreetingService extends Service
{
public function __construct()
{
parent::__construct();
$this->setBasePath('/greet');
}
protected function init()
{
$this->addEndpoint('/hello')->get(function () {
return new Response(['message' => 'Hello!']);
});
$this->addEndpoint('/goodbye')->get(function () {
return new Response(['message' => 'Goodbye!']);
});
}
}Keeping parent endpoints
An extending class can call parent::init() to keep the parent endpoints and add its own:
class ExtendedGreetingService extends GreetingService
{
protected function init()
{
parent::init();
$this->addEndpoint('/hey')->get(function () {
return new Response(['message' => 'Hey!']);
});
}
}Replacing parent endpoints
Or it can skip parent::init() entirely to replace the endpoints:
class ReplacedGreetingService extends GreetingService
{
protected function init()
{
// Only /howdy is registered — parent endpoints are not included
$this->addEndpoint('/howdy')->get(function () {
return new Response(['message' => 'Howdy!']);
});
}
}Accessing the ProcessWire API in the service
Use wire property to access ProcessWire API
$page = $this->wire->pages->findOne('template=basic-page');Accessing the main instance in the service
A reference to the main instance will be injected into the api property of the service. You can use this to access methods and properties of other services.
WARNING
The main instance will be injected into the service when run() is called. Therefore, you cannot access api directly in the service init() method.
class HelloWorldService extends Service
{
protected function init()
{
// ❌ Trying to access API too early
$anotherService = $this->api->findService('AnotherService')
$this->addEndpoint('/use-another-service')->get(function () {
// ✅ This will work
$anotherService = $this->api->findService('AnotherService')
return new Response([
'hello' => 'world',
...($anotherService?->gimmeGimme() ?? []),
]);
});
}
}