Wiki
Clone wikiactionhandler / Home
Web Action Handler Documentation
In this documentation you'll be able to learn how framework works in background and how to properly use it.
Idea behind this framework is different approach to resolving control flow of an request, instead of using one PHP class as controller for many related actions e.g UserController
we split each different action per class, and inside that class we implement wanted and required interfaces to make everything work.
Here we'll go through all important and helpful stuff. So please buckle up because we are about to go deep.
Note: Every part of framework is expecting interfaces as input parameters, so it has flexibility to completely change flow of specific part of it.
Note: This documentation might and will change.
Modules
Utilities
Modules
Application
Consider following list as well
Application is hearth of framework, its used to boot everything up, which means to prepare database connections, to register all routes and to handle request with corresponding route handler.
Application::__construct(string, IRouter, IRequest, IResponse)
To instantiate Application
use ObjectFactory
, only required parameter that script can not determine here is first parameter which is string, that string represents path to configuration file and its required to be provided to ObjectFactor
.
Example:
#!php <?php use \RequestHandler\Modules\Application\IApplication; use \RequestHandler\Utils\ObjectFactory\ObjectFactory; $app = ObjectFactory::create(IApplication::class, "/path/to/config.json");
IApplication::boot(\Closure)
Used to load configuration, connect to database, and after success connection execute callback method that provides instance of \RequestHandler\Modules\Router\IRouter
used to register routes.
Example:
#!php <?php use \RequestHandler\Modules\Application\IApplication; use \RequestHandler\Modules\Router\IRouter; $app = ObjectFactory::create(IApplication::class, "/path/to/config.json"); $app->boot(function (IRouter $router) { /* This will get triggered after configuration file is loaded and connection to database was established */ $router ->get('/user/:user_id', \Controllers\User\GetInfo::class) ->patch('/user/:user_id', \Controllers\User\UpdateInfo::class); });
IApplication::setAttribute(string: name, mixed: value): IApplication
Sets attribute that is shared through request entire flow (middlewares, filters, validators etc...) and can be accessed using getAttribute
Example:
#!php <?php $app->setAttribute('server-region', 'eu');
IApplication::getAttribute(string: name[, mixed: default = null]): mixed
Retrieve value that was set through setAttribute
or return $default
if attribute is not set.
Example:
#!php <?php $reqion = $app->getAttribute('server-region', 'us');
Application Request
Request Handler a.k.a IHandle
Interface \RequestHandler\Modules\Application\ApplicationRequest\IHandle
is used to tell application that given class is able to handle single request, if class that is provided to router does not implement this interface exception ApplicationException
is thrown. As framework is written using PHP 7.1+ syntax required return type of handle
method is IResponse
.
Example:
#!php <?php namespace Controllers\User; use \RequestHandler\Modules\Application\ApplicationRequest\IHandle; class GetInfo implements IHandle { public function handle(IRequest $request, IResponse $response): IResponse { $user = new User(); // Do some logic // Return response return $response->data(['user' => $user]); } }
Request Middleware a.k.a IMiddleware
Interface \RequestHandler\Modules\Application\ApplicationRequest\IMiddleware
is used to tell application that given request handler also has some middlewares that needs to be executed, after or before request handle method is executed. If any of middlewares failes, request is finished with error message what failed and all custom response errors.
Middleware method gives access to IMiddlewareContainer
that is used to register and execute request middlewares from application.
Request middlewares must implement RequestHandler\Modules\Middleware\IMiddlewareHandler
in order for application to execute them.
Example:
#!php <?php // app/middlewares/Authenticate.php use RequestHandler\Modules\Middleware\IMiddlewareContainer; use RequestHandler\Modules\Middleware\IMiddlewareHandler; use RequestHandler\Modules\Request\IRequest; use RequestHandler\Modules\Response\IResponse; class CustomMiddleware implements IMiddlewareHandler { public function handle(IRequest $request, IResponse $response, IMiddlewareContainer $middleware): void { // Some Logic if ($allGood) { $middleware->next(); } // Can put some logic here as well } } // app/controllers/user/GetInfo.php namespace Controllers\User; use \RequestHandler\Modules\Application\ApplicationRequest\IHandle; use \RequestHandler\Modules\Application\ApplicationRequest\IMiddleware; class GetInfo implements IHandle, IMiddleware { public function handle(IRequest $request, IResponse $response): IResponse { $user = new User(); // Do some logic // Return response return $response->data(['user' => $user]); } public function middleware(IMiddlewareContainer $middleware): IMiddlewareContainer { return $middleware->add(new CustomMiddleware()); } }
Request Validator a.k.a IValidate
Interface \RequestHandler\Modules\Application\ApplicationRequest\IValidate
is used to tell application that given request handler also contains request input validation, if validation failed response with errors will be returned and request handler method won't get executed
Example:
#!php <?php namespace Controllers\User; use \RequestHandler\Modules\Application\ApplicationRequest\IHandle; use \RequestHandler\Modules\Application\ApplicationRequest\IValidate; class GetInfo implements IHandle, IValidate { public function handle(IRequest $request, IResponse $response): IResponse { $user = new User(); // Do some logic // Return response return $response->data(['user' => $user]); } public function validate(IInputValidator $validator): IInputValidator { return $validator->validate([ 'field_name' => 'rule|another_rule:param,another_param' ]); } }
Request Filter a.k.a IFilter
Interface \RequestHandler\Modules\Application\ApplicationRequest\IFilter
is used to tell application that we want some of request input data filtered when fetching them.
In following example we are telling script that when accessing user
and age
input data (query, form body etc) we want those values converted (filtered) into different types, we want user
to be matched with user id in database and retrieved as user model, and age
to be retrieved as integer.
Example:
#!php <?php namespace Controllers\User; use \RequestHandler\Modules\Application\ApplicationRequest\IHandle; use \RequestHandler\Modules\Application\ApplicationRequest\IFilter; use \App\Models\User; class GetInfo implements IHandle { public function handle(IRequest $request, IResponse $response): IResponse { // We are sure this value is instance of "User" model $user = $request->get('user'); // We are sure this value has type integer $age = $request->get('age'); return $response->data(['user' => $user]); } public function filter(IRequestFilter $filter): IRequestFilter { return $filter ->add('user', new ModelFilter(User::class, 'id')) ->add('age', new IntFilter()); } }
Database
Database module is used to hold active connection to database, as its meant to be used only with MySQL driver It also provide easy way to prepare, bind and execute mysql queries, it has specific methods for selecting, inserting/updating and deleting queries. Each of those methods uses same logic to execute query only difference is what they return.
Database::__construct(string, string, string, string[, int = 3306])
You probably won't be needing to create new instance of database manually, but in case that you need following parameters are required. host
, database
, username
, password
and there is port
which is optional.
Note: Application handles database preparation using data from config before booting anything else.
Example:
#!php <?php use \RequestHandler\Modules\Database\IDatabase; use \RequestHandler\Utils\ObjectFactory\ObjectFactory; $db = ObjectFactory::create(IDatabase::class, 'localhost', 'test_db', 'user', 'pass');
IDatabase::fetch(string, array): array
Fetch method is used to fetch single record from database and retrieve it as array. First parameter is string and it represents mysql query that needs to be executed, second parameter is array of bindings, binding are mapped one-on-one with query placeholders "?".
Example:
#!php <?php use \RequestHandler\Modules\Database\IDatabase; use \RequestHandler\Utils\ObjectFactory\ObjectFactory; // nothing provided to database constructor as we assume connection was established via framework $db = ObjectFactory::create(IDatabase::class); // $results would be similar to ['id' => ... 'name' => ..., 'age' => ...] $results = $db->fetch('SELECT * FROM `users` WHERE `users`.`first_name` = ? OR `users`.`last_name` = ?;', ['Name', 'Surname']);
IDatabase::fetchAll(string, array): array
This is same as fetch
, only difference is that expected result is array of arrays, which mean more records from database, e.g if query returns only one record, return value of method will be list array with single assoc array instead of only assoc array.
Example:
#!php <?php use \RequestHandler\Modules\Database\IDatabase; use \RequestHandler\Utils\ObjectFactory\ObjectFactory; // nothing provided to database constructor as we assume connection was established via framework $db = ObjectFactory::create(IDatabase::class); // $results would be similar to [['id' => ... 'name' => ..., 'age' => ...], ['id' => ... 'name' => ..., 'age' => ...], ...] $results = $db->fetch('SELECT * FROM `users` WHERE `users`.`age` >= ?;', [23]);
IDatabase::store(string, array): int
This method is used for saving queries (insert/update), after successful execution if primary value is available then lastInsertId
is returned, if there is no primary value rowCount
(number of affected rows) is returned.
Example:
#!php <?php use \RequestHandler\Modules\Database\IDatabase; use \RequestHandler\Utils\ObjectFactory\ObjectFactory; // nothing provided to database constructor as we assume connection was established via framework $db = ObjectFactory::create(IDatabase::class); // Returned id is e.g 3 $id = $db->store('INSERT INTO `users` SET `users`.`name` = ?, `users`.`age` = ?;', ['John', 25]); // As we don't have primary value, number of affected rows which is 1 is returned $affectedRows = $db->store('INSERT INTO `logs` SET `logs`.`text` = ?;', ['User created']);
IDatabase::delete(string, array): int
Works almost same as store
only difference is that return value is always number of affected rows (rowCount
).
Example:
#!php <?php use \RequestHandler\Modules\Database\IDatabase; use \RequestHandler\Utils\ObjectFactory\ObjectFactory; // nothing provided to database constructor as we assume connection was established via framework $db = ObjectFactory::create(IDatabase::class); // $deletedRecords holds number of removed items from database $deletedRecords = $db->delete('DELETE FROM `items` WHERE `item`.`type` = ?;', ['junk']);
Middlewares
Middleware Container
Middleware Container holds all required middlewares for current user request, and provide a way of executing next middleware and checking did all middlewares finished.
MiddlewareContainer::__construct(IRequest, IResponse)
As this is not actual implementation of middleware but rather holder of all required for current request, it depends on two objects on IRequest
and IResponse
, IRequest
is used so we can access request parameters (non-filtered nor validated) in middleware itself, and IResponse
is used if we don't want to fail middleware but simply to tell framework that response holds some errors e.g $response->errors(['logger' => 'Failed to log request']);
IMiddlewareContainer::add(IMiddlewareHandler): IMiddlewareContainer
Use this method to add middleware that needs to be executed before request handler is called.
Note:
Refer to
\RequestHandler\Modules\Application\ApplicationRequest\IMiddleware
to see it in action.
IMiddlewareContainer::next(): void
This method will execute next middleware in queue.
Note:
Refer to
IMiddlewareHandler
IMiddleware::finished(): bool
Check did all middlewares in current IMiddlewareContainer
were executed.
Note: Used by
Application
to determine should execution of request handler be proceeded or not.
Middleware Handler
\RequestHandler\Modules\Middleware\IMiddlewareHandler
interface is interface that needs to be implemented in some class in order to make it behave as middleware and be accepted by IMiddlewareContainer
Example:
#!php <?php use RequestHandler\Modules\Middleware\IMiddlewareContainer; use RequestHandler\Modules\Middleware\IMiddlewareHandler; use RequestHandler\Modules\Request\IRequest; use RequestHandler\Modules\Response\IResponse; class CustomMiddleware implements IMiddlewareHandler { public function handle(IRequest $request, IResponse $response, IMiddlewareContainer $middleware): void { // Do some logic here if ($everythingIsOk) { $middleware->next(); } // Can put some logic here as well } }
Exceptions
Instead of relying on built-in exceptions, this framework uses custom exceptions for each module and utility lib.
To allow easier and more readable exceptions we use BaseException
as our abstract exception.
Note:
Each framework exception has unique code, that is not required its just for easier debugging
BaseException
To achieve easier and readable exceptions handling, with combining both static and custom messages we use (extend) \RequestHandler\Modules\Exception\BaseException
class, it provide us with easy defining error messages for error codes, all it need its simple map.
Example:
#!php
<?php
// app/exceptions/CustomException.php
use \RequestHandler\Modules\Exception\BaseException;
class ModelException extends BaseException
{
const ERROR_INVALID_FIELD = 40001;
const ERROR_MISSING_PRIMARY = 40002;
protected $_errors = [
ModelException::ERROR_INVALID_FIELD => 'Field is not valid',
ModelException::ERROR_MISSING_PRIMARY => 'Primary key is required'
];
}
// app/modules/CustomModel.php
use \RequestHandler\Modules\Model\IModel;
class CustomModel implements IModel
{
private $_fields = ['created_at', 'last_update'];
public function validateField(string $fieldName): void
{
if (false === in_array($fieldName, $this->_fields)) {
// Imagine this method was called with parameter $fieldName equals to 'name' that is not defined in field list
// Error message of this exception would be something like
// Field is not valid: name
// It will also contain name of exception as well as error code
throw new ModelException(ModelException::ERROR_INVALID_FIELD, $fieldName);
}
}
}
#!php <?php // app/exceptions/CustomException.php use \RequestHandler\Modules\Exception\BaseException; class ModelException extends BaseException { const ERROR_INVALID_FIELD = 40001; const ERROR_MISSING_PRIMARY = 40002; protected $_errors = [ ModelException::ERROR_INVALID_FIELD => 'Field is not valid', ModelException::ERROR_MISSING_PRIMARY => 'Primary key is required' ]; } // app/modules/CustomModel.php use \RequestHandler\Modules\Model\IModel; class CustomModel implements IModel { private $_fields = ['created_at', 'last_update']; public function validateField(string $fieldName): void { if (false === in_array($fieldName, $this->_fields)) { // Imagine this method was called with parameter $fieldName equals to 'name' that is not defined in field list // Error message of this exception would be something like // Field is not valid: name // It will also contain name of exception as well as error code throw new ModelException(ModelException::ERROR_INVALID_FIELD, $fieldName); } } }
Request
Request::__construct()
Initialize request object.
Request::method(): string
Retrieve type of request method (e.g GET, POST)
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; /** @var IRequest $request */ $request = ObjectFactory::create(IRequest::class); var_dump($request->method()); // e.g string(3) "GET"
Request::query(string $key, $default = null, ?IDataFilter $dataFilter = null): mixed
Retrieves query (GET) parameter matching given key.
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; use RequestHandler\Utils\DataFilter\Filters\IntFilter; /** @var IRequest $request */ $request = ObjectFactory::create(IRequest::class); // url ...?firstName=John&lastName=Doe&phone=0641234567 var_dump($request->get('firstName')); // e.g string(4) "John" var_dump($request->get('lastName')); // e.g string(4) "Doe" var_dump($request->get('middlename', 'none')); // e.g string(4) "none" var_dump($request->get('phone', null, ObjectFactory::create(IntFilter::class)); // e.g int(641234567)
Note: If requested key is not defined value of "$default" will be filtered and returned If "$dataFilter" is present, retuned value of this method will be result value of data filter (transformer)
Request::parameter(string $key, $default = null, ?IDataFilter $dataFilter = null)
Retrieve specific parameter value from url
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; use RequestHandler\Utils\DataFilter\Filters\IntFilter; $request = ObjectFactory::create(IRequest::class); /** * route: /user/:id/profile * actual: /user/2/profile */ var_dump($request->parameter('id')); // e.g string(1) "2" var_dump($request->parameter('id', null, ObjectFactory::create(IntFilter::class))); // e.g int(2)
Note: If requested key is not defined value of "$default" will be filtered and returned If "$dataFilter" is present, retuned value of this method will be result value of data filter (transformer)
Request::data(string $key, $default = null, ?IDataFilter $dataFilter = null)
Retrieves body data (POST) parameter matching given key
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; use RequestHandler\Utils\DataFilter\Filters\IntFilter; $request = ObjectFactory::create(IRequest::class); /** * Form Data: * - name=Test * - gender=M */ var_dump($request->data('gender')); // e.g string(1) "M" var_dump($request->data('test', 'none'); // e.g string(4) "none"
Note: If requested key is not defined value of "$default" will be filtered and returned If "$dataFilter" is present, retuned value of this method will be result value of data filter (transformer)
Request::get(string $key, $default = null, ?IDataFilter $dataFilter = null)
Retrieves parameter from either body, query or parameter. It follows following order: * 1. POST * 2. If not in POST look GET * 3. If not in none of above look in URL Parameters
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; use RequestHandler\Utils\DataFilter\Filters\BoolFilter; $request = ObjectFactory::create(IRequest::class); /** * route: /user/:action * actual: /user/create?ignoreError=false * Form Data: * - name=John */ var_dump($request->get('name')); // e.g string(4) "John" var_dump($request->get('action')); // e.g string(6) "create" var_dump($request->get('ignoreError')); // e.g string(5) "false" var_dump($request->get('invalid', 'none')); // e.g string(4) "none" var_dump($request->get('ignoreError', null, ObjectFactory::create(BoolFilter::class)); // e.g bool(false)
Note: If requested key is not defined value of "$default" will be filtered and returned If "$dataFilter" is present, retuned value of this method will be result value of data filter (transformer)
Request::getData(): array
Retrieve all body (form) data from request
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; $request = ObjectFactory::create(IRequest::class); /** * Form Data: * - id=1 * - title=Test Title */ var_dump($request->getData()); // e.g array(2) ['id' => 1, 'title' => 'Test Title']
Request::getParameters(): array
Retrieve all url paraneters from request
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; $request = ObjectFactory::create(IRequest::class); /** * route: /user/:action/:subAction * actual: /user/create/social */ var_dump($request->getParameters()); // e.g array(2) ['action' => 'create', 'subAction' => 'social']
Request::getQuery(): array
Retrieve all query parameter from request
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; $request = ObjectFactory::create(IRequest::class); /** * /user/create?ignoreErrors=true&useFacebook=false */ var_dump($request->getQuery()); // e.g array(2) ['ignoreErrors' => true, 'useFacebook' => false]
Request::getAll(): array
Get all key => value pairs from any input POST, GET, URL Parameter In case that there are multiple keys with same name on different arrays (e.g "id" in both URL and Form Body) Following system of prioritizing is applied 1. POST 2. If not in POST look GET 3. If not in none of above look in URL Parameters
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; $request = ObjectFactory::create(IRequest::class); /** * /user/create/social?ignoreErrors=true&useFacebook=false * Form Data: * - name=Test */ var_dump($request->getAll()); // e.g array(2) ['action' => 'create', 'subAction' => 'social', 'ignoreErrors' => true, 'useFacebook' => false, 'name' => 'test']
Request::setFilter(IRequestFilter $filter): IRequest
Set request input filter used to transform values to desire type/value
#!php <?php use RequestHandler\Utils\ObjectFactory\ObjectFactory; use RequestHandler\Modules\Request\IRequest; use RequestHandler\Utils\DataFilter\Filters\IntFilter; use RequestHandler\Modules\Request\RequestFilter\IRequestFilter; $requestFilter = ObjectFactory::create(IRequestFilter::class); $request = ObjectFactory::create(IRequest::class); $request->setFilter($requestFilter); /** * route: /user/:id/profile * actual: /user/1/profile */ var_dump($request->get('id')); // e.g string(1) "1" $requestFilter->add('id', ObjectFactory::create(IntFilter::class)); var_dump($request->get('id')); // e.g int(1)
Updated