bcen/ci-dispatcher

A CodeIgniter plugin for more restful controller

Installs: 62

Dependents: 0

Stars: 5

Watchers: 2

Forks: 1

Language: PHP

0.5.0 2012-11-05 16:36 UTC

README

Introduction

CodeIgniter-Dispatcher uses CodeIgniter's _remap function to do an extra routing to a class-based controller instead of function-based.

Example

CodeIgniter's function-based controller

<?php

class Welcome extends CI_Controller
{
    public function index()
    {
        $this->load->view('welcome_message');
    }
}

With CodeIgniter-Dispatcher's class-based controller

<?php

class Index extends \Dispatcher\DispatchableController
{
    protected $views = 'welcome_message';
}

Installtion

  1. Download the composer.phar executable or use the installer

    curl -s http://getcomposer.org/installer | php
  2. Add this to composer.json

    {
        "require": {
            "bcen/ci-dispatcher": "*"
        }
    }
  3. Run composer php composer.phar install

  4. Include autoload.php in your project and add this to routes.php

    \Dispatcher\Common\BootstrapInstaller::run($route);

    Dispatcher\BootstrapInstaller::run($route) will create 3 files inside of CodeIgniter's APPPATH, if you want to skip the file creation, add true to to skip the file check.

    \Dispatcher\Common\BootstrapInstaller::run($route, true);

Features

Poor man's dependency injection

Middlewares, DispatchableController and DispatchableResource will be injected by default.

// config/dependencies.php
$config['container']['userDao'] = function($container) {
    return new UserDao();
};


$config['container']['dsn'] = 'mydsn_string';
// sharedContainer will return the same instance throughout the request/response cycle
$config['sharedContainer']['pdo'] = function($container) {
    return new PDO($container['dsn']);
};

// user_status.php
<?php

class User_Status extends \Dispatcher\DispatchableController
{
    // CI-Dispatcher will inject $userDao from config/dependencies.php
    // for you.
    public function __construct($userDao)
    {
        $userDao->findUserById(1);
    }
}
Middlewares
<?php

class DebugFilter
{
    public function processRequest(Dispatcher\Http\HttpRequestInterface $req)
    {
        // do something
    }

    public function processResponse(Dispatcher\Http\HttpResponseInterface $res)
    {
        // do something
    }
}

processRequest and processResponse are optional, middleware can implement either one or both to alter the request/response cycle.

CodeIgniter Awareness

Any class that is created by the Dispatcher can implement CodeIgniterAware to have CI injection.

E.g.

<?php

// Controller
class User_Status extends \Dispatcher\DispatchableController implements \Dispatcher\Common\CodeIgniterAware
{
    public function __construct($userDao)
    {
        $userDao->findUserById(1);
    }

    public function setCI($ci)
    {
        $this->CI = $ci;
    }
}

// Middleware
class DebugFilter implements \Dispatcher\Common\CodeIgniterAware
{
    public function processRequest(Dispatcher\Http\HttpRequestInterface $req)
    {
        $cipher = $this->CI->encrypt->encode('plaintext', 'key');
    }

    public function processResponse(Dispatcher\Http\HttpResponseInterface $res)
    {
        // do something
    }

    public function setCI($ci)
    {
        $this->CI = $ci;
    }
}

Configurations

There are two configuration files, config/dispatcher.php and config/dependencies.php.

dispatcher.php
<?php

$config['middlewares'] = array(
    'MyProject\\Namespace\\Middlewares\\SomeFilter',
    'debug_filter'
);

$config['debug'] = true;

debug:
Whether to show or hide debug information.
Set true to show exception, false to return error 404 response when stuff gone wrong.

middlewares:
An array of middleware class(es) to be processed before/after dispatch.
When specifying the middlewares, it can be a fully qualified class name if it is autoloaded, otherwise the class must live under application/middlewares/ in order for CI-Dispatcher to load it (Note: naming convention applies).

dependencies.php

This configuration file is used for DIContainer to load dependencies and inject them into Middlewares, DispatchableController and DispatchableResource's constructor.
Note: DIContainer is a copy cat of Pimple.

