Slim Framework

Composer

Eloquent

Twig

Presentation by Cesar from Bootsoft

@C_Revul

What is Slim Framework?

Slim is a PHP micro framework that helps you quickly write simple yet powerful web applications and Restful APIs.


  • Inspired by Sinatra
  • Favors cleanliness over terseness
  • Favors common cases over edge cases


Webpage http://www.slimframework.com/

Installation

There are two ways:


  1. Manual Installation
  2. Using Composer (Later will be explained)

Hello World

1. Manual installation

index.php

require 'Slim/Slim.php';

\Slim\Slim::registerAutoloader();

$app = new \Slim\Slim();

$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});

$app->run();

Hello World

2. Composer installation

index.php

require 'vendor/autoload.php';

$app = new \Slim\Slim();

$app->get('/hello/:name', function ($name) {
    echo "Hello, $name";
});

$app->run();

...That's it!

Hello World

.htaccess

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Don’t forget!

Routing

GET

$app->get('/books/:id', function ($id) {
    //Show book identified by $id
});

POST

$app->post('/books', function () {
    //Create book
});

PUT

$app->put('/books/:id', function ($id) {
    //Update book identified by $id
});

DELETE

$app->get('/books/:id', function ($id) {
    //Show book identified by $id
});

Routing

Method Override

Unfortunately, modern browsers do not provide native support for HTTP PUT and DELETE requests. To work around this limitation, ensure your HTML form’s method attribute is "post", then add a method override parameter to your HTML form like this:

<form action="/books/1" method="post">
    ... other form fields here...
    <input type="hidden" name="_METHOD" value="PUT"/>
    <input type="submit" value="Update Book"/>
</form>

Route Parameters

$app->get('/books/:one/:two', function ($one, $two) {
    echo "The first paramter is " . $one;
    echo "The second parameter is " . $two;
});

Optional Route Parameters

$app->get('/archive(/:year(/:month(/:day)))', function ($year = 2010, $month = 12, $day = 05) {
    echo sprintf('%s-%s-%s', $year, $month, $day);
});
  • /archive
  • /archive/2010
  • /archive/2010/12
  • /archive/2010/12/05

Features

That was the routing but Slim has much more...

  • Template rendering
  • Flash messages
  • Secure cookies with AES-256 encryption
  • HTTP caching (ETag)
  • Loggin with custom log writers
  • Error handling and debugging
  • Middleware and hook architecture
  • Simple configuration

Slim Framework

Composer

Eloquent

Twig

What is Composer?

Composer is a Dependency Manager for PHP

Composer

Installation

Composer is very easy to install:


Go to http://getcomposer.org and download the installer

Or

Run this in your terminal

curl -s https://getcomposer.org/installer | php

Using Composer

Into the Root of your project create the file:


composer.json

{
    "require": {
        "slim/slim": "2.*"
    }
}

Using composer

To get the dependencies

run in the terminal

composer install

Using composer

To Update the dependencies

run in the terminal

composer update

Packagist

It is the main Composer repository

http://packagist.org

There you can browse and search for packages.

Autoload

Composer generates a vendor/autoload.php file

You can simply include this file and you will get autoloading

require 'vendor/autoload.php';

Autoload

You can even add your own code to the autoloader by adding an autoload field to composer.json.

{
    "autoload": {        
    }
}

There are two ways to autoload your classes:

  1. PSR-0
  2. Classmap

PSR-0

Here you define a mapping from namespaces to paths, relative to the package root

{
    "autoload": {
        "psr-0": {
            "Monolog": "src/",
            "Vendor\\Namespace\\": "src/",
            "Vendor_Namespace_": "src/"
        }
    }
}

This is the recommended way though since it offers greater flexibility

After adding the autoload field, you have to re-run:

composer install

Classmap

You can use the classmap generation support to define autoloading for all libraries that do not follow PSR-0

