Getting Started
In this tutorial, we will build a new application from scratch using SnappyRouter.
The full tutorial application can be found here.
Creating the Project Structure
We begin by creating the project folder and recommended subfolders.
$> mkdir tutorial tutorial/app tutorial/public
$> mkdir tutorial/app/Controllers tutorial/app/Views tutorial/app/Views/index tutorial/app/Models
$> cd tutorial
The folder structure should look like this:
tutorial/
app/
Controllers/
Models/
Views/
index/
public/
Redirects, Composer and index.php
We will use .htaccess files to redirect all incoming requests to a single entry
point in our application (public/index.php
).
Create the following files:
#/tutorial/.htaccess
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule ^$ public/ [L]
RewriteRule (.*) public/$1 [L]
</IfModule>
#/tutorial/public/.htaccess
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
We will also make use of Composer to provide dependencies and to autoload our application classes. If you do not have Composer installed, follow the documentation at getcomposer.org.
Create the file tutorial/composer.json
with the following contents:
{
"name": "vectorface/snappy-tutorial",
"autoload": {
"psr-4": {
"Vectorface\\SnappyTutorial\\": "./app"
}
},
"require": {
"php": ">=5.3.0",
"vectorface/snappy-router": "dev-master"
}
}
and run
$> composer install
and finally the contents of public/index.php
.
<?php // public/index.php
require_once __DIR__.'/../vendor/autoload.php';
use Vectorface\SnappyRouter\Config\Config;
use Vectorface\SnappyRouter\Handler\ControllerHandler;
$config = new Config(array(
Config::KEY_DI => 'Vectorface\\SnappyTutorial\\Models\\TutorialDi',
Config::KEY_HANDLERS => array(
'PageHandler' => array(
Config::KEY_CLASS => 'Vectorface\\SnappyRouter\\Handler\\ControllerHandler',
Config::KEY_OPTIONS => array(
Config::KEY_NAMESPACES => 'Vectorface\\SnappyTutorial\\Controllers',
ControllerHandler::KEY_BASE_PATH => '/tutorial',
ControllerHandler::KEY_VIEWS => array(
ControllerHandler::KEY_VIEWS_PATH => realpath(__DIR__.'/../app/Views')
)
)
)
)
));
$router = new Vectorface\SnappyRouter\SnappyRouter($config);
echo $router->handleRoute();
For simplicity we include the configuration settings directly in
public/index.php
. It is probably a better practice to store these settings
in a separate config file and include it in the index. For example:
...
$configArray = require_once __DIR__.'/../app/config.php';
$config = new Config($configArray);
...
N.B. Any file placed in the public folder will be directly accessible through the web browser. This folder should be used for any web assets (javascript, images, css, fonts) or direct PHP scripts you wish to expose. Any script exposed through the public folder will not be run through SnappyRouter.
Setting up the DI Container
Dependency injection (DI) is a powerful tool for injecting services and dependencies across your application at runtime. Some common examples include the database adapter, cache adapters, mail senders, etc.
For this tutorial, we specify a class to use for DI. Create the file
app/Models/TutorialDi.php
with the following contents:
<?php // app/Models/TutorialDi.php
namespace Vectorface\SnappyTutorial\Models;
use Vectorface\SnappyRouter\Di\Di;
class TutorialDi extends Di
{
public function __construct()
{
parent::__construct($this->getDiArray());
}
protected function getDiArray()
{
return array(
'projectTitle' => function(Di $di) {
return 'SnappyRouter Tutorial';
}
);
}
}
This container registers only the projectTitle
key.
Controllers and Views
We will setup an IndexController
that extends our own abstract controller.
It is good practice to always include your own base controller on top of
Vectorface\SnappyRouter\Controller\AbstractController
to provide common logic
across all your controllers.
The BaseController
implements the initialize
method which is invoked by
SnappyRouter before any action is invoked. Note that we retrieve the
projectTitle
from the DI layer and hand it off to the view.
<?php // app/Controllers/BaseController.php
namespace Vectorface\SnappyTutorial\Controllers;
use Vectorface\SnappyRouter\Controller\AbstractController;
use Vectorface\SnappyRouter\Handler\AbstractRequestHandler;
use Vectorface\SnappyRouter\Request\HttpRequest;
abstract class BaseController extends AbstractController
{
public function initialize(HttpRequest $request, AbstractRequestHandler $handler)
{
parent::initialize($request, $handler);
$this->viewContext['projectTitle'] = $this->get('projectTitle');
}
}
And the IndexController
:
<?php // app/Controllers/IndexController.php
namespace Vectorface\SnappyTutorial\Controllers;
class IndexController extends BaseController
{
public function indexAction()
{
return array(
'content' => 'Hello SnappyRouter!'
);
}
}
Note that there are many ways to pass variables to the view.
- Using the associative array
$this->viewContext
provided byVectorface\SnappyRouter\Controller\AbstractController
. - Returning an associative array (this array will be merged with
$this->viewContext
). - Directly rendering the view with
$this->renderView
. More details for this method can be found here.
We will divide our view into two files. The first file app/Views/layout.twig
will
provide common boilerplate that we could reuse across multiple pages.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ projectTitle|e }}</title>
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
{% block content %}
{% endblock %}
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
</body>
</html>
And a very simple view for our indexAction
in app/Views/index/index.twig
:
{% extends 'layout.twig' %}
{% block content %}
<div class="container">
<h1>{{ content }}</h1>
</div>
{% endblock %}
Once you add the tutorial
folder to your standard web root, you should have
a working application at http://localhost/tutorial/
.