Slim3 Cheatsheet
As with all cheatsheets these are "never done" and continuously added to. If there is something useful you wish to contribute, please feel free to add it in the comments.
Related Posts
Install
Just execute the following command after having installed composer.
composer require slim/slim
Responses
They key thing to remember is that Response objects are immutable. You are never modifying the response object, but creating a new one which you will need to return.
Writing HTML Responses
$myHtml="<html><body><p>Hello world</p></body></html>";
$newResponse = $response->write($myHtml);
return $newResponse;
Writing Json Responses
I mostly use Slim to write small API microservices, so this is handy:
$data = array(
"message" => "something went wrong",
"someOtherThing" => "someValue",
);
$responseCode = 500; //internal server error
// make the response pretty - optional
$bodyJson = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$returnResponse = $response->withStatus($responseCode)
->withHeader("Content-Type", "application/json")
->write($bodyJson);
Redirect
$newResponse = $response->withRedirect('/new-url', 302);
return $newResponse;
Routing
Registering Routes:
Below is an example of registering a GET route of /
$app->get('/', function (\Slim\Http\Request $request, Slim\Http\Response $response, $args) {
$homeController = new HomeController($request, $response, $args);
return $homeController->index();
});
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 (\Slim\Http\Request $request, Slim\Http\Response $response, $args) {
$homeController = new HomeController($request, $response, $args);
return $homeController->hello($args['name']);
});
Thus if I went to /hello/world
then world
would get passed to my HomeController's hello method.
Likewise if I went to /hello/there
then there
would be passed to my HomeController's hello method.
Route Variable Regular Expressions
Lets say you're programming a RESTful API and want to be able to handle the following routes:
- /resource1/{id}
- /resource1/someMethod
This would not work because whenever someone went to /resource1/someMethod
then the first route would match and someMethod
would just be considered as an id for the first route.
What you need to do is use a regular expression on the first route. If you are using auto incrementing integers for your IDs then you would use something like:
$app = new \Slim\App();
$app->get('/users/{id:[0-9]+}', function (\Slim\Http\Request $request, \Slim\Http\Response $response, $args) {
$userController = new UserController($request, $response, $args);
return $userController->view($args['id']);
});
This would ensure that the first route would only match and be executed when a number was passed after /users. E.g. /user/1.
If you are using UUIDs instead of integers, then you can just use:
$app->get('/users/{id:[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}}', function (Request $request, Response $response, $args) {
$userController = new UserController($request, $response, $args);
return $userController->view($args['id']);
});
Route Group
The code example below shows you how you can use groups to easily manage your routes. E.g. create a group for /api, and then a subgroup for each resource.
Then you don't have to keep re-typing /api
or /resource1
etc and it makes renaming and managing your routes a lot easier.
$app->group('/api', function () use($app) {
$app->group('/resource1', function () use($app) {
// e.g. /api/resource1/{resource_id}
$app->get('/{resource_id}', function (\Slim\Http\Request $request, Slim\Http\Response $response, $args) {
$controller = new Resource1Controller($request, $response, $args);
return $controller->load($args['resource_id']);
});
});
$app->group('/resource2', function () use($app) {
// e.g. /api/resource2/{resource_id}
$app->get('/{resource_id}', function (\Slim\Http\Request $request, Slim\Http\Response $response, $args) {
$controller = new Resource2Controller($request, $response, $args);
return $controller->load($args['resource_id']);
});
});
});
Name Your Routes
Naming routes can come in useful, especially when writing middleware. Name your routes like so:
$app->get('/', function (Slim\Http\Request $request, \Slim\Http\Response $response, $args) {
// do something
})->setName('myRouteName');
Capture All Remaining Routes
If you wish to capture all remaining routes (rather than using a dedicated 404 handler), then you can add this to the end of your routes.
$app->get('/[{path:.*}]', function (\Slim\Http\Request $request, \Slim\Http\Response $response, $args) {
// do something
});
Middleware
Middleware is awesome! It's perfect for performing routine operations you need to perform on requests, such as check the user is logged in when accessing certain routes (e.g. not the login page), or logging every request. Create middleware for each type of check/operation you need to perform and stack as many as you want. Thus you don't need to create "god objects" or have massive bootstrap/init files that perform all checks and operations. This structure makes it really easy to add/remove and re-use/share logic. E.g. write your authentication middleware once and easily add it to all your other projects.
I really recommend reading How does middleware work?.
Create Middleware Inline
Below is an example of defining middleware inline (not using a class). This example redirects all routes that have a trailing / to a route without it.
Thus you can treat /home/
the same as /home
<?php
$app = new Slim\App();
// Define trailing slash middleware
$trailingSlashMiddleware = function (Slim\Http\Request $request, Slim\Http\Response $response, callable $next) {
$uri = $request->getUri();
$path = $uri->getPath();
if ($path != '/' && substr($path, -1) == '/')
{
// redirect paths with a trailing slash
// to their non-trailing counterpart
$uri = $uri->withPath(substr($path, 0, -1));
if ($request->getMethod() == 'GET')
{
return $response->withRedirect((string)$uri, 307);
}
else
{
return $next($request->withUri($uri), $response);
}
}
return $next($request, $response);
});
// Apply the middleware to every request.
$app->add($trailingSlashMiddleware);
Apply Middleware To Only Certain Routes
The previous example applied the middleware globally, but if you only want it to execute when certain routes are matched, do it like so:
$myMiddleware = function (Slim\Http\Request $request, Slim\Http\Response $response, callable $next) {
// do something.
};
$app->get('/', function (\Slim\Http\Request $request, Slim\Http\Response $response, $args) {
$allGetVars = $request->getQueryParams();
$myGetVariableValue = $allGetVars['myGetVariableName'];
})->add($myMiddleware);
Alternatively, apply the middleware globally, but name your routes and have the middleware logic itself check the route name and decide whether to apply itself or not. For example, my middleware tutorial for checking if users are logged in has a list of "public routes" that it knows not run authentication against.
Middleware Class
To prevent your codebase getting extremely messy, it's probably worth putting your middleware into classes under a middleware
folder.
Here is an example middleware class:
class MyAwesomeMiddleware
{
private $m_someMemberVariable;
/**
* Create our middleware. You don't have to have a constructor.
* @param $someValue - some value to configure our middleware
*/
public function __construct($someValue="defaultValue")
{
$this->m_someMemberVariable = $someValue;
}
/**
* This is the important bit when creating middleware and what gets invoked (hence the name)
*/
public function __invoke(Slim\Http\Request $request, \Slim\Http\Response $response, $next)
{
$route = $request->getAttribute('route');
if (!empty($route))
{
$routeName = $route->getName();
}
if ($this->doSomeCheck($routeName))
{
$returnResponse = $next($request, $response);
}
else
{
// pretend something went wrong, causing the check to fail
// and return a JSON error message
$data = array(
"message" => "something went wrong",
);
$responseCode = 500; //internal server error
$bodyJson = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$returnResponse = $response->withStatus($responseCode)
->withHeader("Content-Type", "application/json")
->write($bodyJson);
}
return $returnResponse;
}
}
Then you would add it just like inline middleware. E.g.
$app->add(new MyAwesomeMiddleware());
Misc
Display Errors In Dev
To display error details, simply set displayErrorDetails
to true in the settings passed to the App, like shown below:
$slimSettings = array();
if (ENVIRONMENT === 'dev')
{
$slimSettings['displayErrorDetails'] = true;
}
$slimConfig = array('settings' => $slimSettings);
$app = new Slim\App($slimConfig);
Get $_GET variables:
The code below shows how to access your $_GET variables.
$app->get('/', function (\Slim\Http\Request $request, Slim\Http\Response $response, $args) {
$allGetVars = $request->getQueryParams();
$myGetVariableValue = $allGetVars['myGetVariableName'];
});
Get $_POST and $_PUT Variables
$app->get('/', function (\Slim\Http\Request $request, Slim\Http\Response $response, $args) {
$allPostPutVars = $request->getParsedBody();
$myVariableValue = $allPostPutVars['myVariableName'];
});
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_
.
abstract class AbstractSlimController
{
protected $m_request;
protected $m_response;
protected $m_args;
public function __construct(\Slim\Http\Request $request, Slim\Http\Response $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 function registerRoutes($app);
}
This goes along with the fact that when I register routes, it's like so:
$app->get('/', function (\Slim\Http\Request $request, Slim\Http\Response $response, $args) {
$controller = new MyController($request, $response, $args);
return $controller->someControllerMethodToHandleRequest();
});
For larger codebases, I actually register the routes in the controllers, rather than registering them all in the index.php
file.
Getting Help
- Slim3 Docs
- Slim Framework Forum
- Stack Overflow with the slim-3 tag
References
- Slim3 Docs
- Stack Overflow - Slim 3 - how to get all get/ put/ post variables?
- Slim3 Forums - Route Regexp for UUID
- Github - slimphp/Slim - slim 3 route wildcard #1700
First published: 16th August 2018