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+

18 comments

  1. 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.

  2. 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.

  3. 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

  4. 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
    }
    }

  5. 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.

  6. 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

  7. 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

  8. 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() ?

  9. I like this simplistic way of managing the layouts and am using it in my site. However, one issue I have now is, the block works fine when there is static html code inside, but it fails when I m executing php inside it. Any ideas on how to handle this.

  10. JOE兄 我電郵給大為兄 他亦有回答 大為兄意見如下 並沒有任何修改 ***********************************偉亮兄 你好 謝謝轉載和轉達 還是要煩兄代為轉達 小弟對黃金並無特別bias 各國政府面對現況 確實除增加貨幣供應和開一些政府項目外 然後待經濟自然復甦 實別無良法 然而 單純認為現在的政府還會和幾十年前一樣一路狂印 又未免太小看他們了 由聯儲局的手法看 他們不會單純一路狂奔 而是睇餸食飯 所以金價的上升 也不致於一路狂奔 一兩年前 當時的政府尚有條件可以大幅印銀紙 於是乎黃金價格上升速度甚快 而現在既然各政府對通脹已經有所憂慮 印起來就住就住 金價就升得就住就住 同時 政府也怕通縮 結果黃金價格就會變成上上落落 雖然低息環境下 總體仍然是上升 但是速度不一樣了 至於賭金價因回復金本位而大升 以現況看 不敢苟同 金本位 甚至部份金本位 都會對各國財政調動和計劃帶來大量壓力 而且也肯定引發通縮 結果可以相當大鑊 主權國如果搞到內戰外亂破產 當然可以 問題又係一樣 必須要各國政策和經濟環境一成不變才成 但 so far, this is not the case 儘管方法有限 泵多少錢仍然是有彈性的 於是乎 金價變化 由根本來說 也同樣不會是一成變的狂升 現況計 黃金仍然是一個不錯的資產 不過 要期望它在今年內再次狂奔有點難 事關現時美債似乎有企穩的勢頭 如果今年內美債價格在聯儲局沒有干預下也能企穩 則要美國美債出事到避難黃金有點難 Thank you. Best regards,David Veni Vedi Vici!

Leave a Reply

Your email address will not be published.