It seems like you are using an ad blocker. To enhance your experience and support our website, please consider:

  1. Signing in to disable ads on our site.
  2. Sign up if you don't have an account yet.
  3. Or, disable your ad blocker by doing this:
    • Click on the ad blocker icon in your browser's toolbar.
    • Select "Pause on this site" or a similar option for lancecourse.com.

Workouts with Slim 3 - part 4: DIC and Views

By zooboole

I am glad to meet you here again. In the third part I told you I will be talking of Slim's Dependency Injection Container(DIC or simply DI) and to illustrate/apply that I will be using slim-views package which could allow us to have nice HTML documents instead of simple HTTP Response;

If you didn't start this series with us, here is the break down of previous parts:

What's Dependency Injection Container?

Before I dive into that let me first show you with a simple example what Dependency Injection itself is. Say you have a class users as following:

<?php

class Users{

    //...
    private $users;

    public function fetchUsers( )
    {
        $this->users = $databaseHandler->prepare(' SELECT * FROM users ');
        //...
    }

    //...

}

Image 1- Sample Users class

There is nothing really special with this sample class. It's supposed, with the help of the method fetchUsers() get the list of users from the table users. Right here the class will be in need of the database connection for it to be able to access the database records, which is the variable $databaseHandler used to prepare the SQL request.

You may tell me all we have to do is to set up the database connection inside the same class, maybe in the constructor like this:

<?php

class Users{

    //...
    private $users;
    protected $databaseHandler = null;

    // DB settings
    $private $host = 'host';
    $private $dbname = 'dbase';
    $private $dbpwd = 'pwd';
    //....

    public function __construct()
    {

        try{

            //We set the $databaseHandler variable
            $this->databaseHandler = new PDO(....);

        }catch($e){
            $e->getMessage();
        }

    }

    public function fetchUsers( )
    {
        $this->users = $databaseHandler->prepare(' SELECT * FROM users ');
        //...
    }

    //...

}

Illustrate tied goal or dog with necklaces

Image 2- illustration of tied classes. Image by pixabay

Yes. This one too works. But that will definitely bind our database connection to the class. Whenever you would like to share this code with another developer

  • He would have to use PDO
  • He has to alter this class before using it
  • If the database connection hangs, the whole class would do same

This solution causes some few problems and doesn't really look clean. The class depends a lot of the database: which is called a dependency.

With that you may also be thinking of having a different class database which the users class can extend like this:

<?php

class Database{

    protected $databaseHandler = null;

    // DB settings
    $private $host = 'host';
    $private $dbname = 'dbase';
    $private $dbpwd = 'pwd';
    //....

    public function __construct()
    {

        try{

            //We set the $databaseHandler variable
            $this->databaseHandler = new PDO(....);

        }catch($e){
            $e->getMessage();
        }

    }

}

Image 3- Sample database class

Then we create the users class which extends the database class in order to have access to the database instance:

<?php

class Users extends Database{

    //...
    private $users;

    public function fetchUsers( )
    {

        // can use $databaseHandler which comes from the parent class
        $this->users = $databaseHandler->prepare(' SELECT * FROM users ');

        //...
    }

    //...

}

Image 4- Users class extending a database class

Intersection of two things

Image 5- Illustration of inheritance - Image by whyu.org

Here too, even though the two classes are well separated, they're still bound. Users class can't go somewhere without the Database class. And the biggest problem is that:

  • You can't extend more than one class, so if our class needed another option(dependency) like a logging system it will be hard for us to tie these three classes.
  • The two classes are tightly coupled
  • If a method is deleted in the super class, then we will have to re-factor.

Instead of that, all we're going to do is to create the users class independently of any dependency it may have. Then we can prepare it to receive its dependencies as parameters of the constructor:

<?php

class Users{

    //...
    private $users;
    $private $db;

    public function __construct($databaseHandler, $anotherDependency = '', ...)
    {
        $this->db = $databaseHandler;
    }

    public function fetchUsers( )
    {

        // can use $databaseHandler which comes from the parent class
        $this->users = $this->db->prepare(' SELECT * FROM users ');

        //...
    }

    //...

}

Image 6- Users class without Inheritance

Basket

Image 7- Illustration of Slim Container with many entries - Image by nimahia

This can finally allow us to inject or throw into the users' any dependency class and it can make use of it without even knowing about it. *This principle is called Dependency Injection.

What does that have to do with Slim 3 framework. Well, Slim framework does only request and response stuff. If you want to extend slim's power you need to bring third-party modules/packages. To make that powerful and easy for us, Slim has put in place a dependency injection container which will be in charge of feeding the main Slim app with any dependence we would've added to the container of our dependencies(Dependency Injection Container).

This is a very basic way of explaining what a Dependency injection is. Slim's DIC is quite great because it's built on the famous DI pimple. With that we can take Slim 3 from micro-framework to a powerpuf one just by bringing in anything we need. You can so far perceive one of the greatest advantages of Slim 3 which is that you use only what is needed, no garbage to keep.

How is the DIC is used in Slim 3?

When you want to inject any dependency in Slim 3 one way to do that is to get the container class object as following:

<?php

//...

$SomeSettings = require 'settings.php';
$container = new SlimContainer($SomeSettings);

