18Sep/1212

Using layout pattern with CodeIgniter

CodeIgniter is great framework by its simplicity. But when I moved from CakePHP, I really missed layout pattern.

CodeIgniter documentations offers this way to include non-changing site header and footer

$this->load->view('header');
$this->load->view('template'); 
$this->load->view('footer');

For sure it isn't flexible and does not show page structure in a clear way. For many years I use layout pattern. Layout describes whole page as a template with blocks for header, menu, content, etc like on figure below. On page rendering these blocks are filled with data.

I found a small code snipped for CI and improved it. You are welcome to try!

Lets start from simple usage example

Simple usage of Layout

1. Download and put Layout.php into application/libraries folder.

2. Create new default layout in views/layout/default.php with content:

<html>
<head>
  <title><?=$title_for_layout?></title>
</head>
<body>
  <?=$content_for_layout?>
</body>
</html>

3. Create controller

class Main extends CI_Controller {
    // Layout used in this controller
    public $layout_view = 'layout/default';
 
    public function index() {
       $this->load->library('layout');          // Load layout library
       $this->layout->title('Site index page'); // Set page title
       $data = array();
       $this->layout->view('index', $data);     // Render view and layout
    }
}

Simple. All data passed to the template in $data is also passed to the layout template.

Handling JS & CSS

This is first area of improvement.

I added code to include most used external resources like Javascript and CSS files. This way its possible to make optimizations later like stripping/minimizing content or combining into one file.

class Main extends CI_Controller {
   public $layout_view = 'layout/default';
 
   public function index() {
      // Page local resource
      $this->layout->js('js/index.js');
      $this->layout->css('css/index.css');
      $this->layout->title('Page title');
      $this->layout->view('index');
   }
}

Use this for layout/default.php to include resources. It contains two variable $css_for_layout and $js_for_layout

<html>
<head>
   <title><?=$title_for_layout?></title>
   <?=$css_for_layout?>
   <?=$js_for_layout?>
</head>
<body>
  <?=$content_for_layout?>
</body>
</html>

Globalizing

CI offers a way to create a parent class for all controllers in the project. Its a very good place to put site global code.

For example it is not convinient to specify default template in each controller file since sites usually have only one template. Same is related to resources like JS and CSS, some of them are global and must be included in each sit epage.

Lets create file in application/core/MY_Controller.php with content

class <strong>MY_Controller</strong> extends CI_Controller  {
   // Site global layout
   public $layout_view = 'layout/default';
 
   function __construct() {
      parent::__construct();
      // Layout library loaded site wide
      $this->load->library('layout'); 
 
      // Site global resources
      $this->layout->js('js/jquery.min.js');
      $this->layout->css('css/site.css');
   }
}

Next is the controller class itself

class Main extends MY_Controller {
   // Layout is not specified here, it is inherited from MY_Controller
 
   function __construct() {
      parent::__construct();
      // Local resource used for all pages served by this controller
      $this->layout->js('js/jlib.min.js');
      $this->layout->css('css/product.css');
   }
 
   public function index() {
      // Page specific resource
      $this->layout->js('js/lib2.js');
      $this->layout->css('css/page.css');
 
      $this->layout->title('Page title');
      $this->layout->view('index');
   }
 
   // This page uses different layout
   public function page2() {
      $this->layout_view = 'layout/short.php';
      $this->layout->title('Page title');
      $this->layout->view('index');
   }
}

Template inheritace like in Twig in CodeIgniter

Next area of improvement.

Twig is a popular template engine available for PHP and it has a very nice feature called template inheritance. I personally do not see any practical usage of any template engines except PHP itself, but I love this feature and incorporated it here.

Where to use it? Example: You want to implement breadcrumbs and they depends on what is current page.

In your layout make a space for it anywhere with block function

<html>
<head>
 <title><?=$title_for_layout?></title>
 <?=$css_for_layout?>
 <?=$js_for_layout?>
</head>
<body>
  <?$this->layout->block('breadcrumbs')?>
  Site |
  <?$this->layout->block()?>
  <?=$content_for_layout?>
</body>
</html>

In page template include block with same name and it would be replaced in layout with content generated in template

<?$this->layout->block('breadcrumbs')?>
Breadcrumbs for this page
<?$this->layout->block()?>
Page content!

Full source code

<?php
/**
 * CodeIgnighter layout support library
 *  with Twig like inheritance blocks
 *
 * v 1.0
 *
 *
 * @author Constantin Bosneaga
 * @email  constantin@bosneaga.com
 * @url    http://a32.me/
 */
 
if (!defined('BASEPATH')) exit('No direct script access allowed');
 
