impressible/impressible-route

A routing framework for coding modern PHP in Wordpress Plugin.

v2.0-beta2 2022-08-27 01:02 UTC

This package is auto-updated.

Last update: 2024-10-27 06:02:46 UTC


README

CI badge-coverage Packagist

A routing framework for coding modern PHP in Wordpress Plugin.

Why?

For PHP developer who already adapted modern PHP appropaches (e.g. composer package management, PSR-4 autoloading with namespace, service container, etc), it's quite painful to work in the Wordpress environment.

Let's say you want to:

  1. write your Wordpress plugin with custom routing; and you
  2. want to structure your code into controller that works with psr/http-message request and response (for future flexibilities); and
  3. you want to lazy-load your controller with custom initialization logics or even PSR compliant service container.

Then this library is for you.

How to Use This?

When writing your plugin, add this hook implementation:

use Impressible\ImpressibleRoute\Http\Router;
use Impressible\ImpressibleRoute\LazyLoadObject;

require __DIR__ . '/vendor/autoload.php';

function my_plugin_register_routes() {
   /**
    * @var \wpdb $wpdb
    */
   global $wpdb;

   // Lazyload a MyController that, for demo purpose only, somehow need to use wpdb.
   $controller = new LazyLoadObject(fn() => new MyController($wpdb));

   // Create a router instance and register routes with it.
   $router = Router::fromEnvironment(
         'my_plugin_route', // query parameter used for routing.
         __DIR__            // folder for Wordpress template.
      )
      ->addRoute(new Route(
        'mycontent$',
        [$controller, 'handleContentIndex']
      ));
      ->addRoute(
        (new Route(
          'mycontent/mymedia/(\d+)$',
          [$controller, 'handleMediaEndpoint'],
          // Define query arguments supplied to the global \WP_Query
          // that will be passed to the controller method.
          [
            'post_id' => '$matches[1]',
            'post_type' => 'mymedia',
          ],
        ))->withPreGetPosts(function (\WP_Query $wpQuery) {
          // Show all mymedia to the author (after login)
          if (($userId = get_current_user_id()) != 0) {
            $author = get_user_by('slug', $query->get('author_name'));
            if ($userId === $author->ID) {
              // Get post of all status to the post author.
              $statuses = array_keys(get_post_statuses());
              $query->set('post_status', $statuses);
            }
          }
        })
      )
      // register the router methods to the Wordpress environment.
      ->register();
}
add_action('init', 'my_plugin_register_routes');

In your Controller, you have the flexibility to do things in old-style Wordpress way, or the PSR server request / response way:


use Impressible\ImpressibleRoute\Http\TemplatedResponse;
use Impressible\ImpressibleRoute\Http\NotFoundResponse;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Psr7\Response;

class MyController
{
   ...

   public function handleContentIndex(ServerRequestInterface $request)
   {
      return new TemplatedResponse('content-index.php');
   }

   public function handleMediaEndpoint(ServerRequestInterface $request)
   {
      /**
       * @var \WP_Query
       */
      $query = $request->getAttribute('wp_query');
      if (!$query->have_posts()) {
        return new NotFoundResponse();
      }
      $post = $query->next_post();
      return new Response(
         200,
         ['Content-Type' => $post->mymedia_content_type],
         fopen($post->mymedia_content, 'r')
      );
   }
}

Admin Interface Routing

We also support admin page routing in a similar manner with the "admin_menu" and "admin_init" hooks.

use Impressible\ImpressibleRoute\Http\AdminRouter;
use Impressible\ImpressibleRoute\Http\AdminRoute;
use Impressible\ImpressibleRoute\LazyLoadObject;

require __DIR__ . '/vendor/autoload.php';

function my_plugin_register_admin_routes() {
   /**
    * @var \wpdb $wpdb
    */
   global $wpdb;

   // Lazyload a MyController that, for demo purpose only, somehow need to use wpdb.
   $controller = new LazyLoadObject(fn() => new MyAdminController($wpdb));

   // Create a router instance and register routes with it.
   $router = new AdminRouter()
      ->addRoute(AdminRoute::menu(
        'My Admin Section',
        'My Section',
        'some-capability',
        'menu_slug_1',
        [$controller, 'handleAdminSection'],
        'icon-1',
        1 // position
      ))
      ->addRoute(AdminRoute::menu(
        'menu_slug_1',
        'My Admin Sub-section',
        'My Subection',
        'some-capability',
        'menu_slug_2',
        [$controller, 'handleAdminSubection'],
        1 // position
      ));
      // register the router methods to the Wordpress environment.
      ->register();
}
add_action('admin_menu', 'my_plugin_register_admin_routes');

In your MyAdminController, you have the flexibility:


use Impressible\ImpressibleRoute\Http\TemplatedResponse;
use Impressible\ImpressibleRoute\Http\NotFoundResponse;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use GuzzleHttp\Psr7\Response;

class MyAdminController
{
   ...
    public function handleAdminSection(ServerRequestInterface $request)
    {
        // For ordinary admin page responses.
        return new AdminPageResponse(function () use ($request) {
            // The code here will be delayed to execute the time
            // ordinarly Wordpress admin menu callback is run.
            require 'some/path/some/script.php';
        });
    }

    public function handleAdminSubection(ServerRequestInterface $request)
    {
        // For export or other pages without dashboard top bar and sidebar.
        // This will be executed when admin_init hook is run.
        return new Response(
            200,
            [
                'Content-Type' => 'application/json',
            ],
            json_encode([
                'status' => 'success',
                'msg' => 'Successful API call',
            ])
        );
    }

}

License

This library is licensed under the MIT License.