Logo
  • PHP
    • HipHop / HHVM
    • Modern PHP
    • PHPStorm
    • LAMP
    • Laravel
    • Composer
    • PDO
  • JavaScript
    • node.js
    • AngularJS
  • CSS
    • SASS
    • “CSS4” (CSS level 4)
  • HTML
  • Git
  • LAMP
  • Vagrant
  • UI / UX
  • Architecture of …
  • Off-Topic
With ♥ from Berlin
December 11, 2014
Chris
Local Development, mini, MVC, PHP
Comments Off on MINI2, an extremely simple barebone PHP application on top of Slim

MINI2, an extremely simple barebone PHP application on top of Slim

PreviousNext

For my daily work I often needed to setup super-simple PHP applications, just some more or less static pages plus some dynamic pages with simple database calls, one or two simple forms and maybe a little bit of AJAX. You know, the typical agency stuff. This usually led to the question: Use a real framework or just mash some .php files together ? The result was MINI, an extremely simple naked PHP application (or maybe even a framework), more on that in another article or directly on GitHub.

As MINI is really just very very basic I tried to upgrade this project a little bit by building the same naked application from scratch, this time on top of Slim, the popular micro framework. The result is MINI2, a super-simple script that might be a very simple, but very powerful base for smaller PHP applications. As the application’s core logic is now delivered by Slim, the script itself is extremely compact and comes with just 2 (!) php files plus some view templates and the casual .js and .css stuff.

 

The features of MINI2

  • built on top of Slim (you can easily update Slim without harming MINI2)
  • RESTful routes
  • extremely simple: the entire application is just 2 .php files (plus external dependencies plus view templates)
  • makes “beautiful” clean URLs (like example.com/car/show/17, not example.com/index.php?type=car&action=show&id=17)
  • uses Twig as template engine, others are possible (via Slim packages)
  • uses pure PDO instead of ORM (it’s easier to handle)
  • basic CRUD functions: create, read, update/edit and delete content
  • basic search
  • basic AJAX demo
  • (optional) shows emulated PDO SQL statement for easy debugging
  • (optional) compiles SCSS to CSS on the fly
  • (optional) minifies CSS on the fly
  • (optional) minifies JS on the fly
  • (optional) dev/test/production switch

 

The requirements of MINI2

  • PHP 5.3+
  • MySQL
  • mod_rewrite activated (when using Apache), document root routed to /public (tutorial below)

 

A simple screenshot

mini-screenshot

Installation

There’s a quick installation guideline on GitHub. If you are familiar with Vagrant then there’s also a one-click installation (yo, just a simple vagrant up command) that will create a fully configured Ubuntu 14.04 LTS with a completely setup MINI2 inside. Check the GitHub readme for more information.

The structure of MINI2

mini2-structure
Two folders, Mini and public. public holds the index.php that contains your application’s logic plus your JavaScript files, the scss (SASS) / CSS files and for sure the .htaccess that makes beautiful URLs. The SASS-to-CSS compiling is totally optional, you can turn it off by simple commenting out one line of code later.

The folder Mini contains – beside installation stuff – just the model/model.php that holds a set of data-manipulating methods (the MVC-model) and the folder view that holds a set of Twig files (which is just HTML with shortcodes for easily echoing out PHP variables), so it’s the MVC-view. Before you ask: There’s no folder controller as index.php works as the controller.

In the root folder there’s the composer.json that contains the to-be-loaded dependencies and a rule for autoloading our files. When fetching the dependencies while installing MINI2, Composer will load Slim (the router, more on that later), Twig (the template engine that renders the HTML and the variables from .twig files) and some other small tools and put them into the automatically created vendor folder, you know the process. If you have no idea what this means, then MINI2 is not for you, sorry.

