EllisLab text mark
Advanced Search
     
Layouts and actions in CI
Posted: 15 March 2010 06:41 PM
Joined: 2010-03-15
3 posts

To include header, footer and/or sidebar in every view is a really boring practice.

To define a single layout with all the common parts once and then to define the singles action-specific views with just the code that changes sounds better grin

To do it in CI I’ve used this simple approach.

A) I define a “layout” with all the common parts:

view/site_layout.php:

<html>
<
head>
<
title>Welcome to CodeIgniter</title>

<
style type="text/css">
/*...*/
</style>
</
head>
<
body>

<?php echo $action?>

<p><br />Page rendered in {elapsed_time} seconds</p>

</
body>
</
html

 

b) I define an “action” with the action-specific code.

views/welcome_message.php:

<h1>Welcome to CodeIgniter!</h1>

<
p>The page you are looking at is being generated dynamically by CodeIgniter.</p>

<
p>If you would like to edit this page you'll find it located at:</p>
<code>system/application/views/welcome_message.php</code>

<p>The corresponding controller for this page is found at:</p>
<code>system/application/controllers/welcome.php</code>

<p>If you are exploring CodeIgniter for the very first time, you should start by reading the <a href="user_guide/">User Guide</a>.</p> 

c) Then I load/set the layout in the constructor (or in any other place in the Controller):

controllers/welcome.php:

class Welcome extends Controller {

    
function Welcome()
    
{
        parent
::Controller();
        
$this->load->layout('site_layout');
    
}
    
    
function index()
    
{
        $this
->load->view('welcome_message');
    
}

d) And finally here we have the library that make the things happen:

libraries/my_loader.php:

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