class Layout {
    private $obj;
    private $layout_view;
    private $title = '';
    private $css_list = array(), $js_list = array();
    private $block_list, $block_new, $block_replace = false;
 
    function Layout() {
        $this->obj =& get_instance();
        $this->layout_view = "layout/default.php";
        // Grab layout from called controller
        if (isset($this->obj->layout_view)) $this->layout_view = $this->obj->layout_view;
    }
 
    function view($view, $data = null, $return = false) {
        // Render template
        $data['content_for_layout'] = $this->obj->load->view($view, $data, true);
        $data['title_for_layout'] = $this->title;
 
        // Render resources
        $data['js_for_layout'] = '';
        foreach ($this->js_list as $v)
            $data['js_for_layout'] .= sprintf('<script type="text/javascript" src="%s"></script>', $v);
 
        $data['css_for_layout'] = '';
        foreach ($this->css_list as $v)
            $data['css_for_layout'] .= sprintf('<link rel="stylesheet" type="text/css"  href="%s" />', $v);
 
        // Render template
        $this->block_replace = true;
        $output = $this->obj->load->view($this->layout_view, $data, $return);
 
        return $output;
    }
 
    /**
     * Set page title
     *
     * @param $title
     */
    function title($title) {
        $this->title = $title;
    }
 
    /**
     * Adds Javascript resource to current page
     * @param $item
     */
    function js($item) {
        $this->js_list[] = $item;
    }
 
    /**
     * Adds CSS resource to current page
     * @param $item
     */
    function css($item) {
        $this->css_list[] = $item;
    }
 
    /**
     * Twig like template inheritance
     *
     * @param string $name
     */
    function block($name = '') {
        if ($name != '') {
            $this->block_new = $name;
            ob_start();
        } else {
            if ($this->block_replace) {
                // If block was overriden in template, replace it in layout
                if (!empty($this->block_list[$this->block_new])) {
                    ob_end_clean();
                    echo $this->block_list[$this->block_new];
                }
            } else {
                $this->block_list[$this->block_new] = ob_get_clean();
            }
        }
    }
 
}

Download Layout.php

Comments are very welcome!

Did you find this post useful? Support the the author ($10)
My Google Profile+
Comments (12) Trackbacks (1)
  1. Why are you using short open tags? Are those preferred for codeigniter templates?

  2. Short tags are much easier to read. Compare these two lines
    < ?=$var?>
    < ?php echo $var ?>

    I always use them in templates

  3. I remember reading somewhere that short opening tags were deprecated and their use was discouraged. I looked into this further and it turns out that while <? is indeed on the way out from php 5.4 upwards <?= is not. So I guess it's ok to use them.

  4. Great post. Compliments and thanks for sharing. CI is always intuitive e fast to understand and this post help me to know another aspect of this interesting framework.

    Thanks again.

  5. Hi,
    when I use this code:
    class Main extends CI_Controller {
    public $layout_view = ‘layout/default';

    public function index() {
    // Page local resource
    $this->layout->js(‘js/index.js’);
    $this->layout->css(‘css/index.css’);
    $this->layout->title(‘Page title’);
    $this->layout->view(‘index’);
    }
    }
    have in browser:

    An Error Was Encountered

    Unable to load the requested file: index.php
    Why?
    I use HMVC and hold template file in layout/default/default.php

  6. Sorry, this is correct code:
    class Blog extends CI_Controller {
    // Layout used in this controller
    public $layout_view = ‘../../layout/default/default';

    public function index() {
    $this->load->library(‘layout’); // Load layout library
    $this->layout->title(‘Site index page’); // Set page title
    $data = array();
    $this->layout->view(‘index’, $data); // Render view and layout
    }
    }

  7. index.php is page template, it must exists in views/index.php

  8. Hello Dear,

    Really it is a great article. I did not find such a great article till now. It is really very much helpful. Keep doing the great work.

  9. Hi
    I want to add layout controller bases and that layout is work on all functions which is added on that controller pls reply asap

  10. That’s a nice Layout class you’ve developed.
    I think there is an issue when setting new layout_view from the action. I hope that code below fixes it.

    Regards

    function Layout()
    {
    $this->obj = & get_instance();
    $this->layout_view = "layout/default.php";
    // Grab layout from called controller
    }

    function view($view, $data = null, $return = false)
    {
    if (isset($this->obj->layout_view))
    $this->layout_view = $this->obj->layout_view; // Render template

  11. Hi Colud you please tell me how your Block feature works?
    I tried it but nothing is showing in my layout…

    What exactly doing in the block() ?

  12. Hi
    Can you give an example how to use block ? Such as use Breadcrumbs


Leave a comment

CodeIgniter: Layout | Java vs C#