The subfolder modules inside public/scss just contains a .scss with variables (like $font-color: #222222;) for easier SASS rendering, a common way to modularize your scss.

The folder _install in Mini holds 3 .sql statements with demo data for a working demo setup, the folder _vagrant contains a Vagrantfile and a bootstrap.sh to create a 100% automatically installing Ubuntu 14.04 LTS box with MINI2 running inside, more on that in the install tutorial on GitHub.

How MINI2 works

MINI2 works like most other frameworks and uses URLs like this: example.com/songs, example.com/songs/editsong/17, example.com/subpage etc. instead of ugly messy index.php?xxx=111&yyy=222 URIs. By default an installation of MINI2 needs a simple configuration of your Apache (or nginx or IIS) that routes the user directly to /public/index.php. This is easy to set up and clearly described in the install tutorial. Beside the aesthetical point this also prevents access to anything else then the /public folder, which makes your application much much more secure and also protects any potential .git files/folder, swap/noise files etc. from being curl’ed etc.

Let’s look into public/index.php, step by step:

<?php

/******************************* LOADING & INITIALIZING BASE APPLICATION ****************************************/

// Configuration for error reporting, useful to show every little problem during development
error_reporting(E_ALL);
ini_set("display_errors", 1);

// Load Composer's PSR-4 autoloader (necessary to load Slim, Mini etc.)
require '../vendor/autoload.php';

// Initialize Slim (the router/micro framework used)
$app = new \Slim\Slim();

// and define the engine used for the view @see http://twig.sensiolabs.org
$app->view = new \Slim\Views\Twig();
$app->view->setTemplatesDirectory("../Mini/view");

For development, the error reporting is set to maximum, then the Composer-autoloader is loaded, and Slim is initialized. As we use Twig as the template engine Twig is also initialized and the path to the view files is defined. By the way, I really like the way Slim handles this: $app->view. Beautiful!

 

/******************************************* THE CONFIGS *******************************************************/

// Configs for mode "development" (Slim's default), see the GitHub readme for details on setting the environment
$app->configureMode('development', function () use ($app) {

    // pre-application hook, performs stuff before real action happens @see http://docs.slimframework.com/#Hooks
    $app->hook('slim.before', function () use ($app) {

        // SASS-to-CSS compiler @see https://github.com/panique/php-sass
        SassCompiler::run("scss/", "css/");

        // CSS minifier @see https://github.com/matthiasmullie/minify
        $minifier = new MatthiasMullie\Minify\CSS('css/style.css');
        $minifier->minify('css/style.css');

        // JS minifier @see https://github.com/matthiasmullie/minify
        // DON'T overwrite your real .js files, always save into a different file
        //$minifier = new MatthiasMullie\Minify\JS('js/application.js');
        //$minifier->minify('js/application.minified.js');
    });

    // Set the configs for development environment
    $app->config(array(
        'debug' => true,
        'database' => array(
            'db_host' => 'localhost',
            'db_port' => '',
            'db_name' => 'mini',
            'db_user' => 'root',
            'db_pass' => 'your_password'
        )
    ));
});

Here we defined the configs for the development environment. To define for production you’d simply copy this block and fill the according credentials. More on that in the GitHub readme. Skip the $app->hook block for now and look at $app->config: This is self-explaining, right ? Okay, back to $app->hook: These are Slim’s hooks, blocks of code than are called at specific points in the application’s lifetime, in this case before the application is run. Here, in the default setup, these lines are just compiling our .scss files to .css and the style.css is minified, both via Composer-loaded libraries.

 

/******************************************** THE MODEL ********************************************************/

// Initialize the model, pass the database configs. $model can now perform all methods from Mini\model\model.php
$model = new \Mini\model\model($app->config('database'));

Just one line. To better understand this, have a look into Mini/model/model.php (shortened for better explanation):

<?php

namespace Mini\Model;

use PDO;

class Model
{
    /**
     * The database connection
     * @var PDO
     */
    private $db;

    /**
     * When creating the model, the configs for database connection creation are needed
     * @param $config
     */
    function __construct($config)
    {
        // PDO db connection statement preparation
        $dsn = 'mysql:host=' . $config['db_host'] . ';dbname='    . $config['db_name'] . ';port=' . $config['db_port'];

        // note the PDO::FETCH_OBJ, returning object ($result->id) instead of array ($result["id"])
        // @see http://php.net/manual/de/pdo.construct.php
        $options = array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ, PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING);

        // create new PDO db connection
        $this->db = new PDO($dsn, $config['db_user'], $config['db_pass'], $options);
    }

    /**
     * Get all songs from database
     */
    public function getAllSongs()
    {
        $sql = "SELECT id, artist, track, link, year, country, genre FROM song";
        $query = $this->db->prepare($sql);
        $query->execute();
        return $query->fetchAll();
    }
}