<?php

$config['container'] = array();
$config['sharedContainer'] = array();


$config['container']['dsnString'] = 'dsn:user@192.168.1.100';
$config['container']['userDao'] = function($c) {
    return new UserDao($c['dsnString']);
};

Note:
container can have anonymous function or simple value like string, array, etc...
sharedContainer must contian only anonymous function.

Conventions

URL to Controller mappings

URL mapping convention follows almost excatly like CodeIgniter's default strategy.

E.g.
http://domain.com/ maps to application/controllers/index.php with the class name Index
http://domain.com/about_me maps to application/controllers/about_me.php with the class name About_Me

Directory nesting:

http://domain.com/blog and http://domain.com/blog/ajax/fetch_all_posts can be mapped to:

controllers
|
+-- blog
    |-- index.php
    +-- ajax
        |
        +-- fetch_all_posts.php

Mapping strategy:

CI-Dispatcher will search through each URI segments for the exact file name under application/controllers. If it doesn't exists, it will search index.php under that URI segments.

E.g.
http://domain.com/blog has the URI segment: blog.
First CI-Dispatcher will search for application/controllers/blog.php.
If it doesn't exists, then it will try for application/controllers/blog/index.php.

URI variable:

Sometime URI segments are not fixed, thus we cannot mapped to a directory or class. However, we can mapp it to the function arguments of the request handler.

E.g. http://domain.com/blog/posts/this-is-a-crayz-blog-post-about-my-blog/ can be mapped to application/controllers/blog/posts.php with the follow class:

<?php

class Posts extends \Dispatcher\DispatchableController
{
    protected $views = array('header', 'post_body', 'footer');

    // request handler for GET
    public function get($request, $slug)
    {
        $post = $posts->find($slug);
        return $this->renderView(array('post' => $post);
    }

    // request handler for POST
    // providing a default value means that that URI segment is optional.
    // e.g. POST http://domain.com/blog/posts/some-slug
    // or POST http://domain.com/blog/posts
    public function post($request, $slug = null)
    {
        // do something and return response
    }
}

Note: The request handler must accept at least one argument for the Request object.

Middlewares

If middlewares are not autoloaded by class loader like composer, they must live under application/middlewares.

E.g.

$config['middlewares'] => array('debug_filter', 'auth_filter');

debug_filter will be mapped to application/middlewares/debug_filter.php with the class name Debug_Filter.
auth_filter will be mapped to application/middlewares/auth_filter.php with the class name Auth_Filter.

Controller Explained

Any class lives under application/controllers/ and extends Dispatcher\DispatchableController will be created and injected by the CI-Dispatcher.

E.g. Assumes we are sending a GET request to http://domain.com/blog/posts/special-post.

  • CI-Dispatcher will map the URI to application/blog/posts.php
  • Creates a new instance of Posts
  • Invokes the method get with arguments: Dispatcher\Http\HttpRequestInterface and 'special-post'
  • After getting the response object from get, then CI-Dispatcher will run through all the middlewares to process the response
  • Finally, send it to browser by invoking send on the response object
At minimal
<?php

class Posts extends \Dispatcher\DispatchableController
{
    protected $views = 'post_view';
}

By default, a GET request handler is provided, when get got invoked, it will load the views and data.
So when extending the DispatchableController, you only need to provide where the view files are by declaring $views = 'post_view' or as an array $views = array('header', 'post_view', 'footer')

Overrides getContextData to send data to the view layer:

<?php

class Posts extends \Dispatcher\DispatchableController
{
    protected $views = 'post_view';

    public function getContextData($request)
    {
        $posts = array();
        return array('posts' => $posts);
    }
}

Overrides/implements get, post, delete, put and etc to take complete control of the request/response handling.

<?php

class Posts extends \Dispatcher\DispatchableController
{
    protected $views = 'post_view';

    public function get($request)
    {
        // returns a ViewTemplateResponse
        return $this->renderView(array('post' => $posts));
    }
}

Tests

$ composer.phar install --dev
$ vendor/bin/phpunit

License

CI-Dispatcher is released under the MIT public license.