Skip to content

Extending Controller with masterpage template funtionality

Derek Jones edited this page Jul 5, 2012 · 11 revisions

Category:Library::Controller | Category:Library::Community

Introduction

Since i started using the CI, I was searching a way to support master pages or master templates if you like to call it. When a view is loading, i would like the output to be displayed at a certain position of the master page. So i started building a library to do such a thing, when i realized that i had to write more code than before to load my template and all of its components. When i read about the _output method made me to think a more efficient way to add the functionality that i wanted. I have created a MY_Controller class that extends the common Controller class, so i have added the _output method into the MY_Controller class. My controllers now, that have to use this template system, just extends the MY_Controller class instead of extending the Controller class. With this way i was able to add even more common functionality for my controllers without have to write everytime a lot of code in each one of my controllers.

Requirements

CodeIgniter 1.6.x PHP 5

Installation

Create a MY_Controller.php, insert the class that follows and store it in APPPATH/libraries/ folder

Download

File:MY_Controller_2.zip

<?php
/**
 * This library extends the default controller and adds some useful extra features.
 * Features:
 *         1. Enables the usage of master page/templates.
 *         2. Can add css and javascript files.
 *         3. Uses the _output method for controling the controllers output.
 *         4. Can load relative content with in the form of modules (views)
 */
class MY_Controller extends Controller {
    /* Output control constants */
    const OUTPUT_TEMPLATE = 10;
    const OUTPUT_NORMAL = 11;

    /* Private properties */

    //the default template
    private $template_view = 'default';
    
    //the default public folder this means that content inside this folder will be accessed directly
    //without using the routing.
    //Note!!! This folder must be enabled in the .htaccess file.
    private $public_folder = 'public/';
    
    //the default location for the templates inside the views folder this means (views/templates/)
    private $template_folder = 'templates/';
    
    //the default css location for the css files inside the $public_folder("public/" by default) (public/css/)
    private $css_folder = 'css/';
    
    //the default js location for the css files inside the $public_folder("public/" by default) (public/js/)
    private $js_folder = 'js/';
    
    //Inline scripting (Javascript)
    private $inline_scripting = '';

    private $modules = array(); //An array that contains the modules output.

    private $charset = ''; //The page charset

    private $title = ''; //The page Title

    //Media files and data
    private $media = array('css'=>array(),
                            'js'=>array(),
                             //meta tags
                            'meta'=>array(),
                             //RDF are 3rd genreration meta tags
                            'rdf'=>array());

    //The requested controller
    protected $controller = '';
    //The requested method to be called
    protected $method        = '';
    
    private $_output_data = array(); 

    /**
     * The MY_Controller constructor method.
     */
    function __construct(){
        parent::Controller();
                
        //Initializing the controller
        //Get the default charset from the config file.
        $this->charset = $this->config->item('charset');

        //Set the default mode to use a template view
        $this->_setOutputMode(self::OUTPUT_TEMPLATE);

        //Passing as properties the controller and the method that should be called.
        $this->controller = $this->uri->rsegment(1);
        $this->method      = $this->uri->rsegment(2);
    }

    /**
     * CodeIgniter magic method that controls the output of a controller.
     * You can't call it directly.
     *
     * @see http://codeigniter.com/user_guide/general/controllers.html#output
     * @final this method cannot be overloaded
     * @param string $output the controller output
     * @return void
     */
    final function _output($output){
        switch($this->mode){
            //Use the template
            case self::OUTPUT_TEMPLATE:
                $data = array(    'meta'=>$this->media['meta'],
                                'rdf'=>$this->media['rdf'],
                                'js'=>$this->media['js'],
                                'css'=>$this->media['css'],
                                'title'=>$this->title,
                                'charset'=>$this->charset,
                                'output'=>$output,
                                'modules'=>(object)$this->modules,
                                'inline_scripting'=>"[removed]" . $this->inline_scripting . "[removed]");
                
                //Merge the data arrays
                $data = array_merge($data, $this->_output_data);
                
                //Load the final output
                $out = $this->load->view($this->template_folder . $this->template_view, $data, TRUE);
                
                //The End
                echo $out;
            break;
            //or just echo output.
            case self::OUTPUT_NORMAL:
            default:
                echo $output;
            break;
        }
    }
    
    /**
     * Pass extra data on the final output.
     *
     * @param string $paramName the parameter name.
     * @param mixed $value $the value of the parameter
     */
    
    protected function _setOutputData($paramName, $value){
        $this->_output_data[$paramName] = $value;
    }