{
    "autoload": {
        "classmap": ["src/", "lib/", "Something.php"]
    }
}

After adding a new class or file, you have to run:

composer update

create-project

You can use Composer to create new projects from an existing package.

This is the equivalent of doing a git clone/svn checkout followed by a composer install of the vendors.

composer create-project slim/slim-skeleton [my-app-name]

Example

Installing Slim with Composer in Fortrabbit Server

Slim Framework

Composer

Eloquent

Twig

Laravel Framework ORM

Laravel comes with several tools to use with databases:


  • Schema Builder
  • Fluent Query Builder
  • Eloquent ORM

Schema Builder

Provides methods for creating and modifying your database tables.

Schema Builder

Create tables

Schema::table('users', function($table)
{
    $table->create();
    $table->increments('id');
    $table->string('username');
    $table->string('email');
    $table->string('phone')->nullable();
    $table->text('about');
    $table->timestamps();
});

Schema Builder

Methods allow you to add columns

$table->increments('id'); //Incrementing ID to the table
$table->string('email'); //VARCHAR equivalent column
$table->string('name', 100); //VARCHAR equivalent with a length
$table->integer('votes'); //INTEGER equivalent to the table
$table->float('amount'); //FLOAT equivalent to the table
$table->decimal('amount', 5, 2); //DECIMAL with a precision and scale
$table->boolean('confirmed'); //BOOLEAN equivalent to the table
$table->date('created_at');	//DATE equivalent to the table
$table->timestamp('added_on'); //TIMESTAMP equivalent to the table
$table->timestamps(); //Adds created_at and updated_at columns
$table->text('description'); //TEXT equivalent to the table
$table->blob('data'); //BLOB equivalent to the table
->nullable() //Designate that the column allows NULL values
->default($value) //Declare a default value for a column
->unsigned() //Set INTEGER to UNSIGNED

Schema Builder

Defining indexes

$table->primary('id'); //Adding a primary key
$table->primary(array('fname', 'lname')); //Adding composite keys
$table->unique('email'); //Adding a unique index
$table->fulltext('description'); //Adding a full-text index
$table->index('state');	//Adding a basic index

Fluent Query Builder

The Fluent Query Builder is Laravel's powerful fluent interface for building SQL queries and working with your database.

All queries use prepared statements and are protected against SQL injection.

You can begin a fluent query using the table method on the DB class. Just mention the table you wish to query

$query = DB::table('users');

Fluent Query Builder

Retrieving records

$users = DB::table('users')->get();
$user = DB::table('users')->first();
$user = DB::table('users')->find($id);
$email = DB::table('users')->where('id', '=', 1)->only('email');
$user = DB::table('users')->get(array('id', 'email as user_email'));
$user = DB::table('users')->distinct()->get();

Fluent Query Builder

Building Where Clauses

return DB::table('users')
    ->where('id', '=', 1)
    ->or_where('email', '=', 'example@gmail.com')
    ->first();
DB::table('users')->where_in('id', array(1, 2, 3))->get();

DB::table('users')->where_not_in('id', array(1, 2, 3))->get();
$users = DB::table('users')
    ->where('id', '=', 1)
    ->or_where(function($query)
    {
        $query->where('age', '>', 25);
        $query->where('votes', '>', 100);
    })
    ->get();

Fluent Query Builder

Table Joins

DB::table('users')
    ->join('phone', 'users.id', '=', 'phone.user_id')
    ->get(array('users.email', 'phone.number'));
DB::table('users')
    ->left_join('phone', 'users.id', '=', 'phone.user_id')
    ->get(array('users.email', 'phone.number'));

Fluent Query Builder

Ordering results

return DB::table('users')->order_by('email', 'desc')->get();
return DB::table('users')
    ->order_by('email', 'desc')
    ->order_by('name', 'asc')
    ->get();

Limit and Offset

return DB::table('users')->take(10)->get();
return DB::table('users')->skip(10)->get();

