Skip to content

htmx and Routing

Overview

HTMX Docs Slim Docs Origins utilizes Slim as a PHP Routing Framework and HTMX for making network requests.

We also utilize PHP DI for dependency injection. PHP DI Slim Bridge

In combination the two allow for a powerful and flexible way to build web applications.

Getting Started

The following documentation is out of date with our current setup.

The initial entrypoint for our endpoints that htmx uses begins in bootstrap/app.php.

/* --------------------------- Load Custom Routes --------------------------- */
if (strpos($_SERVER['REQUEST_URI'], '/og-ajax/') !== false) {
    $request_uri = str_replace('/og-ajax/', '', $_SERVER['REQUEST_URI']);
    $request_uri = explode('?', $request_uri)[0];
    if (file_exists(Config::get('theme.path') . '/bootstrap/routes.php') && $request_uri !== '') {
        require_once Config::get('theme.path') . '/bootstrap/routes.php';
    }
}

If the request URI contains /og-ajax/ then we load the custom routes. This allows us to keep our routes separate from the main application.

Routing

Located in bootstrap/routes.php we have the following as a starter:

<?php

use DI\Bridge\Slim\Bridge;
use Middlewares\TrailingSlash;
use Origins\Controllers\BlogController;
use Slim\Routing\RouteCollectorProxy;
use Og\Middleware\NonceMiddleware;
use Og\Middleware\RefererMiddleware;
use Origins\Controllers\SearchController;

$app = Bridge::create($container);

/*
* The middleware is present to remove trailing slashes
* from the URI,handle nonces, and referers.
*/
$app->add(new TrailingSlash(false));
$app->add(new NonceMiddleware());
$app->add(new RefererMiddleware());


$app->group('/og-ajax', function (RouteCollectorProxy $group) {
    // This route will be available at /og-ajax/blogs
    // It will call the get_blogs method on the BlogController
    $group->get('/blogs', [BlogController::class, 'get_blogs']);
    $group->get('/search', [SearchController::class, 'get_search_results']);
});

$app->run();

die();

To create a route it is recommended to add it within the /og-ajax group. This allows for a clear separation of routes that are used for htmx requests.

In addition a controller is recommended. These should be placed in the /app/Controllers directory.

The following is the BlogController for an example:

<?php

namespace Origins\Controllers;

use Timber\Timber;
use Origins\Providers\Repositories\BlogPosts;

class BlogController
{
    // Placing the BlogPosts repository in the constructor allows for dependency injection
    public function __construct(private BlogPosts $blogPosts)
    {
    }

    public function get_blogs($request, $response)
    {
        $paged = $request->getQueryParams()['page'] ?? 0;
        $post_count = 5;
        $blog_posts = $this->blogPosts->get_blogs($paged, $post_count);
        $context = Timber::context();
        $context['blog_posts'] = $blog_posts;

        ob_start();
        Timber::render('blocks/Sections/BlogDisplayGrid/blog-grid.twig', $context);
        $html = ob_get_clean();
        $response->getBody()->write($html);
        if($blog_posts->pagination()->current == $blog_posts->pagination()->total)
        {
            $response = $response->withHeader('HX-Trigger', 'out-of-blogs');
        }
        return $response;
    }

}

The controller method that is used on the route will have three arguments passed to it.

$request

This is the request object that is passed to the route.

$response

This is the response object that is passed to the route.

$args

This is an array of arguments that are passed to the route.

The controller method should return a response object. This is what is sent back to the client.