alpipego / awp-router
WP Router Abstraction
Requires
- php: ^7.2
README
Inspired by Themosis Routing which in turns uses Laravel's Routing this WP Router abstraction adds support for adding custom routes and callbacks on WordPress' routes while using WordPress functions (add_rewrite_rule
, add_rewrite_tag
) and without the need of adding third-party routers.
Developed in collaboration with @tatundkraft.
Installation
Install this package via composer:
php composer.phar require alpipego/awp-router
Initialise the dispatcher:
$dispatcher = new \Alpipego\AWP\Router\Dispatcher();
The dispatcher takes a CustomRouterInterface
, a TemplateRouterInterface
, and a AjaxRouterInterface
as optional arguments, if none are provided, the default Routers are initialised.
Custom Routes
$dispatcher->custom->get('/my-custom-route', function(WP_Query $query) { // do something with your query });
The previous example adds a custom route my-custom-route
that only responds to GET
(and HEAD
) request, and passes back a WP_Query
object to your callback.
Methods
At the moment the methods GET
, POST
, and HEAD
are implemented; custom routes can be added via the following methods:
head
(matchesHEAD
requests):head(string $route, callable $callable)
get
(matchesGET
andHEAD
requests):get(string $route, callable $callable)
post
(matchesPOST
requests):post(string $route, callable $callable)
any
(matchesGET
,POST
, andHEAD
requests):any(string $route, callable $callable)
match
methods get passed as first argument:match(array $methods, string $route, callable $callable)
and match
which takes possible methods as a first parameter:
// match(array $methods, string $route, callable $callable) $dispatcher->custom->match(['GET', 'POST'], '/true-get', function(WP_Query $query) { // ... });
Variables
Variables can be either matched and thus be used as query_vars
or be non-matching and only act as a way of validating the route.
Variables are added in the following form {VARNAME:REGEX}
and you can leave out either VARNAME
or the REGEX
.
In the following example form_id
and input_name
are passed back as query_vars
in the $query
object. The last regex simply matches any three characters, e.g. abc
but not abcd
(and does not add a query var):
$dispatcher->custom->post('/forms/{form_id:\d+}/input/{input_name}/{[a-z]{3}}', function(WP_Query $query) { // ... });
Notes:
- These variables are "private" and can therefore not be used in a URL query string; cf. Public vs. Private query vars
Rewrite Tags
Already registered query vars (either built-in public vars or registered through plugins et al.) can be used in the route, and are registered with leading and trailing %
, i.e. {%QUERYVAR%:REGEX}
:
$dispatcher->custom->post('/pages/{%page_id%}/fields/{%field_id%:\d+}', function(WP_Query $query) { // ... });
This will match the built-in page_id
query var and add a custom field_id
query variable to WP_Query
.
Notes:
- All rewrite tags are "public query vars" and can be used in a URL query string; cf. Public vs. Private query vars
- Be careful when adding a regular expression to an already defined rewrite tag, as this will override the previous regex.
Redirects
The RouterInterface
has a method to add redirects:
// redirect(string $route, string $target, array $methods = ['GET', 'HEAD'], int $status = 308) $dispatcher->custom->redirect('/twitter/{twitter_user}', 'https://twitter.com/{twitter_user}');
This route will redirect https://YOURSITE.com/twitter/alpipego
to https://twitter.com/alpipego
Of course, redirects can also have regular expressions and rewrite tags in the route.
$dispatcher->custom->redirect('/attachment/{%attachment_id%}', 'https://external-attachment-handler.com/{attachment_id}');
Notes:
- By default only
GET
andHEAD
requests get redirected - The default redirect code is
308
making sure the HTTP method gets redirected properly
Callbacks
You can pass any callable
as a callback.
The callback will receive the current WP_Query
object as the first parameter, and the following return types can be handled:
- If the callback returns
false
propagation is stopped, and the request ends (i.e., no template gets rendered). - The callback may return the full path to a valid template file (basically any PHP file), which then gets required.
- If the callback does not have a return value, WordPress' default template loading and hierarchy kicks in.
Template Routing
Conditionals
The condition
method takes a callable that returns a boolean value as its first argument and a callback to be executed right before rendering the template if the condition is true as its second argument. Both callbacks receive the current WP_Query
to act on, the callable also receives the full path to the template file. See the section on callbacks on how they are handled.
// public function condition(callable $condition, callable $callable); $dispatcher->template->condition(function(WP_Query $query) { // if this is true return $query->is_page; }, function(WP_Query $query, string $template) { // then execute this return $template; });
Post Type Templates
Register post type templates without the constraints the WordPress implementation has. The template
method takes a template file path as the first argument—relative to the current (child) theme, the template name (for the wp-admin sidebar select box) as the second, the post types for which this template should be available as the third and a callback as the final argument. See the section on callbacks on how they are handled. In addition to the current WP_Query
object, the callback function receives the full path to the template as the second argument:
// public function template(string $template, string $name, array $postTypes, callable $callable); $dispatcher->template->template('my-page-template.php', __('Page Template Name', 'textdomain'), ['page', 'post'], function(WP_Query $query, string $template) { // ... });
Ajax Routing
AJAX requests can be either GET
or POST
requests (or both), and apply to logged-in users only or all users.
You can define custom routes for AJAX requests in the following format:
// public function get(string $route, callable $callback, bool $private = false); $dispatcher->ajax->get('/ajax/user/{user_id:\d+}', function () { $userId = (int)$_GET['query']['user_id']; $user = new WP_User($userId); if ($user->ID === 0) { wp_send_json_error(sprintf('User #%d does not exist', $userId)); } wp_send_json_success($user); }, true);
Notes:
- Compared to the other routers, the callback does not receive a
WP_Query
object; the query vars are included in the$_REQUEST
super global - In contrast to WordPress' AJAX actions, requests that are not marked as private apply to both logged-in and not logged-in users (
priv
vs.nopriv
).