Template challenge

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Template challenge

Post by alex.barylski »

Assume you have some rendering code like:

Code: Select all

$users = array(
  array('name' => 'John Doe', 'age' => 32),
  array('name' => 'Jane Doe', 'age' => 29),
  array('name' => 'Jake Doe', 'age' => 42),
  array('name' => 'Jaws Doe', 'age' => 22),
);

//
// Iterate array and output alternating colors as well as as boldifying text of anyone *over* 40

for($i=0; $i<count($users); $i++){
  $clr = $i & 1 ? 'aeaeae' : 'eaeaea';
  
  if($users[$i]['age'] > 40)
    echo '<div style="background-color: #'.$clr.'"><b>'.$users[$i]['name'].'</b></div>';
  else
    echo '<div style="background-color: #'.$clr.'">'.$users[$i]['name'].'</div>';
}
For those of you that followed (or tried too) my Odd Question in general disscussion...this is the gist of what I was trying to articulate, but in theoretical coding terms??? :P

Anyways, the above is a rather trivial list generator...they alternate colors and boldify anyone who is *older& than 40 years of age...

Easy right? I am sure rendering logic *could* get a lot more complicated which is what I wanted, but for brevity and simplicity assume only the above...

Now...here's the challenge...discover a method/technique or whatever to cram that entire FOR loop into a single function call, all the while allowing a client developer full control over output, whilst *not* requiring any modifying of the rendering function...

I use a very simple native template engine and my code (templates) often get sprinkled with complicated looking for loops, etc...I'm investigating methods to condense the above logic into a flexible, efficient and easy to read macro of sorts (using functions as PHP doesn't support macros)...I've actually been working on a meta language of sorts which solves this very problem, but compilation overhead and complexity is making me consider alternatives...

So I ask...bring your ideas to the table...I'd like to see what an uninitiated community can do and how it differens from my own function.

p.s-I plan on using this function as a helper function inside my own template engine...incase your wondering :)

Cheers :)
User avatar
Maugrim_The_Reaper
DevNet Master
Posts: 2704
Joined: Tue Nov 02, 2004 5:43 am
Location: Ireland

Post by Maugrim_The_Reaper »

If I can escape the weekend rush I'll look at it more, but a few ideas:

# extract <div> into a single statement
# change <b>, add to style as font-weight: bold;
# assuming this is a view, why is it a function and not a template to be "rendered"
# If you abstract, you can a View class with magic methods for variable setting, and View::render()

Maybe I'm missing the context - what exactly are the design goals?

Edit: No changing of your current template engine/class?
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Post by matthijs »

Might not be the answer you're looking for, but still: if you would let the backend code produce a list with classes "odd" and "even", the designer can style it any way he wants.
So

Code: Select all

Php:
<?php
function showList(){
for($i=0; $i<count($users); $i++){ 
  if($users['age'] > 40)
    echo '<li class="odd">'.$users['name'].'</li>';
  else
    echo '<li class="even">'.$users['name'].'</li>';
} 
}
?>
Template:
..
<ul><?php showList(); ?></ul>
..
HTML output:
<ul><li class="odd">Hockey</li><li class="even">Matthijs</li> ... </ul>

CSS:
li.odd {background:pink;}
li.even {background:red;}
You could also let the funtion use a variable to be able to change the usage of <li> into something else (say <td> or <div>).
Something like <?php showList('div'); ?>. IMHO, something like this would be ideal from a front-end designers view (someone not familiar with much php coding).
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Code: Select all

echo '<ul>';
for ($i=0; $i < count($users); $i++) 
{
  	$class = $i & 1 ? 'odd' : 'even';
  	$id = $users[$i]['age'] > 40;
 
 	echo '<li class="'.$class.'" '. ($id ? 'id="bold"' : '').'>'.$users[$i]['name'].'</li>';
} 
echo '</ul>';
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Post by alex.barylski »

I'll try and explain the objective again...but first.

matthijs the function you have defined is exactly what I am trying to avoid. I hear you when you say styling can be applied to your hearts content, but what if LI isn't the markup I wish to use?

Markup *must* remain outside of the function as this function is generic enough it can be reused in *any* situation...

The function takes the markup and the array of data as parameters...yes it's an ugly signature but it can be formatted nicely and shortens the code from the above FOR loop into a single function call - which is the design goal.

A generic function, which is supplied the markup and array of data as parameters - I should have said that right off the hop would have been less confusion. ;)

By passing the function the markup and the array, both the model and view are kept entirely separate and can be pulled from any source. The function handles all of the logic which is normally found inside a template such as the original code peice I supplied above...using nothing but the parameters provided it should be able to reconstruct any list imaginable...

Cheers :)
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

I think this is the responsibility of templates, not the view. The should should be passed the option to decide which template (with the markup) to use.

Of course, this depends on your implementation, as this would be a snap using smarty, of if you avoid using logic in templates all together.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Post by alex.barylski »

Jcart wrote:I think this is the responsibility of templates, not the view. The should should be passed the option to decide which template (with the markup) to use.

Of course, this depends on your implementation, as this would be a snap using smarty, of if you avoid using logic in templates all together.
Hmmmm...templates are responsible for generating the view...template logic bugs me...considering the fact I use a native PHP templates...even more so that Smarty. I dislike Smarty and will never use it unless nessecary - I won't explain why ;)