This is just a simple class that expects an array with database configs when being constructed. When constructed, a database connection will be created and all methods – which are usually database calls and data-manipulating blocks – can be used. So, back to our index.php, let’s look again at that line:

 

/******************************************** THE MODEL ********************************************************/

// Initialize the model, pass the database configs. $model can now perform all methods from Mini\model\model.php
$model = new \Mini\model\model($app->config('database'));

So we create the Model object above and pass the configs to it. We can now perform actions on the $model, like $model->getAllSongs(); ! Totally simple.

 

/************************************ THE ROUTES / CONTROLLERS *************************************************/

// GET request on homepage, simply show the view template index.twig
$app->get('/', function () use ($app) {
    $app->render('index.twig');
});

// GET request on /subpage, simply show the view template subpage.twig
$app->get('/subpage', function () use ($app) {
    $app->render('subpage.twig');
});

This is where the real action starts: Here the routes are defined, some might also call them controllers. The logic behind is extremely simple, everything inside $app->get(‘/’ … will be called when a GET request is made on the project’s homepage, like example.com, everything inside $app->get(‘/’subpage … will be called when a GET request is made on example.com/subpage. Super-simple. Slim’s router can handle GET (browser’s default when simply “surfing” websites), POST (usually when submitting a form) and most other REST verbs.

The line $app->render(‘index.twig’); says what you expect: The file Mini/view/index.twig (which is just pure HTML in this case) is loaded, so in this case a simple static page is shown. See the Twig documentation for more information. You’ll  love Twig, as it’s indeed easier than pure PHP (!).

Now to some more complex routes:

// All requests on /songs and behind (/songs/search etc) are grouped here. Note that $model is passed (as some routes
// in /songs... use the model)
$app->group('/songs', function () use ($app, $model) {

    // GET request on /songs. Perform actions getAmountOfSongs() and getAllSongs() and pass the result to the view.
    // Note that $model is passed to the route via "use ($app, $model)". I've written it like that to prevent creating
    // the model / database connection in routes that does not need the model / db connection.
    $app->get('/', function () use ($app, $model) {

        $amount_of_songs = $model->getAmountOfSongs();
        $songs = $model->getAllSongs();

        $app->render('songs.twig', array(
            'amount_of_songs' => $amount_of_songs,
            'songs' => $songs
        ));
    });

    // POST request on /songs/addsong (after a form submission from /songs). Asks for POST data, performs
    // model-action and passes POST data to it. Redirects the user afterwards to /songs.
    $app->post('/addsong', function () use ($app, $model) {

        // in a real-world app it would be useful to validate the values (inside the model)
        $model->addSong(
            $_POST["artist"], $_POST["track"], $_POST["link"], $_POST["year"], $_POST["country"], $_POST["genre"]);
        $app->redirect('/songs');
    });

    // GET request on /songs/deletesong/:song_id, where :song_id is a mandatory song id.
    // Performs an action on the model and redirects the user to /songs.
    $app->get('/deletesong/:song_id', function ($song_id) use ($app, $model) {

        $model->deleteSong($song_id);
        $app->redirect('/songs');
    });

 

$app->group(‘/songs’ … “groups” everything that happens under example.com/songs, like example.com/songs itself, example.com/songs/editsong etc. Let’s look at $app->get(‘/’, … which defines what happens under example.com/songs(/). Here we show a twig file like before, but also pass some variables to the view: Mini/view/songs.twig will be shown, and the content of $amount_of_songs will be available inside that file via {{ amount_of_songs }} [that’s the syntax of Twig], like this (songs.twig):

<h2>We have {{ amount_of_songs }} songs</h2>

Okay, back to the index.php: The above code clearly shows where $amount_of_songs comes from: $amount_of_songs = $model->getAmountOfSongs(); ! As we have defined $model earlier we can use any method from that class and so get or manipulate data, like in this case simply fetching a number. Important: Note that as we use $model inside the $app->get block, the $model variable has to be passed inside, like shown in exactly that line and the $app->group line before: use ($app, $model .. defines which variable are known inside these methods. Without passing the $model we could not use $model inside.

Next block: $app->post( handles a POST request on example.com/songs/addsong. The method’s code is nearly self-explaining: We perform a method of $model and pass some variable from $_POST there. After that we route the user back to example.com/songs via $app->redirect(‘/songs’).

 

And basically: That’s it! Finally the index.php closes with

/******************************************* RUN THE APP *******************************************************/

$app->run();

to run the entire application for sure. Have a deeper look into all these files for a better understanding and some more examples, like the AJAX call or the search feature. Also, make sure you read into Slim’s excellent documention and the Twig docs.

 

Where to find MINI2 ?

On GitHub for sure, you might also bookmark the portal page php-mini.com and the Facebook page. I’m also trying to build a documentation on php-mini.com/documentation. There’s also the first version of MINI (1) on GitHub.

 

appapplicationbareboneframeworkminimini2nakedPHPskeleton
Share this
cheap cloud server php

DigitalOcean rolls out interesting feature: Transfering server snapshots directly to the client’s account

Today DigitalOcean has rolled out a quite interesting new feature: You are now able to transfer a server snapshot (which

How to get a single table out of a massive MySQL .sql database backup file (mysql dump splitter)

Imagine the following situation: Somebody backs up an entire MySQL database – a very large one – with common tools.

mod-rewrite-ubuntu-14-04-lts

How to enable mod_rewrite in Ubuntu 14.04 LTS

A little note first: This is the most basic way to enable mod_rewrite. However, it’s not the best way. The

How Snapchat wants to earn money (by establishing vertical videos)

Snapchat is worth 15-19 billion dollars. An insane number, making everybody ask: How the hell is this possible, why has

shadow dom

Crossbrowser-safe HTML5 video (IE6+) with a few lines of code and just one .mp4 video file

No time to read the full article ? Get the code directly here on GitHub: panique/html5-video. Publishing a video on

bash-command-line-tutorial

Best introduction to unix command line / bash ever (by André Augusto Costa Santos)

java

Interesting: code of the same application in PHP, Python, Ruby, Closure, node.js, Java and Go

Definitly worth a look: Adam Bard created a tiny application in 4 different languages (PHP, Python, Ruby, Closure), just to

How to show the available version of a package (before doing apt-get install)

To show the version of the package that will be installed with apt-get install, do apt-cache policy packagename. To show

php

How to install PHP curl extension (in 5 seconds)

It’s a common wordpress problem: PHP’s curl extension is not installed! No need to mess around in config files etc,

How major web companies (and banks) handle passwords quite wrong

There’s a very interesting “movement” in password handling going on for a long time, the basic idea is to encourage

1/4

Categories

Search

php
How to prevent PHP sessions being shared between different apache vhosts / different applications
[Link] Interesting: Designing a Nuclear Waste Warning Symbol That Will Still Make Sense in 10,000 Years
php
How to install/setup latest version of PHP 5.5 on Debian Wheezy 7.0/7.1/7.2 (and how to fix the GPG key error)
O’Reilly’s Learning JavaScript Design Patterns by Addy Osmani for free
Interesting stats on SONY’s hacked passwords
cheap cloud server php
DigitalOcean rolls out interesting feature: Transfering server snapshots directly to the client’s account
twig
A 6min video introduction into Twig, the PHP templating engine
How to show the available version of a package (before doing apt-get install)
php
How the PHP session garbage collector really works
[Link] Making a website vertically responsive
github-logo-octocat
GitHub rolls out .PSD diff and viewing
the-php-login-project
How to install php-login-minimal on Ubuntu 12.04 LTS
angular js
Learn AngularJS in 20 (or 90) minutes with Dan Wahlin
php
New GitHub repo: simple php-long-polling for creating real-time apps
How to fix the ugly font rendering in Google Chrome

Tags

apache bash centos composer conference coupon CSS debian fonts framework git GitHub hack HHVM HipHop HTML HTML5 IDE JavaScript JS LAMP laravel linux mod_rewrite MVC MySQL Nginx optimization PHP PHP 5.5 PHP 5.6 phpmyadmin PHPStorm security server SSD Ubuntu UI UX vagrant video virtual machine voucher VPS wordpress
Side-Project: Wordle-Solver:
www.wordle-helper.info

Pages

  • Privacy Policy