Fluent Query Builder

Aggregates

$min = DB::table('users')->min('age');

$max = DB::table('users')->max('weight');

$avg = DB::table('users')->avg('salary');

$sum = DB::table('users')->sum('votes');

$count = DB::table('users')->count();
$count = DB::table('users')->where('id', '>', 10)->count();

Fluent Query Builder

Insert - Update - Delete

DB::table('users')->insert(array('email' => 'example@gmail.com'));
$affected = DB::table('users')->update(array('email' => 'new_email@gmail.com'));
$affected = DB::table('users')->where('id', '=', 1)->delete();

Eloquent ORM

Eloquent is an ORM (Object-relational mapper) very easy to use


Define a simple model

class User extends Eloquent {}

Eloquent ORM

Conventions

  • Each table should have a primary key named id
  • Each table name should be the plural of its corresponding model name

But you can change it...

class User extends Eloquent {
	protected $table = 'my_users';
	protected $key = 'id_user';    
}

Eloquent ORM

Retrieving Models

Of course every method that is available through the Fluent Query Builder is available in Eloquent

$user = User::where('email', '=', $email)->first();

$user = User::where_email($email)->first();

$users = User::where_in('id', array(1, 2, 3))->or_where('email', '=', $email)->get();

$users = User::order_by('votes', 'desc')->take(10)->get();

Eloquent ORM

Aggregates

Of course every method that is available through the Fluent Query Builder is available in Eloquent

$min = User::min('id');

$max = User::max('id');

$avg = User::avg('id');

$sum = User::sum('id');

$count = User::count();
$count = User::where('id', '>', 10)->count();

Eloquent ORM

Inserting and Updating Models

Of course every method that is available through the Fluent Query Builder is available in Eloquent

$user = new User; //Insert
$user = User::find(1); // Or getting first for Update

$user->email = 'example@gmail.com';
$user->password = 'secret';

$user->save();
Another way to insert a new record
$user = User::create(array('email' => 'example@gmail.com'));

Eloquent ORM

created_at and updated_at

Whenver you save the model, the creation an update timestamps will be set automatically

class User extends Eloquent {
     public $timestamps = true;
}
You can update the update_at without saving the model
$comment = Comment::find(1);
$comment->touch();
Or
$comment = Comment::find(1);
$comment->timestamp();
//do something else here, but not modifying the $comment model data
$comment->save();

Eloquent ORM

Relationships

  • One-to-One
  • One-To-Many
  • Many-To-Many

Eloquent ORM

Relation One-To-One

class User extends Eloquent {

     public function phone()
     {
          return $this->has_one('Phone');
     }

}
Now execute
$phone = User::find(1)->phone()->first();
Two queries will be performed
SELECT * FROM "users" WHERE "id" = 1

SELECT * FROM "phones" WHERE "user_id" = 1

Eloquent ORM

Relation One-To-Many

class Post extends Eloquent {

     public function comments()
     {
          return $this->has_many('Comment');
     }

}
Now execute
$comments = Post::find(1)->comments()->get();

$comments = Post::find(1)->comments;
Two queries will be performed
SELECT * FROM "posts" WHERE "id" = 1

SELECT * FROM "comments" WHERE "post_id" = 1

Eloquent ORM

Relation Many-To-Many

This is the most complicated, for the example create 3 tables

users
id    - INTEGER
email - VARCHAR
roles
id   - INTEGER
name - VARCHAR
role_user
id      - INTEGER
user_id - INTEGER
role_id - INTEGER

Eloquent ORM

Relation Many-To-Many

class User extends Eloquent {

     public function roles()
     {
          return $this->has_many_and_belongs_to('Role');
     }

}
Now execute
$roles = User::find(1)->roles()->get();
Or
$roles = User::find(1)->roles;

Slim Framework

Composer

Eloquent

Twig

Twig

Twig is the flexible, fast and secure template language for PHP

Created by Fabien Potencier (Symfony)


