views:

1485

answers:

4

Best practices: What's the best way for constructing headers and footers? Call it all from the controller, or include from the view file?

I'm using Codeigniter. What's best practice? Loading all the included view files from the controller, like this?

class Page extends Controller {

   function index()
   {
      $data['page_title'] = 'Your title';
      $this->load->view('header');
      $this->load->view('menu');
      $this->load->view('content', $data);
      $this->load->view('footer');
   }

}

or calling the single view file and calling the header and footer views from there, like this:

//controller file    
class Page extends Controller {

   function index()
   {
      $data['page_title'] = 'Your title';
      $this->load->view('content', $data);

   }

}

//view file

<?php $this->load->view('header'); ?>

<p>The data from the controller</p>

<?php $this->load->view('footer'); ?>

I've seen it done both ways, but want to choose now before I go too far down a path and screw myself.

Cheers. jon

+2  A: 

It's bad practice to call views inside of other views. This could be a form of controller view mixing. The view function in CI allows you to pass a third parameter that causes it to return that views output as a string. You can use this to create a compound view.

For example:

class Page extends Controller {
   function index() {
      $data['page_title'] = 'Your title';

      $this->load->view('default_layout', array(
         'header'  => $this->load->view('header' , array(), true), 
         'menu'    => $this->load->view('menu'   , array(), true), 
         'content' => $this->load->view('content', $data  , true), 
         'footer'  => $this->load->view('footer' , array(), true), 
      ));
   }
}

default_layout.php

<? echo $header, $menu, $content, $footer; ?>

You may want to combine your header and footer to make a template like this.

class Page extends Controller {
   function index() {
      $data['page_title'] = 'Your title';

      $this->load->view('default_template', array(
         'menu'    => $this->load->view('menu'   , array(), true), 
         'content' => $this->load->view('content', $data  , true), 
      ));
   }
}

default_template.php

<html><head></head><body><span>Some Header HTML</span> // this is your header html
<? echo $menu, $content; ?>
<span>some footer HTML</span></body></html>  // this is your footer html
gradbot
+3  A: 

I think the first way you are doing it is cleaner. Simply from a point of view of knowing that is going to be rendered. Rather than having to enter the view file to find the rest.

Jack B Nimble
+4  A: 

You could also try it this way -- define a default view template, which then pulls in the content based on a variable ('content' in my example) passed by the controller.

In your controller:

$data['content'] = 'your_controller/index';

// more code...

$this->load->vars($data);
$this->load->view('layouts/default');

Then define a default layout for all pages e.g. views/layouts/default.php

// doctype, header html etc.

<div id="content">
    <?= $this->load->view($content) ?>
</div>

// footer html etc.

Then your views can just contain the pure content e.g. views/your_controller/index.php might contain just the variables passed from the controller / data array

<?= $archives_table ?>
<?= $pagination ?>
// etc.

More details on the CI wiki/FAQ -- (Q. How do I embed views within views? Nested templates?...)

meleyal
My favourite thing about this approach is that I can get rid of all those errors in my IDE about missing end tags and missing start tags in the header and footer fragments. Nice!
Don Kirkby
+6  A: 

Actually, after researching this quite a bit myself, I came to the conclusion that the best practice for including headers and footers in MVC is a third option - namely extending a base controller. That will give you a little more flexibility than htxt's suggestion, particularly if you're building a very modular layout (not just header and footer, also sidebar panels, non-static menus, etc.).

First, define a Base_controller class, in which you create methods that append your page elements (header, footer, etc.) to an output string:

class Base_controller extends Controller
{
    var $_output = '';

    function _standard_header($data=null)
    {
        if (empty($data))
            $data = ...; // set default data for standard header here

        $this->_output .= $this->load->view('header', $data, true);
    }

    function _admin_header($data=null)
    {
        if (empty($data))
            $data = ...; // set default data for expanded header here

        $this->_output .= $this->load->view('admin_header', $data, true);
    }

    function _standard_page($data)
    {
        $this->_standard_header();
        $this->_output .=
            $this->load->view('standard_content', $data, true);
        echo $this->_output; // note: place the echo statement in a
                             // separate function for added flexibility
    }

    function _page_with_admin_header($data)
    {
        $this->_admin_header($data);
        $this->_output .=
            $this->load->view('standard_content', $data, true);
        echo $this->_output;
    }
}

Then, in your page controllers, simply extend the base class and call your functions to build the page.

class Page_controller extends Base_controller
{
    function index()
    {
        $data = ...; // Set content data here
        $this->_standard_page($data);
    }

    function admin()
    {
        $data = ...; // Set content and header data here
        $this->_page_with_admin_header($data);
    }
}

Using a base controller, you can achieve very clean code in your individual page controllers AND have separate views for elements on the page (allowing code reuse in both views and controllers). All you need to do is define your common page 'sections' (what you might be tempted to call 'fragments') as functions in your base controller.

And if the base controller should start to grow uncontrollably (which can happen on large sites), you can rearrange some of its less-general functions by placing them in subclasses, and let the corresponding page controllers extend those instead of the original base controller.

Enjoy!

/Jens Roland

Jens Roland