class 
MY_Loader extends CI_Loader {
    
    
var $_layout;

    function 
layout($layout{

        $this
->_layout $layout;
        return 
$this;

    
}

    
function view($view$vars = array(), $return FALSE)
    
{
        
if (isset($this->_layout)) {
            $this
->_ci_cached_vars['action'$this->_ci_load(array('_ci_view' => $view'_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => true));
            return 
$this->_ci_load(array('_ci_view' => $this->_layout'_ci_vars' => array(), '_ci_return' => $return));

        
else {
            
return $this->_ci_load(array('_ci_view' => $view'_ci_vars' => $this->_ci_object_to_array($vars), '_ci_return' => $return));

        
}
    }

With this library you can load your views in the classic CI way, but you can also define a layout for your views.

This is my first contribute to CI. What do you think about it? raspberry

 
Posted: 15 July 2010 03:38 AM   [ # 1 ]   [ Rating: 0 ]
Joined: 2008-01-03
4 posts

great work wink
thank you so much

 
Posted: 15 July 2010 04:22 AM   [ # 2 ]   [ Rating: 0 ]
Avatar
Joined: 2010-07-15
91 posts

You could’ve done it this way:

first make a custom controller that will extend the default Controller.

example (in this sample, i use this to check for ion auth sessions but you can basically include the “common parts” in the constructor). place this on your application/libraries folder:

<?php
  
class Protected_Page_Controller extends Controller
  {
    
public function __construct()
    
{
      parent
::__construct();

      if(!
$this->ion_auth->logged_in())
        
redirect("auth/login""refresh");
    
}
  }
?> 

then for each controller that will need the “common parts”, you simply have to let them extend the name of your library instead of the usual controller. like this:

<?php if(!defined("BASEPATH")) exit("No direct script access allowed");
  class 
Admin extends Protected_Page_Controller
  {
    
public function __construct()
    
{
      parent
::__construct();
    
}

    
public function index()
    
{
      $this
->template->add_css("styles/page.css");
      
$this->template->add_css("styles/jui.css");
      
$this->template->add_css("styles/custom.css");
      
$this->template->add_js("scripts/jquery-1.4.2.min.js");
      
$this->template->add_js("scripts/jquery.dataTables.min.js");
      
$this->template->write("title""Body Match");
      
$this->template->write_view("header""header");
      
$this->template->write_view("content""admin/content_admin");
      
$this->template->write_view("footer""footer");
      
$this->template->render();
    
}
  }
?> 

there! it’s actually that easy. but you need one final thing, the magic method autoload, in order to autoload the custom library to make the “extend” work. add the code below on the bottom of your “config.php”:

/*
| -------------------------------------------------------------------
|  Native Auto-load
| -------------------------------------------------------------------

| Nothing to do with cnfig/autoload.php, this allows PHP autoload to work
| for base controllers and some third-party libraries.
|
*/
function __autoload($class)
{
  
if(strpos($class'CI_') !== 0)
  
{
    
@include_once( APPPATH 'libraries/'$class EXT );
  
}

after that, you’re done! *my example is not really that explicative with what you are trying to achieve but I hope you got my point on how to apply them for the “common parts” thing you were pointing out.

 Signature 

Ignited Datatables

 
Posted: 18 July 2010 10:36 AM   [ # 3 ]   [ Rating: 0 ]
Joined: 2010-03-15
3 posts

thank you aamir grin
I’m happy that my snippet is useful for someone

@cryogenix: if I do not misunderstand your post, you didn’t remove repetition of code, you just moved it from the views to the controllers.

Another advantage of my approach is that the code in the view is executed before the code in the layout. So the view can easily set vars to be used in the layout. For example in welcome_message we can have:

$this->load->vars(‘title’, ‘The title of this action’)

And in the layout:

...
<title><?php echo isset($title)? $title: “A default title here” ?></title>
...

An other useful think we can do in the view is to set something like:

$this->style[] = base_url() . ‘css/section.css’;
$this->js[] = base_url() . ‘js/section.js’;

Or using a specific library:

$this->template->add_css(...);
$this->template->add_js(...);

An so adding styles and javascripts in the layout from the view.

 
Posted: 20 July 2010 08:31 PM   [ # 4 ]   [ Rating: 0 ]
Avatar
Joined: 2010-07-15
91 posts

I see you missed my point. Well let me give you an example where I don’t use the template library since that one is out of the picture with what I am actually pointing out but will then be necessary later on as I will explain.

Let’s say you want to remove repetition of code for your header and footer on a default page since both of them are static views anyway and won’t change no matter what. I’ll do it like this:

<?php
  
class Default_Page_Controller extends Controller
  {
    
public function __construct()
    
{
      parent
::__construct();

      
$this->load->view("default_header");
      
$this->load->view("default_footer");
    
}
  }
?> 

I will then place this in my libraries folder (and also add the magic function on the config which I mentioned on my above post) and just let it be extended by the controller of whatever page that needs the default header and default footer.

Say for example the index page:

<?php if(!defined("BASEPATH")) exit("No direct script access allowed");
  class 
Welcome extends Default_Page_Controller
  {
    
public function __construct()
    
{
      parent
::__construct();
    
}

    
public function index()
    
{
      $this
->load->view("content_welcome");
    
}
  }
?> 

and also maybe a signup page will need the default header and footer as well:

<?php if(!defined("BASEPATH")) exit("No direct script access allowed");
  class 
Signup extends Default_Page_Controller
  {
    
public function __construct()
    
{
      parent
::__construct();
    
}

    
public function index()
    
{
      $this
->load->view("content_signup");
    
}
  }
?> 

and etc. as you can see, i only wrote the definition of the header and footer once. and just reused that definition (as a library) by extending it on the controllers that needs it.

now there will be a flaw in the sequence of view loading since the footer was called first than the content view. and that’s where a template library comes in. i hope you got it so far by this point. i will be waiting for your reply and see how it turns out…

 Signature 

Ignited Datatables

 
Posted: 19 November 2010 01:15 PM   [ # 5 ]   [ Rating: 0 ]
Joined: 2010-03-15
3 posts

Hi, sorry for the late :D

>> and that’s where a template library comes in.
>> i hope you got it so far by this point

Exactly. Based on your example, just edit your Default_Page_Controller like so:

class Default_Page_Controller extends Controller
  {
    
public function __construct()
    
{
      parent
::__construct();

      
//$this->load->view("default_header");
      //$this->load->view("default_footer");
      
$this->load->layout("my_layout");
    
}
  } 

In this way the layout will rendered *after* the code in the action, so the action can set some template vars such as metas, title, stylesheets to be included and so on.

This is a simple implementation of the Decorator Pattern, commonly used in the view part of frameworks like Symfony or Zend grin