    /**
     * This method sets the output mode. That the controller should use to display the content
     *
     * @access protected
     * @param int $mode One of the constants self::OUTPUT_TEMPLATE, self::OUTPUT_NORMAL
     * @return void
     */
    protected function _setOutputMode($mode){
        $this->mode = $mode;
    }
    
    /**
     * Sets the template that will be used at the final output.
     *
     * @access protected
     * @param string $template
     * @return bool
     */
    public function _template_to_use($template){
        $filepath = APPPATH . "views/"  . $this->template_folder . str_replace('.php','',$template) . ".php";
                
        if(!$this->_is_file_exists($filepath)){
            show_error("Cannot locate template file <tt>$template</tt>");
            return false;
        }

        $this->template_view = $template;
        
        return true;
    }

    /**
     * Adds a Javascript file into the template.
     *
     * @access protected
     * @param string $file a js file located inside the public/js/ folder or an url.
     * @param boolean $custom_url Default FALSE. A flag to determine if the given $file is an url or just a file inside the public/js/ folder.
     * @return bool
     */
    public function _add_js_file&#40;$file, $custom_url=false&#41;{
        
        if($custom_url===false){
            $filepath = $this->public_folder . $this->js_folder . str_replace('.js','',$file) . ".js";
                    
            if(!$this->_is_file_exists($filepath)){
                show_error('Cannot locate javascript file <tt><a href="'.$filepath.'">'.$filepath.'</a></tt>');
                return false;
            }
            
            $filepath = base_url() . $filepath;
            
        }else{
            $filepath = $file;
        }
        
        if (array_search($filepath, $this->media['js']) === false)
            $this->media['js'][] = $filepath;
        else
            return false;
            
        return true;
    }
    
    /**
     * Adds a CSS file into the template.
     *
     * @access protected
     * @param string $file a css file located inside the public/js/ folder or an url.
     * @param boolean $custom_url Default FALSE. A flag to determine if the given $file is an url or just a file insite the public/js/ folder.
     * @return bool
     */
    public function _add_css_file&#40;$file, $custom_url=false&#41;{
        if(!$custom_url){
            
            $filepath = $this->public_folder . $this->css_folder . str_replace('.css','',$file) . ".css";
            
            if(!$this->_is_file_exists($filepath)){
                show_error('Cannot locate css file: <tt><a href="'.$filepath.'">'.$filepath.'</a></tt>');
                return false;
            }
            
            $filepath = base_url() . $filepath;
        }else{
            $filepath = $file;
        }

        if(array_search($filepath, $this->media['css']) === false)
            $this->media['css'][] = $filepath;
        else return false;
        
        return true;
    }

    /**
     * Sets the default charset
     *
     * @access protected
     * @param string $charset
     * @return void
     */
    public function _set_chartset($charset){
        $this->charset = $charset;
    }

    /**
     * Sets the page title
     *
     * @access protected
     * @param string $new_title
     * @return void
     */
    public function _set_title($new_title){
        $this->title = $new_title;
    }

    /**
     * Appends a string at the title text
     *
     * @access protected
     * @param string $title
     * @return void
     */
    public function _append_title($title){
        $this->title .= " - $title";
    }

    /**
     * Adds meta tags.
     *
     * @access protected
     * @param string $name the name of the meta tag
     * @param string $content the content of the mneta tag
     * @return bool
     */
    public function _add_meta($name, $content){
        if(array_key_exists($name, $this->media['meta']))
            show_error("Duplicate usage of meta tag file <tt>$name</tt>.");

        $this->media['meta'][$name] = $content;
        return true;
    }

    /**
     * Adds RDF meta tags (3rd generation meta tags).
     *
     * @access protected
     * @param string $name the name of the meta tag
     * @param string $content the content of the mneta tag
     * @return bool
     */
    public function _add_RDF($name, $content){
        if(array_key_exists($name, $this->media['rdf']))
            show_error("Duplicate usage of meta tag file <tt>$name</tt>.");

        $this->media['rdf'][$name] = $content;
        return true;
    }

    /**
     * Registers module positions
     *
     * @access protected
     * @param string $position_name the name of the position (no special chars or spaces are allowed)
     * @return bool
     */
    public function _register_module_position($position_name){
        if(array_key_exists($position_name, $this->modules))
            show_error("Module position failed because position <tt>$position_name</tt> has already been registered.");

        //Check for illegal characters.
        if(!preg_match("/[a-zA-Z0-9]*/", $position_name))
            show_error("Position name <tt>$position_name</tt> contains some illegal characters. Only letters or numbers are allowed.");

        $this->modules[$position_name] = array();

        return true;
    }

    /**
     *    Loads a view file &#40;module&#41; in a certain position.
     *
     * @access protected
     * @param string $position the module position
     * @param string $view_file the view file path.
     * @param array $params    the parameter to be passed in the view file.
     * @return bool
     */
    public function _load_module($position, $view_file, $params = array()){
        if(!array_key_exists($position, $this->modules))
            show_error("Module position <tt>$position</tt> hasn't ever been registered.");
        
        $this->modules[$position][] = $this->load->view($view_file, $params, TRUE);
        
        return true;
    }

    /**
     * Marks the begining of inline scripting.
     * Example:
     *     &lt;?php ....
     *
     *             $this->_start_inline_scripting();
     *     ?&gt;
     *     [removed] .... [removed]
     *  &lt;?php
     *         $this->_end_inline_scripting();
     *     .....
     *!!!Note that the <scrpt*>[removed] tags will be removed!!!
     *
     */
    public function _start_inline_scripting(){
        ob_start();
    }

    /**
     * Marks the end of the inline scripting.
     */
    public function _end_inline_scripting(){
         $s = ob_get_clean();
         
         $s = preg_replace("/[removed]]*>/", '', $s);
         $s = preg_replace("/<\/script>/", '', $s);
              
         $this->inline_scripting .= $s;
    }
    

    /**
     * Checks if the given file exists in the filesystem.
     *
     * @access private
     * @param string $filepath The file path, using a physical relative path
     * @return bool
     */
    private function _is_file_exists($filepath){
        return file_exists($filepath);
    }
}
?&gt;

