Every request is routed to a Handler
, which is responsible for authorizing the request, fetching data and formatting the response.
Code inside of a Handler
can not be reused, so Handler
s should not perform these actions themselves. Instead, they should coordinate other classes to fulfil the request.
Handler
s do not authorize the request and should not be used without appropriate authorization policies.
Tip:
Handler
s perform the role ofController
s in the MVC (Model-View-Controller) application architecture.
Page Handlers receive GET
requests and return HTML
output. The Page Handler must define a method for each op it supports.
use APP\handler\Handler;
class IssueHandler extends Handler
{
/**
* Display the table of contents for the current issue
*/
public function current(array $args, Request $request): string
{
return '<html>...</html>';
}
}
Learn more about page URLs, routes and ops.
Page Handlers may define an index
op to handle URLs that do not contain an op.
use APP\handler\Handler;
class IssueHandler extends Handler
{
/**
* Display a list of all issues
*/
public function index(array $args, Request $request): string
{
return '<html>...</html>';
}
}
The router must declare the index
op:
switch ($op) {
case 'index':
define('HANDLER_CLASS', 'IssueHandler');
import('pages.issue.IssueHandler');
break;
}
Any URL fragments that are appended after the op will be passed to the Handler
’s method in the $args
param.
use APP\handler\Handler;
class IssueHandler extends Handler
{
public function view(array $args, Request $request): string
{
$issueId = isset($args[0]) ? (int) $args[0] : null;
return '<html>...</html>';
}
}
Return a 404
error when page arguments request an entity that does not exist.
use APP\handler\Handler;
class IssueHandler extends Handler
{
public function view(array $args, Request $request): string
{
$issueId = isset($args[0]) ? (int) $args[0] : null;
if (/* issue not found */) {
$this->getDispatcher()->handle404();
}
return '<html>...</html>';
}
}
Page Handlers return HTML code using the TemplateManager
.
use APP\handler\Handler;
class IssueHandler extends Handler
{
public function current(array $args, Request $request)
{
$templateMgr = TemplateManager::getManager($request);
return $templateMgr->display('/path/to/template.tpl');
}
}
Read the Frontend section of this documentation to learn more about templates.
API Handlers use the Slim API framework and return JSON
output. Requests are routed to endpoint callbacks defined in the Handler
’s constructor.
$ curl https://example.org/publicknowledge/api/v1/submissions
use PKP\core\APIResponse;
use Slim\Http\Request as SlimRequest;
use PKP\handler\APIHandler;
class PKPSubmissionHandler extends APIHandler
{
public function __construct()
{
$this->_endpoints = [
'GET' => [
[
'pattern' => 'submissions',
'handler' => [$this, 'getMany'],
],
],
];
}
public function getMany(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse
{
return $response->withJson([...], 200);
}
}
Routing can identify named variables in the URL path that are part of the endpoint.
$ curl https://example.org/publicknowledge/api/v1/submissions/1
use PKP\core\APIResponse;
use Slim\Http\Request as SlimRequest;
use PKP\handler\APIHandler;
class PKPSubmissionHandler extends APIHandler
{
public function __construct()
{
$this->_endpoints = [
'GET' => [
[
'pattern' => 'submissions/{submissionId}',
'handler' => [$this, 'get'],
],
],
];
}
public function get(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse
{
$submissionId = (int) $args['submissionId'];
return $response->withJson([...], 200);
}
}
Query parameters in the URL can be accessed through the $slimRequest
object.
$ curl https://example.org/publicknowledge/api/v1/submissions?searchPhrase=barnes
use PKP\core\APIResponse;
use Slim\Http\Request as SlimRequest;
use PKP\handler\APIHandler;
class PKPSubmissionHandler extends APIHandler
{
public function __construct()
{
$this->_endpoints = [
'GET' => [
[
'pattern' => 'submissions',
'handler' => array($this, 'getMany'),
],
],
];
}
public function getMany(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse
{
$params = $slimRequest->getQueryParams(); // ['searchPhrase' => 'barnes']
return $response->withJson([...], 200);
}
}
POST
and PUT
requests include data in the body of the request, which can be accessed with $slimRequest->getParsedBody()
.
$ curl https://example.org/publicknowledge/api/v1/submissions/1 \
-d '{"contactEmail": "editor@example.org"}' \
-H "Content-Type: application/json" \
-X PUT
use PKP\core\APIResponse;
use Slim\Http\Request as SlimRequest;
use PKP\handler\APIHandler;
class PKPSubmissionHandler extends APIHandler
{
public function __construct()
{
$this->_endpoints = [
'PUT' => [
[
'pattern' => 'submissions/{submissionId}',
'handler' => array($this, 'edit'),
],
],
];
}
public function edit(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse
{
$data = $slimRequest->getParsedBody(); // ['contactEmail' => 'editor@example.org']
...
}
}
API Handlers are passed a $response
object which should be returned using the $request->withJson()
method.
use PKP\core\APIResponse;
use Slim\Http\Request as SlimRequest;
use PKP\handler\APIHandler;
class PKPSubmissionHandler extends APIHandler
{
public function __construct()
{
$this->_endpoints = [...];
}
public function get(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse
{
return $response->withJson([
'id' => 1,
'title' => 'Example Submission',
], 200);
}
}
Always return the correct HTTP Status Code.
use PKP\core\APIResponse;
use Slim\Http\Request as SlimRequest;
use PKP\handler\APIHandler;
class PKPSubmissionHandler extends APIHandler
{
public function __construct()
{
$this->_endpoints = [...];
}
public function get(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse
{
if (/* no submission found */) {
return $response->withJson(false, 404);
}
if (/* submission access not allowed */) {
return $response->withJson(false, 403);
}
return $response->withJson([
'id' => 1,
'title' => 'Example Submission',
], 200);
}
}
Error responses from the API should pass a locale key that describes the error.
use PKP\core\APIResponse;
use Slim\Http\Request as SlimRequest;
use PKP\handler\APIHandler;
class PKPSubmissionHandler extends APIHandler
{
public function __construct()
{
$this->_endpoints = [...];
}
public function get(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse
{
if (/* no submission found */) {
return $response->withStatus(404)->withJsonError('api.submissions.404.submissionNotFound');
}
...
}
}
The $slimRequest
object is a PSR 7 object created by the Slim API framework. It is not an instance of the application’s main Request
object that is passed to other Handler
ops. The application’s main Request
object can be accessed with APIHandler::getRequest()
.
use PKP\core\APIResponse;
use Slim\Http\Request as SlimRequest;
use PKP\handler\APIHandler;
class PKPSubmissionHandler extends APIHandler
{
public function get(SlimRequest $slimRequest, APIResponse $response, array $args): APIResponse
{
$request = $this->getRequest();
echo get_class($request); // APP\core\Request
}
}
Read the Slim API Framework usage guide to learn more about the $slimRequest
and $response
objects.
Controller Handlers are deprecated. New features should be built using the UI Library components that interact with API Handlers.
Controller Handlers receive requests from UI Controllers and return JSON
output. They act like Page Handlers except that they serve individual interactive UI components, such as a submission’s list of files, discussions or participants. Learn more about working with controllers.