Using Eloquent, Twig and Slim PHP


Warning - Old content ahead!

This page has been marked as a legacy post. This means it's quite old (from 2013) and probably out of date. Take it with a grain of salt!

Note: See the update to this post where I've made several changes

Originally while I was writing this blog I intended to make a step by step guide to putting together three of my favourite PHP tools. After a few issues and finding out that Slim Views is now a thing I decided instead to put together a git repo so anyone can get started quickly. I will use this blog post to quickly show how it works!

*Note that this tutorial doesn’t aim to teach you Slim, Twig or Eloquent

You can find the repo here:

After following the Installation instructions you should be able to use each component as well as Eloquent validation.

Folder Structure

As the public/index.php calls /index.php from the root of the application which then includes config.php, app/application.php and app/routes.php.

app/application.php sets Slim, Twig and Eloquent up so that you can create all of your Slim routes in app/routes.php.

Example Usage

Assuming you've now run git clone, composer install, renamed the config.php file and changed your database settings let's create a table. I've opted to use MySQL - here's the query to make a posts table:

CREATE TABLE `posts` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  `created_at` datetime NOT NULL,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)

A simple table with an unique id integer, title string, content text, created_at datetime and updated_at timestamp.

Next for the routes. Enter the following in app/routes.php:

/* new */
$app->get( '/posts/new', function () use ( $app, $data ) {
  $data['post'] = new Post;
  $app->render( 'posts_new.twig', $data );
})->name( 'posts_new' );

/* create */
$app->post( '/posts', function () use ( $app, $data ) {
  $post = new Post;
  $post->title = $_POST['title'];
  $post->content = $_POST['content'];
  if( $post->validate( array( 
    'title'   => $_POST['title'], 
    'content' => $_POST['content'] 
    $app->flash( 'notice', "you're post has been created" );
    $app->redirect( $app->urlFor( 'posts_index' ) );
    $app->flash( 'error', 'your post was not valid' );
    $app->flash( 'errors', $post->errors() );
    $app->redirect( $app->urlFor( 'posts_new' ) );
})->name( 'posts_create' );

/* view all */
$app->get( '/posts', function () use ( $app, $data ) {
  $data['posts'] = Post::all();
  $app->render( 'posts.twig', $data );
})->name( 'posts_index' );

Above there are three routes. GET /posts to view all posts, GET /posts/new to enter a new post and POST /posts to create the post. On lines 9 and 31 you can see calls to a class Post which we will create shortly in our models directory. On line 12 you can see that you can use $posts->validate() to verify entered input.

Here's the model along with validation located inapp/models/Post.php

class Post extends Illuminate\Database\Eloquent\Model
    protected $table = 'posts';
    private $errors;

    private $rules = array(
      'title'   => 'required|between:4,16',
      'content'  => 'required|min:3'
    private $messages = array(
      'required' => 'Your :attribute is required.',
      'min'      => 'Your :attribute must be at least :min characters long.',
      'max'      => 'Your :attribute must be a maximum of :max characters long.',
      'between'  => 'Your :attribute must be between :min - :max characters long.',
      'email'    => 'Your :attribute must be a valid email address'

    public function validate($params)
        $validator = Validator::make( $params, $this->rules, $this->messages );
        if( $validator->fails() )
          $this->errors = $validator->errors()->all();
          return false;
        return true;

    public function errors()
      return $this->errors;

Validation is entirely optional - If you do not require validation just create the class and extend Illuminate\Database\Eloquent\Model

At this point we just need to create our views - but before that you should run composer update so that your new model can be automatically loaded.

All templates are kept in app/views


{% extends 'layout.twig' %}

{% block content %}
  {% for post in posts %}
    <li>{{ post.title }}</li>
    {% else %}
    There are no posts here :(
  {% endfor %}
  <a href="{{ urlFor('posts_new') }}">Create Post</a>
{% endblock %}


{% extends 'layout.twig' %}

{% block content %}
 <form action="{{ urlFor('posts_create') }}" method="post">
 {% if flash.error %}
  <p>{{ flash.error }}</p>
  {% if flash.errors %}
    {% for error in flash.errors %}
    <li>{{ error }}</li>
    {% endfor %}
  {% endif %}
 {% endif %}
  <input type="text" name="title" placeholder="Title">
  <input type="text" name="content" placeholder="Content">
  <input type="submit" value="Create">
{% endblock %}

layout.twig (Put your layout in here as usual)

{% block content %}
{% endblock %}

As you can see, Slim functions are available with urlFor() being called on line 11 in posts.twig and line 4 in posts_new.twig. You can also use Slim's flash functionality to send messages to redirections. At this point everything should work!

Custom Configurations

Perhaps you don't want your templates to be in a view folder, you want to disable twig caching or change error reporting for different environments?

99% of the magic inside of app/application.php. This is where Slim is instantiated along with Twig and Eloquent. Make sure that you take a look so you can perfect your own set up!


While putting this together I came across some links which helped me out a lot. Here are as many as I can remember:

Spread the word

Share this article

Like this content?

Check out some of the apps that I've built!


Command-line snippet manager for power users



Third party app for iOS.



GUI for the rsync command