CodeIgniter Forums
Extending the Loader class (more or less) - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Archived Discussions (https://forum.codeigniter.com/forum-20.html)
+--- Forum: Archived Libraries & Helpers (https://forum.codeigniter.com/forum-22.html)
+--- Thread: Extending the Loader class (more or less) (/thread-3512.html)



Extending the Loader class (more or less) - El Forum - 10-07-2007

[eluser]Unknown[/eluser]
Hello

Problem:
I need to implement some sort of themes for a site I'm working on.


Solution:
Extend the CI_Loader class and overwrite the "view" function, to add an optional subdirectory for my views. Something like:

Code:
application/themes/mytheme/view.php

Extending the CI_Loader class would be simple, but we have a problem: the CI_Controller class extends CI_Loader and somewhere in the code we have:
Code:
$this->load =& $this; //to allow $this->load->view(...) to work
So, $this->load isn't an instance of the CI_Loader class (that we might have overwritten), but it's a reference to the controller object.

The solution is simple: between CI_Controller and our app controllers, we will add a new Controller class, that will overwrite the "view" function. The result will look like:
CI_Loader -> CI_Controller -> MyBaseController -> App Controller.

Code:
<?php
// application/controllers/MyControllerBase.php
class MyControllerBase extends Controller {
    var $theme = NULL;
    function view($view, $vars = array(), $return = FALSE, $fixored = FALSE)
    {
    $folder = '';
    if (!is_null($this->theme)) {
        $folder = 'themes/'.$this->theme.'/';
    }
    return $this->_ci_load(array('view' => $folder.$view, 'vars' => $this->_ci_object_to_array($vars), 'return' => $return));
    }
}
?>

and an application controller:
Code:
<?php
require_once(APPPATH.'/controllers/MyControllerBase.php');
class SomeController extends MyControllerBase {
    ...
}
?>


I hope this helps and of course, if you have better suggestions, please post them here.

Peter


Extending the Loader class (more or less) - El Forum - 10-07-2007

[eluser]Derek Allard[/eluser]
I haven't fully digested what you're writing here, but I wanted to point out that there is a "display_override" hook available (hooks docs) that might be more flexible for you, and wouldn't require any core hacking.


Extending the Loader class (more or less) - El Forum - 10-07-2007

[eluser]Unknown[/eluser]
The display_override hook will give me access to a string that will contain all the generated output (right? Big Grin). I find it a bit harder to process that string and change the layout, than just use another view for a different layout Smile

Let's consider the following directory structure:

application/view/ <-- will contain the "default" views.
application/view/themes/ <-- will contain a subfolder for each theme.
application/view/themes/myfancytheme/ <-- will contain all views that are different from the default one.


If I don't set a theme, then the "login.php" view will be loaded from the application/view/ directory. If I set the theme to "myfancytheme", the the "login.php" view will be loaded from the application/view/themes/myfancytheme/ directory.

Ofcourse, the code I posted earlier is just the rough idea, still needs checks (if the theme directory exists and so on), maybe a fallback mecanism (that will allow me to rewrite only some views) etc.


Extending the Loader class (more or less) - El Forum - 10-07-2007

[eluser]Rick Jolly[/eluser]
The only problem with your code is that you are calling private CI core methods that aren't part of the public API (_ci_load() and _ci_object_to_array()). A couple of alternatives:
Code:
#1.
&lt;?php
// application/controllers/MyControllerBase.php
class MyControllerBase extends Controller {
    var $theme = NULL;
    function loadview($view, $vars = array(), $return = FALSE)
    {
       $folder = '';
       if (!is_null($this->theme)) {
           $folder = 'themes/'.$this->theme.'/';
       }
      
       if ($return) {
           return $this->load->view($folder.$view, $vars, true);
       }
       else {
           $this->load->view($folder.$view, $vars);
       }
    }
}
?&gt;

#2.
// or more simply, add a method that appends the theme folder to the view
$view = $this->getViewPath('someView');
$this->load->view($view, $vars); // or $this->load->view($view, $vars, true);



Extending the Loader class (more or less) - El Forum - 12-13-2007

[eluser]voltechs[/eluser]
Hey y'all...

Back with hopefully another solid Giveback to the community. I like having a "layout" for those of you familiar with Ruby On Rails, for my sites. Figured this is what the original posted was going for. I'd ultimatly love it if this could be turned into a plug-in of sorts, or some other "override" which doesn't actually "hack" the core of CI, so to all

BEWARE: THIS WILL MODIFY YOUR CORE SYSTEM.

Not for the faint of heart. That being said, its not a very big change, and I've taken the liberty of creating a .patch file for you all. This will only work for unix/linux systems. Run this command to patch the Loader.php file. patch -R path/to/system/libraries/Loader.php < Loader.patch

I recommend you look over the patch file so you know what's going on. It should be fairly straight forward, and I commented where necessary to blend in with the rest of the file. If you don't have Unix/Linux, and haven't made any changes to Loader.php, you can simply replace it with the one I've attached. I recommend the patch if possible over replacing. This also allows for _partial.php views. You can call them from within your views and layouts.

After you're patched up, make a sibling directory to views called layouts and pop in your php/html layout there. Then just define your layout in your controller by saying: $layout = "name_of_layout";
Code:
class Admin extends Controller {

    var $layout = "home";

    function Admin()
    {
        parent::Controller();
    }
}
In your layout file, you'll want something like this. The important piece is the bit of PHP imbedded in there:
Code:
<div id="primary">
    &lt;?= $content_for_layout ?&gt;
    <span class="clear"/>
</div>

EDIT: Turns out I can't attach a patch file. I'll just post it here. One caveat to be aware of. Files have different line endings between computer systems. I work in the UNIX standard LF. Just make sure both the patch and the Loader.php file have the same encoding and line endings or the patch will fail. Save the following code to a file on your computer.

Code:
34d33
<     var $_ci_layout_path    = '';
57d55
<         $this->_ci_layout_path = APPPATH.'layouts/';
266,289d263
<      * Load Partial
<      *
<      * This function is used to load a "partial" file.  It has three parameters:
<      *
<      * 1. The name of the "partial" file to be included.
<      * 2. An associative array of data to be extracted for use in the view.
<      * 3. TRUE/FALSE - whether to return the data or load it.  In
<      * some cases it's advantageous to be able to return data so that
<      * a developer can process it in some way.
<      *
<      * @access    public
<      * @param    string
<      * @param    array
<      * @param    bool
<      * @return    void
<      */
<     function partial($view, $vars = array(), $return = FALSE)
<     {
<         return $this->_ci_load(array('partial' => true, 'view' => $view, 'vars' => $this->_ci_object_to_array($vars), 'return' => $return));
<     }
<    
<     // --------------------------------------------------------------------
<    
<     /**
600c574
<         foreach (array('partial', 'view', 'vars', 'path', 'return') as $val)
---
>         foreach (array('view', 'vars', 'path', 'return') as $val)
604,608c578
<        
< //        if ($partial && $view[0] != '_') {
< //            $view = substr_replace($view, "/_".$view, strripos($view, "/"));
< //        }
<            
---
>
675,677c645
<         eval('?&gt;'.preg_replace("/;*\s*\?&gt;/", "; ?&gt;", str_replace('&lt;?=', '&lt;?php echo ', file_get_contents($path))).'&lt;?php ');
<        
<         if (!$partial)
---
>         if ((bool) @ini_get('short_open_tag') === FALSE AND config_item('rewrite_short_tags') == TRUE)
679,695c647,651
<             if (!isset($this->layout)) {
<                 show_error('No layout defined in one of your controllers');
<             }
<        
<             $layout_ext = pathinfo($this->layout, PATHINFO_EXTENSION);
<             $layout_file = ($layout_ext == '') ? $this->layout.EXT : $this->layout;
<             $layout_path = $this->_ci_layout_path.$layout_file;
<            
<             if ( ! file_exists($layout_path))
<             {
<                 show_error('Unable to load the requested layout: '.$this->_ci_layout_path.$layout_file);
<             }
<            
<             $content_for_layout = ob_get_contents();
<             @ob_end_clean();
<        
<             include $layout_path;
---
>             echo eval('?&gt;'.preg_replace("/;*\s*\?&gt;/", "; ?&gt;", str_replace('&lt;?=', '&lt;?php echo ', file_get_contents($path))).'&lt;?php ');
>         }
>         else
>         {
>             include($path);

Hope this helps!

Cheers,

-- volte