Part of the EllisLab Network
   
1 of 12
1
Proposal: View Library (updated x3!)
Posted: 04 April 2007 08:39 PM   [ Ignore ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1746
Joined  06-23-2006

UPDATE #1 - June 4th, 2007
- added handy functions for easier view management
- added improvements suggested by novaphoenix (thanks!)
UPDATE #2 - July 19th, 2007
- added TRUE as third parameter to $this->view->part() method to tell the library to render the partial immediately. This resolves a possible namespace conflict that Al James pointed out in this post. The conflict only arises if you want to use the same partial view more than once on a given page.
UPDATE #3 - July 28th, 2007 (revised) - full details here
- added support for sending no-cache headers to browser, upon request of Code Arachn!d
- added support for parsed templates, upon request from Athfar
- added support for a config/View.php file for storing configuration values
- added phpDoc-style comments

Download View Library
—-

I’ve always found the default nature of view and view data handling in CI to be awkward at best. Therefore, a few months ago, I solved that problem for myself by creating a “View” library that is automatically loaded.

Let’s look at some examples of how we can address the awkwardness.

The requirement to pass a data array into $this->load->view().

Normally, we do this:
1. create a $data array, quite often with a different line of code for each item in the array.
2. pass it into the view

$data['name'] = 'Easter Bunny';
$data['treat']  = 'chocolate';
$this->load->view('template', $data);

By using a View library, this can be written as:

$this->view->set('name', 'Easter Bunny');
$this->view->set('treat', 'chocolate');
$this->view->load('template');

Same amount of code, but much easier to read, and much more flexible, as you’ll begin to see.

How do we assign data to the view when in the constructor?
Because the View library would already be loaded, it would be available in constructors, which is always an awkward problem for some projects.

$this->view->set('variable', 'value');

One line of code per variable. No need to mess around with controller-wide variables, globals, or other fancy gimmicks.

What about those annoying partials (views within views)?

I handle partials by having a variable in my main view that the partial’s html gets assigned to. I know not everyone will do it this way, but let’s continue so you can see how a View library might make this easy for you.

Say you have 3 view files (a main template, and two sub-views that get included into it):

/views/main_template.php
/views/left_column.php
/views/right_column.php

The main template has a placeholder variable for each partial, such as $left_column and $right_column. Now, we just need to assign the partials and display the view:

$this->view->part('left_column', 'left_column.php');  // (file extensions added for clarity)
$this->view->part('right_column', 'right_column.php');
...
$this->view->load('main_template');

No need for a view to include a subview directly. You can do that in one line of code, as shown. Upon rendering the main template, the subviews are rendered and the resulting code is assigned to respective view variables which are automatically passed to the main view.

What about libraries and models?
When data from a library needs to be passed to the view, we find ourselves passing it back to the controller function and then assigning it as shown above. Well, why not just assign it directly to the view inside our library or model?

In a library or model:

$obj =& get_instance();
$obj->view->set('variable', 'value');

You want to be careful not to break the MVC rules of conduct, so the choice to do this will depend on the role of library or model function called. You always have the option to pass data back to the controller and assign to the view from there.

$this->view->set('variable', $this->some_library->get_some_value());

I’ll post a working library here and may add something to the Wiki. Feel free to chime in with questions and comments.

 Signature 

Mac OS X 10.4.10, Apache 1.3.3, PHP 5.2.3, CodeIgniter 1.5.x., baby!

Profile
 
 
Posted: 04 April 2007 09:02 PM   [ Ignore ]   [ # 1 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1746
Joined  06-23-2006

A prototype library (adapted from my own version):

class View {
    
var $vars  = array();
    var
$parts = array();

    function
View() {
        
// constructor
    
}

    
function set($k, $v) {
        $this
->vars[$k] = $v;
    
}

    
function part($var, $file) {
        $this
->parts[$var] = $file;
    
}

    
// same prototype as Loader::view()
    // wraps around Loader::view() for now
    
function load($file, $data = array(), $return = FALSE) {
        $CI
=& get_instance();
        
// merge $this->vars with $data parameter
        
$vars = array_merge($this->vars, $data);
        
// process partials
        
foreach ($this->parts as $var => $part) {
            $vars[$var]
= $CI->load->view($part, $vars, TRUE);
        
}
        
return $CI->load->view($file, $vars, $return);
    
}

}

(It might be an idea for View::set() to wrap around Loader::vars() for backward-compatibility with existing code.)

EDIT:
- Optimization: moved get_instance() to the load() method because it’s not needed until then. This saves a step should load() never be called, such as if there was a page redirect().
- Fixed overwriting of $file parameter during partial process.

 Signature 

Mac OS X 10.4.10, Apache 1.3.3, PHP 5.2.3, CodeIgniter 1.5.x., baby!

Profile
 
 
Posted: 04 April 2007 09:55 PM   [ Ignore ]   [ # 2 ]  
Grad Student
Avatar
Rank
Total Posts:  97
Joined  07-05-2006

Very, very interesting approach! Will look at it in deep.

Thanks!

 Signature 

Server 2003 SP1—IIS6.0—PHP 5.2.0—SQL SERVER EXPRESS 2005—CI 1.5.2; downer  hmmm surprised grin snake

Profile
 
 
Posted: 04 April 2007 10:24 PM   [ Ignore ]   [ # 3 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  599
Joined  09-30-2006

Not a bad approach - especially with the ‘parts’ I can see some nice potential for component caching.

How does this library integrate with the CI Output class? Is your view library handling all the output to the browser or does it sit on top of the CI View implementation and pass final output to it somehow?

 Signature 

sitesquad.net | < insert catchy tagline here />

Profile
 
 
Posted: 04 April 2007 10:39 PM   [ Ignore ]   [ # 4 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1746
Joined  06-23-2006
The German - 04 April 2007 10:24 PM

How does this library integrate with the CI Output class? Is your view library handling all the output to the browser or does it sit on top of the CI View implementation and pass final output to it somehow?

It sits on top of the existing view logic. It invokes the standard CI methods behind the scenes, so there’d be room for tighter integration if it were to become a standard component.

The example library I provided is the essence of it.

(Hey, why the name change Big J?)

 Signature 

Mac OS X 10.4.10, Apache 1.3.3, PHP 5.2.3, CodeIgniter 1.5.x., baby!

Profile
 
 
Posted: 05 April 2007 12:30 AM   [ Ignore ]   [ # 5 ]  
Grad Student
Avatar
Rank
Total Posts:  59
Joined  06-27-2006

Erm.. I don’t get it…? raspberry

Perhaps the ability to send data straight from a library to a view could be nice.. but like you say, you have to be careful about the whole MVC implementation. Do you have an example of an application where this would be useful or more beneficial than having the controller handle the flow of data?

 Signature 

beyondcoding.com - From beta to world-beater…

Profile
 
 
Posted: 05 April 2007 12:48 AM   [ Ignore ]   [ # 6 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1746
Joined  06-23-2006

Maybe the part about a library or model taking advantage of this just complicated the matter. This merely illustrates a more streamlined approach to assigning data to a view. It doesn’t replace the existing “flow” of the controller passing that data to the view, it just gives it more flexibility.

Let’s look at more examples:

1. Starting at the top, let’s assign data to the view in a constructor.

Current method:

function Constructor() {
    $data[
'variable'] = 'value';
    
$this->load->vars($data);
}

Proposed method:

function Constructor() {
    $this
->view->set('variable', 'value');
}

2. Assigning data to a view in a controller method:

function method() {
    $data[
'var1'] = 'value1';
    
$data['var2'] = 'value2';
    
$data['var3'] = 'value3';
    ...
    
$this->load->view('template', $data);
}

Proposed way:

function method() {
    $this
->view->set('var1', 'value1');
    
$this->view->set('var2', 'value2');
    
$this->view->set('var3', 'value3');
    ...
    
$this->view->load('template');   // no need to pass data in, which keeps the code cleaner
}

Now, let’s see if we can clear up your confusion…

3. Say our site has some view components (ie. login form, dynamic menu) that need to be loaded from any page so we build this into a library or model. That library or model could generate the html for our component. It would be up to us to either return that html to the controller function and assign it to the view, or just have the library assign it internally (through the controller). The choice would entirely depend on what makes the most sense.

For example, if we have a Menu library or model, we could do this:

$this->view->set('nav_menu', $this->menu->generate());

or to assign it internally:

$this->menu->assign_html('nav_menu');

Either way would be perfectly fine, in my opinion.

Another advantage to having a View library is customization. We could subclass it and add functions, such as menu(), login_form(), etc… and call them like so:

$this->view->login_form('variable_to_assign_to_here');
...
$this->view->nav_menu('variable_to_assign_to_here');

Some of us spend a lot of time creating custom libraries to generate view components. With this View library in place, we have a single, logical place to put those components. Since libraries can load other libraries and models, a subclassed View library would be able to do all kinds of stuff without muddying up our controller functions. I think that brings us even closer to the MVC (Model-VIEW-Controller) design.

Debatable Point: HTML is merely a rendering of the view. The view is not the HTML. The view can be rendered using other media.

 Signature 

Mac OS X 10.4.10, Apache 1.3.3, PHP 5.2.3, CodeIgniter 1.5.x., baby!

Profile
 
 
Posted: 05 April 2007 01:05 AM   [ Ignore ]   [ # 7 ]  
Grad Student
Avatar
Rank
Total Posts:  59
Joined  06-27-2006

I think I understand now.. smile

You’re basically using the library to assign a list of “standard” data for a view right? So when you call the view from different controllers, the same standard data set is pulled.. am I following now? smile

 Signature 

beyondcoding.com - From beta to world-beater…

Profile
 
 
Posted: 05 April 2007 01:20 AM   [ Ignore ]   [ # 8 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1746
Joined  06-23-2006

With a name like Goo Theory, I’m not surprised you talk in abstract thoughts. wink

You’re on the right track…

 Signature 

Mac OS X 10.4.10, Apache 1.3.3, PHP 5.2.3, CodeIgniter 1.5.x., baby!

Profile
 
 
Posted: 05 April 2007 07:51 AM   [ Ignore ]   [ # 9 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  599
Joined  09-30-2006
coolfactor - 04 April 2007 10:39 PM

(Hey, why the name change Big J?)

I’m messing around with the login system. I want my old name back, but’s not working.  tongue rolleye
It let’s me be TheGerman in both EE and CI but not Mirage.

 Signature 

sitesquad.net | < insert catchy tagline here />

Profile
 
 
Posted: 05 April 2007 01:06 PM   [ Ignore ]   [ # 10 ]  
Research Assistant
RankRankRank
Total Posts:  970
Joined  04-13-2006

I like this, but it’s going to give me a design headache which I am pleased to share.  wink
I already have a very non-expert “registry” pattern file which includes child classes such as a QueryRegistry where I can store details of, er, queries. Query name and any parameters, for example, are placed in the registry object for further action as necessary. At its simplest, the registry just needs setters and getters.
Now, your View library seems to conform to that pattern except that it’s specific to CI where mine is not. The question is: am I right in applying (in my non-expert way) the Registry pattern here; and if so, should CI make specific provision for this kind of structure where appropriate? Argument now open to all, I’m out of here.

Profile
 
 
Posted: 05 April 2007 01:11 PM   [ Ignore ]   [ # 11 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  472
Joined  09-26-2006

Now this is something I could really get used to. I have a bunch of questions (mostly around ideas for
component caching) but I don’t seem to be able to phrase an intelligible one right now grin

Thanks for the post, Time to go empirical. (Hooo boy, when will I ever get some work done wink

 Signature 

Old programmers never die, they just parse away.

Profile
 
 
Posted: 05 April 2007 01:20 PM   [ Ignore ]   [ # 12 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  599
Joined  09-30-2006
Martin Hall - 05 April 2007 01:06 PM

Now, your View library seems to conform to that pattern except that it’s specific to CI where mine is not. The question is: am I right in applying (in my non-expert way) the Registry pattern here; and if so, should CI make specific provision for this kind of structure where appropriate? Argument now open to all, I’m out of here.

I’m not even going to pretend that I fully understand what you are asking here.  gulp

However, what would be the benefit of CI making provisions for a certain kind of programming pattern? Perhaps you could explain in more detail how this would be helpful to you so that we may entertain whether it could be helpful to all. I’m all ears - but perhaps a separate thread?

Coolfactor’s [CI specific] implementation is a really good example of building on top of CI’s infrastructure. An independent library that feeds the underlying classes but provides some [good] alternatives to the built-in functionality. I love code like that…

 Signature 

sitesquad.net | < insert catchy tagline here />

Profile
 
 
Posted: 05 April 2007 03:42 PM   [ Ignore ]   [ # 13 ]  
Research Assistant
RankRankRank
Total Posts:  970
Joined  04-13-2006
The German - 05 April 2007 01:20 PM


I’m not even going to pretend that I fully understand what you are asking here.  gulp

That’s a real shame, I was kind of hoping you could tell me what I mean….  wink
To simplify, setting aside any considerations of universal usefulness, would you make coolfactor’s View class a child of an already existing library of set/get classes? That seems logical to me, but perhaps I’m trying to think on too many different levels at once. Patterns add an extra discipline for me, although I’ll never be an expert, and it makes sense to group similar objects together.
In PHP5 my “Registry” library would have an interface rather than a parent class. Its classes would then implement methods named in the interface as well as defining any individual methods which may be required. Hence an “upgrade” to PHP5 - in the case of this “Registry” example - will be relatively simple.
Patterns, PHP5 upgrade - maybe I think too much. But I want to code efficiently, and that means being aware of wider concepts than CI.

Profile
 
 
Posted: 05 April 2007 09:16 PM   [ Ignore ]   [ # 14 ]  
Research Assistant
RankRankRank
Total Posts:  915
Joined  07-10-2006
Martin Hall - 05 April 2007 01:06 PM

Now, your View library seems to conform to that pattern except that it’s specific to CI where mine is not. The question is: am I right in applying (in my non-expert way) the Registry pattern here; and if so, should CI make specific provision for this kind of structure where appropriate? Argument now open to all, I’m out of here.

Makes a lot of sense to me. If the Registry supported the notion of parent objects, then it should be possible to handle view fragment nesting within the registry. For example, menu and login fragments could be associated with the left column (a region object) and other view fragments could be associated with the right column (another region object).

It seems to me that Coolfactor’s solution is more of a template-like class (view manager), less the negative aspects of a template parsing class.

Profile
 
 
Posted: 07 April 2007 05:25 AM   [ Ignore ]   [ # 15 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  472
Joined  09-26-2006

Coolfactor, I’m having a problem which no doubt has a really simple answer, but I can’t see it.
Basically, I’m testing the rendering of partial views, and only the “footer” is being rendered.
I’ve adapted the standard CI welcome / welcome_message code and autoloaded the View library

class Welcome extends Controller {

    
function Welcome()
    
{
        parent
::Controller();    
    
}
    
    
function index()
    
{
        
// Proposed method >> Only the footer view, "Page rendered in 0.0861 seconds" shows in browser
        
$this->view->set('h1_content', 'Welcome to View Lib Testing');
        
$this->view->set('title', 'welcome to view lib testing');
        
$this->view->part('header', 'header.php');
        
$this->view->part('footer', 'footer.php');
        
$this->view->load('welcome_message');

        
        
// Current method >> Renders in browser as expected
        //$data['h1_content'] = 'welcome to view lib';
        //$data['title'] = 'Welcome to View Lib Testing';
        //$data['header'] = $this->load->view('header', $data, TRUE);  
        //$data['footer'] = $this->load->view('footer', $data, TRUE);        
    //$this->load->view('welcome_message', $data);
        
}
}
 Signature 

Old programmers never die, they just parse away.

Profile
 
 
   
1 of 12
1
 
Post Marker Legend
New Topic New posts Hot Topic Hot Topic with new posts New Poll New Poll Moved Topic Moved Topic Sticky Topic Sticky topic
Old Topic No new posts Hot Old Topic Hot Topic with no new posts Old Poll Old Poll Closed Topic Closed Topic Announcement Announcements
Theme
Change Theme
Visitor Statistics
The most visitors ever was 719, on June 06, 2008 10:16 AM
Total Registered Members: 77562 Total Logged-in Users: 33
Total Topics: 101555 Total Anonymous Users: 1
Total Replies: 544393 Total Guests: 247
Total Posts: 645948    
Members ( View Memberlist )