3.4 3.3
Jump to table of contents

Handlers

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 Handlers should not perform these actions themselves. Instead, they should coordinate other classes to fulfil the request.

Handlers do not authorize the request and should not be used without appropriate authorization policies.

Tip: Handlers perform the role of Controllers in the MVC (Model-View-Controller) application architecture.

Page Handlers #

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 Indexes #

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;
}

Page Arguments #

Any URL fragments that are appended after the op will be passed to the Handler’s method in the $args param.

Diagram indicating the parts of a URL for Page Handlers

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>';
    }
}

Responses #

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 #

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);
    }
}

Path Variables #

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 Params #

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);
    }
}

Request Body #

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']
        ...
    }
}

Responses #

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');
        }
        ...
    }
}

Request Objects #

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
    }
}

Slim Framework #

Read the Slim API Framework usage guide to learn more about the $slimRequest and $response objects.

Controller Handlers (deprecated) #

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.


Learn how the user is authenticated.