// Then you can use the container variable to inject any needed service
// you can create as many as possible services
$container['service'] = function ($container) {
    // get the setting in here
    $config = $container->get('settings');

    // return the service class instance
    return new MyServiceClass();
};

// ...

// Now while creating our Slim App,we can give it this container
$app = new SlimApp($container);

Image 8- Slim app with container entry

From here the container entry service can be accessed from anywhere in our Slim $app and the entry holds an instance of MyServiceClass like this:

$app->service->aServiceMethod();

Templating

By default Slim doesn't fully support the MVC model. There is no Model, no View, only the Controller which handles HTPP requests and returns just another HTTP response. So in order to benefit from full view system(templating) which will allow us to use HTML document as our view instead of raw HTTP responses, Slim has some services we can call(inject ) in it and get that power: Slim-Twig and PHP-View.

If you want to understand the real use of this templating here, run the application we did in the previous part, then on your browser choose to show the source code. You should see there is no HTML code. And you can't design a website without HTML. If you wanted to see some HTML code you would have added it just in your routes and you would have ended up mixing your HTML and your PHP code: It's not professional, not maintainable, not clean and a very bad practice.

Template come in to solve this problem. We'll just have to tell Slim, "hey, just go there and look for the needed HTML and display it along with the HTTP response".

How does that work in real Slim Application?

To illustrate how exactly all this work together and how useful it's, let me use the sample application I create in [beginning]() of this tutorial.

So let use Slim's DIC to inject the Slim-Twig module/service so that we can add our templates easily. Since I have chosen to use Slim-Twig note that we will be using Twig templating engine actually in the template.

Let start by adding two new folders at the root of our application templates and cache. The first one will contain our templates and the second one will store Twig's cache.

Then let's require the Twig-View package from composer to our project:

composer require slim/twig-view

Image 9- Require Twig-View package with composer

This will take some few seconds and you should have it appearing in your vendor folder. At the end my composer.json file looks like:

{
    "require": {
        "slim/slim": "^3.0",
        "slim/twig-view": "^2.1"
    }
}

Image 9- composer.json file

and the the folder structure is like:

-- myapp
    - public
        - index.php
    - cache
    - templates
        - home.twig
    - vendor
    - ...

Image 9- Application folder structure

Now here is the full code in our index.php file:

<?php

    // Let's ask PHP to display all errors whenever they
    // occur in our slim code,
    // otherwise Slim will kind of swalow them, it will
    // only show in the command like.
    // Make sure you set this before other codes

    // The value mast become `false` before deployment
    ini_set('display_errors', true);

    // Call composer to autoload(make them available)
    // all classes from the the `vendor` folder

    // This file is in charge of doing that, and it's
    // located at vendor/autoload.php
    // Your folders structure might be different from mine,
    // make sure your adjust this relatively
    require __DIR__ . '/../vendor/autoload.php';

    // Let's announce to our application that we will be using
    // the Slim application class(`vendor/slim/slim/slim/App.php`) by calling its namespace. We don't need to require it with its
    // full path `vendor/slim/slim/slim/App.php`. Composer autoload
    // has already done that for us up there.
    use SlimApp;

    // Use the PSR-7 HTTP Messages interfaces
    use PsrHttpMessageServerRequestInterface as Request;
    use PsrHttpMessageResponseInterface as Response;

    // Some settings for twig
    $settings = [

        'view' => [
            'template_path' => '../templates',
            'twig' => [
                'cache' => '../cache',
                'debug' => true,
                'auto_reload' => true,
            ],
        ],

    ];

    // Create an instance of the container
    $container = new SlimContainer($settings);

    // Create  new container service for Twig-Views
    $container['views'] = function($container){

        // Get the settings
        $path = $container->get('view')['template_path'];
        $opt = $container->get('view')['twig'];

        // Return an instance of twig-view
        return new SlimViewsTwig($path, $opt);
    };

    // We now get a new instance/Object of slim app itself
    // and we save it in a variable we can name `$app`
    // You can name this variable anything
    // We now inject the container inside our app
    $app = new App($container);

    // We add our first route which will respond to the home page
    // request, usually located at `/` or root.
    $app->get('/', function(Request $request, Response $response, $args){

        // Do anything here, like:
        // echo "Welcome to Slim Town!";

        // Now we can render an HTML document(template)
        // Notice the container service key name 'views' set previously
        // It's now accessible here as an object
        return $this->views->render($response, 'home.twig');

    });

    // We add a route for the about page
    $app->get('/about', function(Request $request, Response $response, $args){

        // Do anything here, like:
        echo "About Us <br>";
        echo" This is our about page ";

        // Then return an HTTP response
        return $response;

    });

    // Once we have the instance of SlimApp we can ask it to start running
    // the application by calling Slim's run() function
    $app->run();

Image 10- Source code in public/index.php

And the source code inside our twig template:

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">
        <title>slim 3</title>
    </head>
    <body>
        <h1>Welcome to slim town!</h1>
    </body>
</html>

Image 10- Source code in templates/home.twig

Run your application now and you get this:

Slim Application running

Image 11- Application result

Conclusion

Even though I made the talk much, this is actually a simple thing. I needed to let you know what exactly is going on. We will have the chance in this series to explore the Slim's DIC and Views. In fact the next part in this series is going to be on Views. We will be crafting a very simple static website with Slim 3. Stay tuned we're getting there.

Last updated 2024-01-11 UTC