Lose yourself in the PHP Renaissance.

Hello {you} with Slim Framework 3.x

Last week I wrote my Slim 3.x "Hello World" tutorial after hearing Josh Lockhart introduce the framework at our UpstatePHP meet-up. I was eager to learn the basics of using Slim as an HTTP router, and since then I've continued to learn new things (yay!), like using named routes / arguments and its dependency injection container (based on SensioLabs's oddly named Pimple).

Let's break it down here...

Named Routes

Defining a route is pretty straightforward. Slim's App class gives us methods to add routes named after their correlating HTTP methods (e.g. get(), post(), etc.). To give a route a name, you simply append a call to setName() to its definition:

<?php
$app
->get('/hello', function(Slim\Http\Request $request, Slim\Http\Response $response, array $args) {
  return
$response->write('Hello, world!');
})->
setName('hello-world');
?>

Why would you want to do this? There may be times in your application where you want to redirect from one route to another or create a link to another route. Instead of hardcoding route paths into your code, use Router::pathFor() to let Slim generate the path for you:

<?php
$app
->get('/some/other/route', function($request, $response, $args) {
 
$path = $this->getContainer()->get('router')->pathFor('hello-world');
 
// ...
});
?>

In Slim 2.x and prior, this was named urlFor(). Yours truly played semantic stickler to propose renaming it in a pull request that was recently merged by Rob Allen, a.k.a @akrabat, who offers a great intro to Slim 3.x in his Slim 3 Primer and numerous blog posts.

Dynamic Routes with Named Arguments

In addition to static routes like the ones defined above, Slim depends on FastRoute to support dynamic routes that use named arguments in their path patterns. Curly braces identify named arguments in a route's pattern, and regular expressions further determine what request paths match a route:

<?php
// Add a route for a personal greeting using an argument.
$app->get('/hello/{name:[A-Za-z]+}', function(Slim\Http\Request $request, Slim\Http\Response $response, array $args) {
  return
$response->write('Hello, ' . $args['name'] . '!');
})->
setName('hello-name');
?>

To create a path for this route, I would use the same function from above but pass it an array including the named argument:

<?php
// From inside a route closure...
$path = $this->getContainer()->get('router')->pathFor('hello-name', ['name' => 'Shady']);
?>

The regular expression in the pattern will only permit a match to this route if the request path includes a second argument containing only upper or lower case letters. Throw a number, a space, or punctuation in there, and you'll get a 404. Granted, if I were actually writing an application, I would use a template engine like Twig to automatically escape variables when generating output.

Slim's Dependency Injection Container

Ahh, what a fine segue that was!

To use Twig in a Slim application, you might make use of Slim's dependency injection container (or DIC). I'm still relatively new to the concept, so I won't try to explain the design pattern in depth.

At a high level, the DIC is an object that your application uses to find other objects that encapsulate discrete bits of functionality. Pimple refers to these objects as services, and they may be responsible for communicating with the database, sending mail, or rendering output through a template engine.

When you create a new Slim App object, you pass in the container object like so:

<?php
// Prepare the Pimple dependency injection container.
$container = new \Slim\Container();

// Add a Twig service to the container.
$container['twig'] = function($container) {
 
$loader = new Twig_Loader_Filesystem('templates');
  return new
Twig_Environment($loader, array('cache'));
};

// Create the Slim application using our container.
$app = new \Slim\App($container);
?>

Now any route can make use of the Twig service to build output via a template:

<?php
$app
->get('/hello', function(Slim\Http\Request $request, Slim\Http\Response $response) {
 
// Load the template through the Twig service in the DIC.
 
$template = $this->getContainer()->get('twig')->loadTemplate('index.html');
 
// Render the template using a simple content variable.
 
return $response->write($template->render(['content' => 'Hello, world!']));
})->
setName('hello-world');
?>

The Slim DIC also contains an array of settings that you can append values to when you construct it by passing in an associative array of settings:

<?php
// Prepare the Pimple dependency injection container.
$container = new \Slim\Container([
 
'site_name' => 'Slim Shady',
]);
?>

These settings are appended to a default settings array and may be used elsewhere in the code. Slim binds route closures (the function we defined in above examples as the second argument to the $app->get() calls) to the $app object, so a quick reference to the site_name setting would look like:

<?php
$app
->get('/hello/{name:[A-Za-z]+}', function(Slim\Http\Request $request, Slim\Http\Response $response, array $args) {
 
// Load the template through the Twig service in the DIC.
 
$template = $this->getContainer()->get('twig')->loadTemplate('index.html');
 
// Render the template using content and site name variables.
 
return $response->write($template->render([
   
'content' => 'Hello, ' . $args['name'] . '!',
   
'site_name' => $this->settings['site_name'],
  ]));
})->
setName('hello-name');
?>

You can learn more about the DIC in the Slim 3.x dependency injection documentation or Rob Allen's comprehensive blog post on accessing services in Slim 3.x.

A final word...

I'm used to using the Devel module for debugging when writing Drupal modules. I was a bit lost writing PHP outside of Drupal at first, but then I remembered that Devel for Drupal 8 uses the Kint library to provide fantastic variable / backtrace debugging support.

To use it in your Slim application, add it to your composer.json file and the autoloader will allow you to make automatic use of it. The composer.json for my little learning application is currently:

{
    "require": {
        "slim/slim": "dev-develop",
        "raveren/kint": "^1.0",
        "twig/twig": "~1.0"
    }
}

Adding a simple line like the following to a route closure will produce some easily navigable debug output to the page:

<?php
d
($this->settings);
?>

Seriously... it's magic. There's never been a better time to lose yourself in the PHP Renaissance.

Topics: 

Comments

Hey, I'm so glad people like Kint - it's my labor of love Smile

And even though I came here via analytics traceback, I was actually researching for a smart and no learning curve framework with routing as a first-class citizen, I'm going to try Slim now, thank you!!

hah, awesome. Thanks for the comment! Definitely appreciate Kint, especially coming from the school first of print_r() and then krumo. Wink