Slim 4 Cheatsheet
Table Of Contents
Related Posts
Getting Started
Install the framework with:
composer require slim/slim 4.*
composer require slim/psr7
Then get started by putting the following in ./public/index.php
<?php
require_once(__DIR__ . '/../vendor/autoload.php');
$app = Slim\Factory\AppFactory::create();
$app->addErrorMiddleware($displayErrorDetails=true, $logErrors=true, $logErrorDetails=true);
$app->get('/', function (Psr\Http\Message\ServerRequestInterface $request, Psr\Http\Message\ResponseInterface $response) {
$body = $response->getBody();
$body->write('Hello world'); // returns number of bytes written
$newResponse = $response->withBody($body);
return $newResponse;
});
$app->run();
run with:
php -S localhost ./public/index.php
Routing
Route Variables
Having a route like /hello?name=john
is pretty ugly.
It would be much nicer to have a route like /hello/john
.
You can use something like below to allow a variable name in the route and pass it to a function
$app->get('/hello/{name}', function (Psr\Http\Message\ServerRequestInterface $request, Psr\Http\Message\ResponseInterface $response, array $args) {
$name = $args['name'];
$body = $response->getBody();
$body->write('Hello world'); // returns number of bytes written
$newResponse = $response->withBody($body);
return $newResponse;
});
Body Parameters
To get the body parameters, you need to use:
$request->getParsedBody()
... however, for that to work, you need to ensure the body parsing middleware is run by adding:
$app->addBodyParsingMiddleware();
Returning JSON Responses
$responseData = array('hello' => 'world');
$responseBody = json_encode($responseData);
$response = new \Slim\Psr7\Response($status=200);
$response->getBody()->write($responseBody);
$response = $response->withHeader('Content-Type', 'application/json');
return $response;
Returning A Redirect
This is easier with just providing you a function:
/**
* Create a redirect response;
* @param string $newLocation - where you wish to redirect the user.
* @param Slim\Psr7\Response $response - the existing response to work with
* @param bool $useTemporaryRedirect - whether this redirect is temporary, or browser should cache it.
* @return Slim\Psr7\Response - the redirect response.
*/
function redirect(
string $newLocation,
bool $useTemporaryRedirect=true
) : Slim\Psr7\Response
{
$httpCode = ($useTemporaryRedirect) ? 302 : 301;
$response = new \Slim\Psr7\Response($httpCode);
$response = $response->withHeader('Location', $newLocation);
return $response;
}
Abstract Slim Controller
Whenever I create a new Slim project, I always like creating this class right off the bat which all my "concrete" controllers extend. This way I access the request, response, and args with $this->m_.
<?php
abstract class AbstractSlimController
{
protected Psr\Http\Message\RequestInterface $m_request;
protected \Psr\Http\Message\ResponseInterface $m_response;
protected $m_args;
public function __construct(\Psr\Http\Message\RequestInterface $request, \Psr\Http\Message\ResponseInterface $response, $args) {
$this->m_request = $request;
$this->m_response = $response;
$this->m_args = $args;
}
// this one is optional - refer to Slim3 - Simplifying Routing At Scale
// https://blog.programster.org/slim3-simplifying-routing-at-scale
abstract public static function registerRoutes($app);
}
Middleware
Middleware changed significantly between Slim 3 and 4. This is because Slim 4 makes use of PSR-15 for middleware.
This means that if you write middleware for Slim, you can use it anywhere else that makes use of PSR-15 as well.
The key difference is that instead of the body of your middleware being inside __invoke
, it is now within a public method called process
,
which only has parameters for the request and the handler (there is no parameter for response anymore).
Example
Thus, if you wanted some authentication middleware, that checks if the user is logged in as an admin if they go to anywhere under /admin, then it would look something like this:
<?php
use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Server\RequestHandlerInterface;
use \Psr\Http\Message\ResponseInterface;
class MiddlewareAdminAuth implements \Psr\Http\Server\MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$uri = $_SERVER['REQUEST_URI'];
if (str_starts_with($uri, "/admin/") && AuthLib::isLoggedInAsAdmin() === false)
{
$response = new \Slim\Psr7\Response(302);
$response = $response->withHeader('Location', '/login');
}
else
{
$response = $handler->handle($request);
}
return $response;
}
}
You would apply the middleware to the entire app (all routes) like so:
$app = Slim\Factory\AppFactory::create();
$app->add(new MiddlewareHttpBasicAuth());
Alternatively, if you want to apply it to individual routes, you can do so:
$app->get('/', function (Request $request, Response $response, $args) {
$response->getBody()->write('Hello ');
return $response;
})->add(new MyMiddlewareClass());
Also, you can apply it to a group of routes:
$app->group('/utils', function(\Slim\Routing\RouteCollectorProxy $group) {
$group->get('/date', function (Request $request, Response $response) {
// stuff here
});
$group->get('/time', function (Request $request, Response $response) {
// stuff here
});
})->add(new MyMiddlewareClass());
Middleware Order And Routing
It is worth noting that middleware in Slim 4 operates in a last-in-first-out (LIFO) manner (reference), which might matter for you. For example, one can add the following line to have the routing evaluated (more info).
$app->addRoutingMiddleware();
This is the Slim4 replacement to the Slim 3 way of having to provide the determineRouteBeforeAppMiddleware
setting when creating the app, as shown below:
$app = new Slim\App([
'settings' => [
'displayErrorDetails' = $displayErrorDetails,
'determineRouteBeforeAppMiddleware' => true
]
]);
Having the routing evaluated means that you can find out the name of the route that was hit like so:
class MyMiddlewareClass implements \Psr\Http\Server\MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$routeContext = \Slim\Routing\RouteContext::fromRequest($request);
$route = $routeContext->getRoute();
if (empty($route)) { throw new HttpNotFoundException($request); }
$routeName = $route->getName();
# do things...
}
}
Thus, if one wishes to make use of the route details in Middleware, one needs to make sure to add the middleware before adding the $app->addRoutingMiddleware();
line. E.g.
$app = Slim\Factory\AppFactory::create();
// register our global middleware that uses route names
$app->addMiddleware(new MyMiddlewareClass());
// This must come *after* middleware that uses routing
$app->addRoutingMiddleware();
MyController::registerRoutes($app);
$app->run();
Add 404 Page
In order to add a 404 page, one needs to tell the error handler how to handle the HttpNotFoundException like so:
$app = Slim\Factory\AppFactory::create();
// register any custom middleware....
$app->addMiddleware(new SomeCustomMiddleware());
$app->addMiddleware(new AnotherCustomMiddleware());
// register the error middleware. This must be registered last so that it gets executed first.
$errorMiddleware = $app->addErrorMiddleware(
$displayErrorDetails = (ENVIRONMENT === "dev"),
$logErrors=true,
$logErrorDetails=true
);
// Create your 404 handler...
$errorMiddleware->setErrorHandler(\Slim\Exception\HttpNotFoundException::class, function (
\Psr\Http\Message\ServerRequestInterface $request,
\Throwable $exception,
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails
) {
$response = new \Slim\Psr7\Response();
$body = new View404();
$view = new ViewCustom404Page();
$response->getBody()->write((string)$view);
return $response->withStatus(404);
});
// register routes/controllers here....
$app->run();
References
- Github - Slim 4 Readme
- Slim 4 Website
- Stack Overflow - Slim 3 getParsedBody() always null and empty
- Slim Freamework - Invoke 404 Not Found Handler
First published: 15th August 2020