Code: Select all

- Model
 - View
 - Templates
Model is the data, View is the resulting HTML and Templates are the logic mixed with markup that eventually provides the view??? Am I correct in assuming this is how you currently view (no pun intended) the problem?

Interesting, this differs slightly from my own - but makes still sense. ;)

I see the the template (markup w/ logic) as the view and the template engine as the layer of seperation (if that makes sense?).

My reasoning is that on the server templates are the view, but when delivered to the client (in the form of pure HTML) they are also the view but in the context of the client *not* the server. Follow me?

But ignoring our discrepancies...do you see what it is I am trying to achieve?
User avatar
John Cartwright
Site Admin
Posts: 11470
Joined: Tue Dec 23, 2003 2:10 am
Location: Toronto
Contact:

Post by John Cartwright »

Hmmmm...templates are responsible for generating the view...template logic bugs me...considering the fact I use a native PHP templates...even more so that Smarty. I dislike Smarty and will never use it unless nessecary - I won't explain why ;)

Code: Select all

- Model
 - View
 - Templates
Model is the data, View is the resulting HTML and Templates are the logic mixed with markup that eventually provides the view??? Am I correct in assuming this is how you currently view (no pun intended) the problem?
View's are responsible for generating the template, not vise-versa. I very much dislike applications that merge these two layers together.

Code: Select all

class IndexView extends View
{
   public function render() 
   {
      $this->loadTemplate('indexView.html');
      //$this->loadTemplate('indexView.xml');
   }
   
   public function setBlockA($a)
   {
      $this->set('variable', $a);
      $this->set('foo', 'bar');
   }
}
Templates are what they are, templates. They receive the data and output them.
Hmmmm...templates are responsible for generating the view...template logic bugs me...considering the fact I use a native PHP templates...even more so that Smarty. I dislike Smarty and will never use it unless nessecary - I won't explain why ;)
I am the same way about smarty, I prefer to inline my php in templates just as long as it is simple logic. Even still, I would preferably avoid if()'s but I see no problem with loops.
But ignoring our discrepancies...do you see what it is I am trying to achieve?
I'm getting there, just looking for a bit more input from you in terms of your application design.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Post by alex.barylski »

View's are responsible for generating the template, not vise-versa.
I can see why you would think that way in following the Zend approach to MVC...but in my experience the 'View' is the user interface not a component of the MVC triad. The user interface in this case is the complete HTML.

Which brings me back to my seperation of client/sever MVC...perhaps from the server perspective...View is simply a component of the MVC triad as you have demonstrated, whereas a template is simply a by-product of the view??

I dunno dude...after thinking about that I can't say I agree...in the Zend world MVC are objects...but only because they are part of a framework which needs explicit control over those components inorder to do it's automagical stuff...Unless my understanding is off...I've only ever used ZendF for about a week and lost interest because of the issues I raised before. :P

Anyways...conceptually (not technically according to ZF) i've always seen the View as the UI...which I think is accepted as the industry standard...the view component of ZF isn't actually the View but a view generator IMHO so I would argue the above quote as the opposite...Templates indeed generate the View. The view you refer too in ZF terms is simply a component of the framework's interpretation of the MVC triad, but it by no means is the conceptual view. This is subjective. I mean I think my analysis is correct, however your definition of either would depend on how you viewed the problem (technically based on a framework or conceptually like I have described above?)

Anyways...I've gone off course here slightly but you raised an interesting point which hadn't been considered hitherto - good job. :)

What I am trying to accomplish (which is impossible using solely PHP) is to seperate concerns completely. Basically remove as much template logic from the template itself...making a template easier to modify, even for a seasoned veteran (changing HTML is easier than figuring out logic).

Using PHP this requires modularization of the templates - which will go against known practice as most template engines support the concept of an entire template with variable placeholders. This is good for WYSIWYG editors, but makes it difficult to represent anything complicated without advanced template logic.

What I am trying to discover/attempt/implement is a method of template decomposition (besides programming is all about divide and conquer) where instead of writing nearly complete templates you develope templates in modular - highly reusable segments or parts which represent a single record *state*

For example here is what I have considered:

Code: Select all

//
// Template represents an individual record - could be stored anywhere (DB, PHP, TXT, etc)
$html['list'] = <<<HTML
  <input type="checkbox" style="border: 0px" disabled="%s" />
  <div class="%s">
    @var['name']@
    <img src="none.gif" />
  </div>
HTML;

//
// Substitue for FOR loop with logic - used inside native PHP template
nitro_list_repeat(array('var' => $var->arr), 
  array( // FORMAT: HTML Segment -- PreRendering Expression
    array(sprintf($html['list'], 'css_enabled1', 'enabled'), 'var.state EQ "enabled" AND var.cnt % 2'),
    array(sprintf($html['list'], 'css_enabled2', 'enabled'), 'var.state EQ "enabled" AND var.cnt % 1'),
    array(sprintf($html['list'], 'css_disabled', 'disabled'), null) // Use when all other expressions fail?
  )
);
The following works (at least in my head) but only for trivial loops...the minute you introduce something more complicated, like nested for loops...this technique or helper function will not suffice.

Cheers :)
Post Reply