Create a file default.php insert the foolowing code and store it in APPPATH/views/templates/ folder:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
&lt;html &gt;
&lt;head&gt;
    &lt;title&gt;&lt;?php echo $title; ?&gt;&lt;/title&gt;
    &lt;meta http-equiv="Content-Language" content="&lt;?php echo isset($lang) ? $lang : 'en';?&gt;" /&gt;
    &lt;meta http-equiv="Content-Type" content="text/html; charset=&lt;?php echo $charset; ?&gt;" /&gt;
&lt;?php foreach($meta as $name=>$content): ?&gt;
    &lt;meta name="&lt;?php echo $name; ?&gt;" content="&lt;?php echo $content; ?&gt;" /&gt;
&lt;?php endforeach; ?&gt;
&lt;?php if(count($rdf) > 0): ?&gt;
&lt;!--
    <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:ddc="http://purl.org/net/ddc#">
        <rdf:Description rdf:about="&lt;?php echo base_url(); ?&gt;">
&lt;?php foreach($rdf as $name=>$content): ?&gt;
            <&lt;?php echo $name; ?&gt;>&lt;?php echo $content; ?&gt;</&lt;?php echo $name; ?&gt;>
&lt;?php endforeach;?&gt;
        </rdf:Description>
    </rdf:RDF>
--&gt;
&lt;?php endif;?&gt;
&lt;?php foreach($js as $js_file): ?&gt;
    [removed][removed]
&lt;?php endforeach; ?&gt;
&lt;?php foreach ($css as $css_file): ?&gt;
    &lt;link rel="stylesheet" type="text/css" href="&lt;?php echo base_url() . $css_file; ?&gt;" /&gt;
&lt;?php endforeach; ?&gt;
&lt;?php if (isset($fav_icon)) :?&gt;
    &lt;link rel='shortcut icon' type="image/x-icon" href='&lt;?php echo base_url() . $fav_icon; ?&gt;' /&gt;
&lt;?php endif; ?&gt;

&lt;?php echo $inline_scripting ?&gt;

&lt;/head&gt;
&lt;body&gt;
<div>
    <div id="main">
    &lt;?php echo $output; ?&gt;
    </div>
</div>
<div id="bottom">&lt;?php
//If we had to display some extra modules (views). This will display all the modules that have
//loaded at the "bottom" position. But first we should propably register the postion inside
//the MY_Controller construct (or whatever) using the $this->_register_module_potion() method.
//eg: $this->_load_module("bottom", "modules/my_module_view", $data);

//    foreach ($modules->bottom as $mod ){
//        echo $mod;
//    }
?&gt;
</div>
&lt;/body&gt;
&lt;/html&gt;

Example

A controller should look like this:

&lt;?php
class SomeController extends MY_Controller{
      
      function __construct(){
         parent::__construct();
         $this->_template_to_use("my_template_file");

      }

      function index(){
         $this->load->view("some_view_file");
      }
}
?&gt;

As you can see here it shouldn't change the structure or the way that you are usually writing a controller. For more portability you can use a config file to configure the MY_Controller class in which default template will be using, or which position should register an more. it's up to you.

Clone this wiki locally