Creating First Slim Application

Slim is a PHP micro framework which is very light wight and can be used to build a robust web application and REST APIs. For more information please feel free to visit (Slim website)[http://www.slimframework.com].

Folder Structure

So far I haven't come across anything that says specifically about how Slim up should be arranged, so I guess you a freedom to arrange as you wish, it is bes to have a structure in place. I follow slim site for this with the structure as below:

public/
    .htaccess
    index.php
    styles/
    images/
    scripts/
app/
    routes/
        session.php
        member.php
        admin.php
vendor/
lib/
data/

public/ Contains the Slim Framework’s .htaccess and index.php. files. The index.php file is where you instantiate and run your Slim Framework application. Public assets (stylesheets, images, and scripts) are in this directory, too.

app/ Contains the application’s code that should not be available in the public document root.

vendor/ Contains third-party libraries.

lib/ Contains custom libraries used by my application.

data/ Contains the application’s database schema and (if needed) SQLite databases.

Lest's Start

We are going to build a small slim app about a car, make and model name. The aim is that we will lear - The basics of slim application. - Help understand how MVC (Model View Controller works) - Also help us understand dependency injection container

There is a slim skeleton that you can use for your apps but for this app we are going to create everything from scratch just so you can maximise the learning.

First create a folder that will have all you application code such as FirstApp. In the next steps we will need composer to install slim and its dependencies, so if you don't have composer installed go ahead and install composer globally once done navigate inside the directory that you created earlier i.e. FirstApp and while using terminal type in this command:

composer require slim/slim 

Once that done go ahead and create folder as per above structure. Next add you namespace to autoloding in composer.json like so

"autoload": {
    "psr-4": {
      "FirstApp\\": "app/"
    } 

This will mean we can reference our classes using namespaces rather than having loads of require's everywhere.

Next on your command line run composer dump-autoload this regenerates our autoloading and namespaces to contain the changes we just made.

MVC (Model View Controller)

To explain very briefly the MVC approach:

Model - is ver business logic goes i.e. all the programming is needed on lower level whether it is interaction with a database or other services. Controller - The controller provides model data to the view.
View - This is is where data are actually displayed in form of different templates/themes.

So first thing we are going to do is to create our car name model like so

app/Models/CarMakeModel - Car make model call to hold information about the make of the car.

<?php

namespace FirstApp\Models;

class CarMakeModel {

  public $carMake = "Porsche";
}

Next we are going to create another class that will hold information about the model of the car.

app/Models/CarModelNameModel - The name of the car model e.g.BMW X6 or Porsche Calimera

<?php

namespace FirstApp\Models;

class CarModelNameModel {

  public $modelName = "Calimera";
}

Notice that both classes we have created under our app folder.

Next we are going to create a CarModel like so:

app/Models/CarModel

<?php

namespace FirstApp\Models;

class CarModel {

  public $make;
  public $model;

  public function __construct($make, $model) {
    $this->make  = $make;
    $this->model = $model;
  }

  public function getCarMake() {
    return $this->make->carMake;
  }

  public function getCarModelName() {
    return $this->model->modelName;
  }
}

On the next step is to create a CarProductionModel so car can be created quickly with all dependencies in just one call. So we are going to create CarProductionModel inside app/Models folder like so:

app/Models/CarProductionModel

<?php

namespace FirstApp\Models;

class CarProductionModel {

  function __invoke() {
    $carMake      = new CarMakeModel();
    $carModelName = new CarModelNameModel();

    return new CarModel($carMake, $carModelName);
  }
}

As you will see our CarProductionModel doesn't need a constructor function, the logic is put inside the __invoke magic method because of the way it is called from our Dependency Injection Container (DIC).

The Dependency Injection Container (DIC) will serve a big associative array that knows how to instantiate objects for you when we put in there for use later in our application.

Now that we have done this now we can create our dependencies where CarProductionModel is referenced. So first create a php fine at app/dependencies then copy and past the following code.

app/dependencies

<?php
// DIC configuration

$container = $app->getContainer();

// view renderer
$container['renderer'] = function ($c) {
    $settings = $c->get('settings')['renderer'];
    return new Slim\Views\PhpRenderer($settings['template_path']);
};

$container['carModel'] = new \FirstApp\Models\CarProductionModel();

Note that at this point you need to install slim/php-view this is needed for rendering templates which we will do later on. While on your project root go ahead and install php-view like so:

composer require slim/php-view

This will update composer.json where it will all slim/php-view under the required. This line $container['carModel'] = new \FirstApp\Models\CarProductionModel(); we are saying inside our container (DIC) add a new key of carModel and when we ask for that carModel run our CarProductionModels to instantiate and return a ready made car object with all the dependencies.

Routing

When we first created this application if we created under an index.php file public folder and pasted this in:

<?php

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$app = new \Slim\App;
$app->get('/hello/{name}', function (Request $request, Response $response, array $args) {
  $name = $args['name'];
  $response->getBody()->write("Hello, $name");

  return $response;
});

$app->run();

Then we would have a route set up which is /hello/john so when we go to our apps domain i.e. /hello/john then we would see the following:

Hello, john

This is very basic way of setting up a route. What we are going to do is create a custom route so when people request a certain path then this in turn will call a function and the function will have the logic that will do the work.

Create a Custom Routing

As mentioned above we will create a route so when users request that path it will use a controller to serve. So fir thing to do here create a php file withing app folder like so:

app/route

<?php
$app->get('/car', \FirstApp\CarController::class);

Now we have created /car route and next thing is to create a controller.

Create a Controller

Now we are going to create a controller that when users go to /car will call this controller and will display data on a template. Here is the code for this controller.

app/CarController

<?php

namespace FirstApp;

class CarController {

  protected $container;

  // This constructor passes the DIC in so we can get our PenFactory out of it later
  function __construct($container) {
    $this->container = $container;
  }

  function __invoke($request, $response, $args) {
    // Create our car from CarProduction model in DIC
    $car = $this->container->get('carModel');
    // Assign args (variables that will be available on rendered view).
    $args['carMake']      = $car->getCarMake();
    $args['carModelName'] = $car->getCarModelName();

    // Get the default template renderer out of DIC and pass the response and $args to a template file.
    return $this->container->get('renderer')
      ->render($response, 'showCar.phtml', $args);
  }
}

Now that our controller exists when can go back to our route and observe this line $app->get('/car', \FirstApp\CarController::class); And hopefully this now make sense what is happening here.

Template

In our controller above you will notice that we are calling showCar.phtml which has not been created yet, so next step is to create this file at app/tempates/showCar.phtml like so:

app/tempates/showCar.phtml

<!DOCTYPE html>
<html>
<head>
  <title>First Application</title>
  <style>
    h2 {
      font-size: 5rem;
      color: red;
    }
  </style>
</head>
<body>
<h2>Car Make: <?php echo htmlspecialchars($carMake); ?>!</h2>
<h2>Car Model: <?php echo htmlspecialchars($carModelName); ?>!</h2>
</body>
</html>

What we are doing above is use the values we put into the $args associative array as normal variables.

Other Settings

Other settigns that we need to do are to create two other files as below

Settings.php

Create a seetings.php where you can putt all different types of settings:

app/settings.php

<?php
return [
    'settings' => [
        'displayErrorDetails' => true, // set to false in production
        'addContentLengthHeader' => false, // Allow the web server to send the content-length header

        // Renderer settings
        'renderer' => [
            'template_path' => __DIR__ . '/../templates/',
        ],

        // Monolog settings
        'logger' => [
            'name' => 'slim-app',
            'path' => isset($_ENV['docker']) ? 'php://stdout' : __DIR__ . '/../logs/app.log',
            'level' => \Monolog\Logger::DEBUG,
        ],
    ],
];

index.php

public/index.php needs to all different settings of the app as well as routes, middleware, dependencies etc.

public/index.php

<?php
require __DIR__ . '/../vendor/autoload.php';

session_start();

// Instantiate the app
$settings = require __DIR__ . '/../app/settings.php';
$app = new \Slim\App($settings);

// Set up dependencies
require __DIR__ . '/../app/dependencies.php';

// Register middleware
// require __DIR__ . '/../app/middleware.php';

// Register routes
require __DIR__ . '/../app/routes.php';

// Run app
$app->run();

.htaccess File

public/.htaccess

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]

To run the application as self-hosted you can type this command:

php -S localhost:8080 -t ./public

When you to http://localhost:8080/car then you will see the details on the page. You can also create a virtual host so it will have your own custom domain e.g. my-cosum-domain.local.

That is it! we have now created our first slim application feel free to grab the code from below in case you miss something from the steps above.

The Code

Here is the code.