Mr Simon Cohen on Slim
Introduction
Slim is a micro framework, it can help you to build a web application fast, not only a working website, but also a prototype if you need a fast prototyping tool.
Over a year ago I just built a working website in a few days (of course it didn't include the frontend development - HTML, CSS & JavaScript) for Mr Simon Cohen with Slim entirely for the site's soft launch. Simon is a keynote speaker and broadcaster who gave away a million pound company, Global Tolerance, for his family and currently living in Cornwall, United Kingdom. He is happy for me to publish the code (usually you should not simply publish the source code of your client's website) for learning and sharing purposes. Simon's current site is on WordPress which was developed with a custom theme and launched in a couple of months after the soft launch.
As said, Slim is a micro framework, so it focuses on HTTP request and response. It does not provide a CMS like WordPress or Drupal does. Of course you can develop a full CMS with Slim if you are up for it.
Concept
Simon's Slim website uses a very simple application structure for fast development. If you want a more complicated and modular structure, you can take a look on this post that I made sometime ago - Creating a Modular Slim Application.
The structure for Simon's:
/path/to/project/
bootstrap.php
config/
library/
module/
source/
vendor/
templates/
public/
.htaccess
index.php
Just as many other Slim applications, this is how you run the Slim's instance:
// Import classes.
use Slim\App as Slim;
// Include application bootstrap.
chdir(dirname(__DIR__));
require 'bootstrap.php';
// Get the application settings file.
$settings = require 'config/application.php';
// Get an instance of Slim.
$app = new Slim(["settings" => $settings]);
// Set up dependencies.
require 'config/dependencies.php';
// Register routes.
require 'config/routes.php';
// Run the application!
$app->run();
Template Engine
Twig was chosen for Simon's, so you would install it this way:
composer require slim/twig-view
And hook it up to Slim's container in config/dependencies.php
:
$container = $app->getContainer();
$container['view'] = function ($container) {
$loader = new Twig_Loader_Filesystem('templates/');
// Add additional paths to the existing.
$loader->addPath('templates/layout/');
$twig = new Twig_Environment($loader, array(
// options
));
// Add twig extension.
$twig->addExtension(new \Twig_Extensions_Extension_Array());
return $twig;
};
Then this is how you would call it in one of your routes:
// Home route.
// PSR 7 standard.
use Slim\Http\Request;
use Slim\Http\Response;
$app->get('/', function (Request $request, Response $response, array $args) {
// Get an instance of the Twig Environment.
$twig = $this->view;
// From that get the Twig Loader instance (file loader in this case).
$loader = $twig->getLoader();
// Add the module template and additional paths to the existing.
$loader->addPath('templates/home/');
$loader->addPath('templates/keywords/');
// Render.
$response->getBody()->write(
$twig->render('index.html', ['current' => ''])
);
return $response;
});
You can follow the guide from Twig or Slim on how to use Twig in more details. It is as simple as:
{% extends "layout.html" %}
{% block body %}
<h1>User List</h1>
<ul>
<li><a href="{{ path_for('profile', { 'name': 'josh' }) }}">Josh</a></li>
</ul>
{% endblock %}
Routes and Code Logic
In a more complex application, you should separate the HTTP route and the logic of your code, just as in the module structure that I proposed. But Simon's was not intended to scale, nor was for the CMS development, so I kept the logic in the route for its simplicity. For example, in Contact page's routes, it has:
$app->post('/contact', function (Request $request, Response $response) {
$data = $request->getParsedBody();
// https://bootstrapbay.com/blog/working-bootstrap-contact-form/
if (isset($data["submit"])) {
$name = $data['name'];
$email = $data['email'];
$message = $data['message'];
$human = $data['g-recaptcha-response'];
$errName = null;
$errEmail = null;
$errMessage = null;
$errHuman = null;
$ok = null;
$fail = null;
// Check if name has been entered
if (!$name) {
$errName = 'Please enter your name';
}
// Check if email has been entered and is valid
if (!$email || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errEmail = 'Please enter a valid email address';
}
//Check if message has been entered
if (!$message) {
$errMessage = 'Please enter your message';
}
//Check if simple anti-bot test is correct
if (!$human) {
$errHuman = 'You must complete the captcha to submit.';
} else {
// Google recaptcha integration.
// Get your own secret key from https://developers.google.com/recaptcha/intro
$secret = "xxxxxx";
$gRecaptchaResponse = $human;
$remoteIp = $_SERVER["REMOTE_ADDR"];
$recaptcha = new \ReCaptcha\ReCaptcha($secret);
$resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp);
if ($resp->isSuccess()) {
// verified!
} else {
// error!
$errHuman = 'Your captcha verification failed.';
}
}
// Mailer variables.
// Receiver's email.
$to = 'simon@mrsimoncohen.com';
$subject = "Message from Contact | Mr Simon Cohen ";
// Always set content-type when sending HTML email
$headers = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-Type: text/plain; charset=utf-8\r\n";
$headers .= "X-Priority: 1\r\n";
$headers .= "From: ". $name . " <". $email . ">\r\n";
$headers .= "Reply-To: " . $email . "\r\n";
// If there are no errors, send the email
if (!$errName && !$errEmail && !$errMessage && !$errHuman) {
if (mail($to, $subject, strip_tags($message), $headers)) {
$ok = 'Thank you for leaving your mark. I will be in touch soon.';
} else {
$fail = 'Sorry there was an error sending your message. Please try again later.';
}
}
}
// Get an instance of the Twig Environment.
$twig = $this->view;
...
...
}
But in a complex application, you may want to have a controller, model, gateway and so on to handle the logic above, for example:
-------------
_routes/
insert_user.php
update_user.php
fetch_users.php
fetch_user.php
delete_user.php
Controller/
Controller.php <-- abstract class to be extended by the following classes:
InsertController.php
UpdateController.php
FetchController.php
DeleteController.php
Gateway/
Gateway.php <-- abstract class to be extended by the following classes:
InsertGateway.php
UpdateGateway.php
FetchGateway.php
DeleteGateway.php
Mapper/
Mapper.php <-- abstract class to be extended by the following classes:
InsertMapper.php
UpdateMapper.php
FetchMapper.php
DeleteMapper.php
Model/
Collection.php
Model.php
index.php <-- the user main route.
-------------
And in the route PHP file, you just have to do so:
// PSR 7 standard.
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
$app->put('/users', function (Request $request, Response $response, $args) {
// Autowiring the controller.
$controller = $this->get('Monsoon\User\Controller\UpdateController');
// Obtain result.
$result = $controller->updateUser($request);
$response->getBody()->write(json_encode($result));
});
Conclusion
That's it. Hope you can see how easy it is to build a working application or a website with Slim when you come up against a deadline or a launch date. You can download this working sample on GitHub and run it on your local machine:
# Dependencies
$ cd [my-app-name]
$ composer update
# Production build
$ php -S 0.0.0.0:8080 -t public
Make sure that the port 8080 is not occupied by any other app before accessing it at http://0.0.0.0:8080/. You should see the following screen shots on your browser:
(Desktop view)
(Mobile view)
Have fun!
Notes
- The logo of Mr Simon Cohen was made by Ginger Monkey.