Friday, April 17, 2015

Using View Layouts in CodeIgniter

I'll show you how tp extend CodeIgniter's loader to enable layout views.

CodeIgniter doesn't use a template engine. I think their argument that PHP is already a templating language is acceptable for small projects - my team is just me, templates would just add to project complexity and maybe impact performance.

But I do miss the concept of layouts. Instead, codeigniter uses this idiom:

    $this->load->view('templates/header', $data);
    $this->load->view('pages/'.$page, $data);
    $this->load->view('templates/footer', $data);

That's ugly. As your site grows, and you need to sync css to javascript libraries between headers and footers, this results in maintenance pains. I'd prefer to embed my content in a layout:

  <html>
    <head>
        <title>Hello</title>
    </head>
    <body>
        <h1>Hey!</h1>
        <div class="content">
        <?php echo $content; ?>
        </div>
        <hr />
        <em>© 2014</em>
    </body>
</html>

And then invoke like this ...

    $this->load->setLayout('layouts/main');
    $this->load->view('pages/'.$page, $data);


Well, it's pretty simple to extend CodeIgniter to do this by customizing the view load behavior. First, we'll create the class ./application/core/MY_Loader.php (MY_ is our customization namespace), and add a couple member variables:

    class MY_Loader extends CI_Loader {

        var $layout = '';
        const EXT = '.phtml';

    }

The $this->layout variable holds the file path to the layout. I am also using a different file extension for my views. This keeps me mindful not to burden my views with too much logic. It's too easy to get mixed up when with the views and code have the same file extension.

 Now the constructor:

    function __construct() {
        parent::__construct();
        
        $layout = config_item('layout');
        if ($layout <> '') {
            $this->layout = $layout.self::EXT;
        }
    }    

Make sure to call the core class constructor, and fetch a default layout value from the site configuration (we'll see how to add that below). We'll add a convenience method to set the layout manually:

    function setLayout($layout) {
        $this->layout = $layout.self::EXT;
    }

And finally, we override the view method of loader:

    function view($view = '' , $view_data = array(), $return = FALSE) {  
        if ($view <> '') {
            $view = $view.self::EXT;
        }
        if ($this->layout <> '') {
            $view_data['content'] = parent::view($view, $view_data, TRUE);
            return parent::view($this->layout, $view_data, $return);
        } else {
            return parent::view($view, $view_data, $return);
        }
    }

If there is no layout, we use the standard process. Otherwise, we first merge the view and data to create partial content, and them inject that into the layout.

Remember the configuration? We add the following line to the end of ./application/config/config.php:

$config['layout'] = 'layouts/default';

Of course, we also have to create our default layout: views/layouts/default.phtml - I'll leave that up to you.

 You can find more about extending codeigniter at http://www.codeigniter.com/user_guide/general/core_classes.html

No comments:

Post a Comment