http://twig.sensiolabs.org/

Twig

Example .html using Twig

<!DOCTYPE html>
<html>
<head>
<title>My Webpage</title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>

<h1>My Webpage</h1>
{{ a_variable }}
</body>
</html>

Twig

Variables

{{ foo.bar }}
{{ foo['bar'] }}
setting variables
{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}

Twig

Filters

The following example removes all HTML tags
{{ name|striptags|title }}
Example joining a list by commas
{{ list|join(', ') }}
Applying a list on a section of code
{% filter upper %}
  This text becomes uppercase
{% endfilter %}

Twig

Functions and Control structure

{% for i in range(low=1, high=10, step=2) %}
    {{ i }},
{% endfor %}
To display a list
<h1>Members</h1>
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
If structure control
{% if users|length > 0 %}
<ul>
{% for user in users %}
<li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}

Twig

Comments

{# note: disabled template because we no longer use this
    {% for user in users %}
        ...
    {% endfor %}
#}

Twig

Encoding and working with Dates

Encoding
{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}

{# versus #}

{{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}
Formating Dates with Timezones
{# both work #}
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
{{ "now"|date(timezone="Europe/Paris", 'd/m/Y H:i') }}

Twig

Template Inheritance

We can create a base "skeleton" template with all common elements
<!DOCTYPE html>
<html>
<head>
{% block head %}
<link rel="stylesheet" href="style.css" />
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
<body>
<div id="content">{% block content %}{% endblock %}</div>
<div id="footer">
{% block footer %}
&copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
{% endblock %}
</div>
</body>
</html>

Twig

Template Inheritance

We can create a base "skeleton" template with all common elements
{% extends "base.html" %}

{% block title %}Index{% endblock %}
{% block head %}
{{ parent() }}
<style type="text/css">
.important { color: #336699; }
</style>
{% endblock %}
{% block content %}
<h1>Index</h1>
<p class="important">
Welcome to my awesome homepage.
</p>
{% endblock %}

Twig

Macros

Useful to reuse often used HTML fragments to not repeat yourself
{% macro input(name, value = "", type = "text", size = 20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
{% endmacro %}
Import the macro
{% import "forms.html" as forms %}

<p>{{ forms.input('username') }}</p>

All Together

Example

Bootie Bowling Score

Program to see the score of Bootsoft teams

Database

Two tables in MySQL

users
id     - INTEGER
name   - VARCHAR
avatar - VARCHAR
team   - VARCHAR
scores
id      - INTEGER
round   - INTEGER
points  - INTEGER
user_id - INTEGER

Composer

We create the composer file in the root

{
    "name": "Bowling",    
    "require": {
        "php": ">=5.3.0",
        "slim/slim": "2.*",
        "slim/extras": "2.*",
        "twig/twig": "1.*",
        "dhorrigan/capsule": "*"
    }
}
In the console execute:
composer install

Index and .htaccess

.htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
index.php
require 'vendor/autoload.php';

$app = new \Slim\Slim();

$app->get('/', function () {
    echo "Hello Bowling!!";
});

$app->run();

Models

models/User.php
class User extends Illuminate\Database\Eloquent\Model {

	protected $table = "Users";
	public $timestamps = false;

	public function scores() {
		return $this->hasMany('Score');
	}    

}
models/Score.php
class Score extends Illuminate\Database\Eloquent\Model {
	
	protected $table = "Scores";
	public $timestamps = false;

}

Autoload Models

To autoload the models we have to add it to composer.json

{
    "name": "Bowling",    
    "require": {
        "php": ">=5.3.0",
        "slim/slim": "2.*",
        "slim/extras": "2.*",
        "twig/twig": "1.*",
        "dhorrigan/capsule": "*"
    },
    "autoload": {
        "classmap": [
            "models"
        ]
    }
}
In the console execute:
composer update

Setting up database

In the index.php add this code

// Make a new connection
$app->db = Capsule\Database\Connection::make('default', array(
    'driver'    => 'mysql',
    'host'      => 'localhost',
    'port'      => 3306,
    'database'  => 'bowling',
    'username'  => 'root',
    'password'  => '',
    'prefix'    => '',
    'charset'   => "utf8",
    'collation' => 'utf8_general_ci'    
), true);

Generate the scores

Off the record: I generate the Score rows randomly with this code

$app->get('/generate', function () {
    $users = User::all();
    foreach ($users as $user) {
        for ($i = 1; $i<=10; $i++) {
            $oScore = new Score();
            $oScore->round = $i;
            $oScore->points = rand(0,10);
            $oScore->user_id = $user->id;
            $oScore->save();
        }
    }
    echo "generated";
});

Routing

/users
$app->get('/users', function () {
    $users = User::all();
    echo $users->toJson();
});
/team/:team
$app->get('/team/:team', function ($team) {   
    $users = User::where('team', '=', $team)->get();    
    echo $users->toJson();
});
/user/:id
$app->get('/user/:id', function ($id) use ($app) {
    $user = User::with('scores')->where('id', '=', $id)->get();
    echo $user->toJson();    
        
    echo Score::where('user_id', '=', $id)->sum('points');
});

Twig

Now lets setup Twig in Slim

In the index change
$app = new \Slim\Slim();
By
// Setup custom Twig view
$twigView = new \Slim\Extras\Views\Twig();

$app = new \Slim\Slim(array(
    'debug' => true,
    'view' => $twigView,
    'templates.path' => 'templates/',
));

Templates

We are going to get Zurb foundation (CSS Framework) using compass

Execute in the console
compass create public -r zurb-foundation --using foundation

Templates

create base.html in templates

  <!-- Nav Bar --> 
<div class="row">
<div class="large-12 columns">
<div class="nav-bar right">
<ul class="button-group">
<li><a href="/team/TEAM_1" class="button">Team 1</a></li>
<li><a href="/team/TEAM_2" class="button">Team 2</a></li>
<li><a href="/team/TEAM_3" class="button">Team 3</a></li>
<li><a href="/team/TEAM_4" class="button">Team 4</a></li>
</ul>
</div>
<h1><a href="/">Bootsoft Bowling</a></h1>
<hr />
</div>
</div>
<!-- End Nav -->

{% block content %}{% endblock %}

Templates

create players.html in templates

{% extends "base.html" %}

{% block content %}

{% for user in users %}
<div class="row">
<div class="small-4 large-2 columns">
<img src="http://s3.amazonaws.com/37assets/svn/765-default-avatar.png" width="60"/>
</div>
<div class="small-4 large-6 columns">
<h2>{{ user.name|e }}</h2>
</div>
<div class="small-4 large-2 columns">
<h3>{{ user.score }}</h3>
</div>
</div>
{% endfor %}

{% endblock %}

Templates

Now at index.php change the code to

$app->get('/', function () use ($app) {
    $users = User::all();

    foreach ($users as $user) {
        $user->score = Score::where('user_id', '=', $user->id)->sum('points');
    }
    
    $app->render('players.html', array('users' => $users));
});

Some Links

Example

Get the Bootsoft Bowling example

https://github.com/revuls/Bootsoft-Bowling

Slim Framework

Official Website

http://www.slimframework.com/

PHP the Rigth Way

http://www.phptherightway.com/

Keeping it Small (Jeremy Kendall)

http://goo.gl/QanWR

Slim + Composer + Eloquent ORM (Phil Sturgeon)

http://goo.gl/o8OB2

Composer

Official Website

http://getcomposer.org/

Eloquent ORM

Laravel Framework

http://laravel.com/

Laravel Eloquent ORM

http://laravel.com/docs/database/eloquent

Twig

Official Website

http://twig.sensiolabs.org/

THE END

Thank you!

BY Cesar