Slim 3 Controllers and Actions

In this article we will take a look at how we can use Slim to create an MVC pattern. Later we will learn how to modify our Controller and create different Actions.

Let's get started.

The setup

Setting up your folder/class structure can be a little bit combersome for junior programmers. There are a few different concepts one must have a firm grasp on in order to fully benefit from all Slim provides.

  1. Namespacing
  2. Dependency Injection Containers
  3. Slim's Callable Resolver

Namespacing

For those familar with Composer and programming in general, namespacing is a programming practice that isolates classes in logical containers. With composer you can turn a namespace directly into a path to a specific class file. Composer makes this magic happen, lets take a look at an example.

Example (composer.json) segment:

"autoload" : {
    "psr-4" : {
         "Foo\\Bar\\" : "src"
    }
}

This is selling to prefix the Foo\Bar namespace to any folders under the "src" directory.If we further had a folder ... "src/Controllers", the namespace to those files would be "Foo\Bar\Controllers".

Fully understanding this concept is key when we start talking about Dependency Injection Controllers.

DiC (Dependency Injection Controllers) [PIMPLE]

Slim has a default DiC which is Pimple. It comes with all installations and is enabled by default. A DiC hold the configuration for every class your application has. The DiC's power comes from the ability to only initialize classes that get called [lazy loading]. That means if your application only executes 1 controller, then only that controller and its dependencies get created. Proper utilization of the DiC makes Slim applications super fast and responsive.

The way pimple and other DiC's do this is through factory methods. Let's look at an example.

//Pimple Factory
$container = $app->getContainer();
$container[MyController::class] = function ($c) { 
    return new MyController();
};

This is defining a key in the container for a controller class called MyController. For those of you who have not seen the ::class used before, it returns the Fully Qualified Class Name which includes the namespace. Going back to my previous example, if MyController was in Foo\Bar\Controllers... MyController::class would be "Foo\Bar\Controllers\MyController"

The reason we do this is to save a bit of typing, and should you ever move the class, you only have to update the import statement [Ie use ... ]. Most IDE's do this automagically and you can be super happy about that!

The method above will only ever return 1 instance of MyController meaning subsequent calls to the container key holding it will always return the same instance thus keeping your overhead low because you aren't wasting copies of things.

Slim is aware of all classes in your project if you have setup proper namespacing, and it can use your classes through a special Callable Resolver class.

Callable Resolver

Slim has an awesome little internal trait called Callable Resolver. It is responsible for finding your classes in the container, OR creating it. I will touch on the latter in a bit as it needs a more lengthy explanation. The callable resolver is invoked by you simply trying to be a normal programmer. Let's take a look at some code.

$app->get('/mypage', MyController::class . ":myPage");

You might have seen something like this in the wild. This line of code does a heck of a lot of things for you. There are 2 scenarios that will play out.

  1. MyController is defined in the container
  2. It is not defined in the container

If it is defined in the container Slim will simply grab the instance of it and try to access the myPage method, ":myPage". Assuming myPage has the right method signature it gets executed and it's that simple!.

If it is not defined in the container, Slim will first try to construct it. Slim does this by calling the constructor method of the class and will pass by default the entire Container Interface into your class. I cannot emphasize this enough. Slim will try to use your class, but please create a container key for your classes. Passing around the entire container can be quite costly and it could bog you down later.

After Slim has the controller it follows the same execution page as before.

Actions

You might have heard of Action Classes. An action class is a special Controller. Actions only do 1 thing and are invokable. They only really contain 1 public method and are the ultimate king in Single Responsibility Design. Let's take a look at the scaffold for an action.

namespace Foo\Bar\Actions;

abstract class Action
{
    protected $ci;

    public function __construct(ContainerInterface $ci) {
        $this->ci = $ci;
    }
    
    abstract public function __invoke(Request $request, Response $response, $args = []);
}

This particular Action class is setup for the 2nd option of creating a class. This abstract class represents the simple inards of any action class. Let's say we wanted to use this class in displaying something, we could do something like this.

namespace Foo\Bar\Actions;

class HelloWorldAction extends Action
{

   public function __invoke(Request $request, Response $response, $args = []) {
       return $response->write("Hello World");
   }
}

The above code shows the actual Action class. Now all we have to do is tell Slim what to do with it.

$app->get("/hello", HelloWorldAction::class);

You might be scratching your head..."Where's the __invoke method". This is one of Slim's awesome features, it doesn't need one because Slim can execute the class as a function. It's a more advanced PHP Concept but that's how Invokable classes work!

I hope this helps you out, if you like this article ping @ me on Twitter.

o7 Happy Coding

Written by Glenn Eggleton on Thursday January 28, 2016
Permalink - Chapter: Slim