tags:

views:

338

answers:

6

I have a function that loops through different types of listings pulled from MySQL and displays them as a Facebook-esque "feed". Each type of listing has a different appearance, and therefore requires a different template.

Here is the below function example might be called 100 times (if there are 100 listings):

function display_listing($id,$type) {
    global $abs_path;

    switch($type) {

        case 'photo':
            include($abs_path . '/templates/photo.php');
            break;

        case 'video':
            include($abs_path . '/templates/video.php');
            break;

    }

}

Is there a better way to do this, where I don't potentially end up with 100 includes - which may slow my process down? Can I somehow save the include once on the page, and refer to it as many times as needed?

...or is this the best practice?

Also, I want to know how to do this on my own, without a template engine...

EDIT: The problem isn't solved just by include_once (I dont believe)...I need to be able to reuse the included file multiple times, replacing the variables each time.

A: 

You want to use the include_once function instead of include.

The include_once() statement includes and evaluates the specified file during the execution of the script. This is a behavior similar to the include() statement, with the only difference being that if the code from a file has already been included, it will not be included again. As the name suggests, it will be included just once.

Andrew Hare
I’m not sure about that. What if he wants to have 3 different photo listings, for example?
Nate
I believe asker needs the output of the included script multiple times, not just once.
Crescent Fresh
ah sh*t, im smart than that. where is my mind...thanks!
johnnietheblack
actually, andrew...im sorry, i believe i jumped the gun! yes...i need to reuse the template many times, and include_once seems to only work once....i apologize for the misunderstanding
johnnietheblack
A: 

create an array of allowed templates. have you function look to see if the requested template is in the array. if it is, then use the $type variable to include the file. yes, in general you shouldn't use user-supplied data in an include file name, but since you've validated it against a known-good array of values, it's safe.

longneck
A: 

If you're going to be including templates using a switch for different keywords, then you should set a variable, say $template_filename, in the switch and then once you break out of the switch, do the include like so:

include_once("/templates/{$filename}.php");

If you don't want to use including at all, then you might as well use a templating engine...

This method seems reasonable to me however.

BraedenP
when using include_once, the function works on the first pass, but then detects that the file has been included already on the second pass and does not load it again, which means the listing doesn't appear. I need a fast way of STORING the file locally once its loaded the first time....(i think)
johnnietheblack
+2  A: 

Although a switch is not the most scalable way to write this code, I'm afraid that includes is the only way to keep your templates in separate files, here.

You could, conceivably, encapsulate each template's code in a function, however:

/*
photoTemplate.php
*/

<?php
function loadPhotoTemplate($id) {
?>
  <div id="photo">
...
  </div>
<?php
}
?>


/*
listing.php
*/

function display_listing($id,$type) {
    global $abs_path;

    switch($type) {

        case 'photo':
            include_once($abs_path . '/templates/photo.php');
            loadPhotoTemplate($id);
            break;

        case 'video':
            include_once($abs_path . '/templates/video.php');
            loadVideoTemplate($id);
            break;
    }

}

This will load each template file at most once, and then just call the function each time you want to display the template with the specific data for that item.

Edit

It would probably be even better to include all template files at the beginning, then just call the appropriate function in the switch. PHP's *_once() functions are slow, as they must consult the list of previously included/required files every time they are called.

Lucas Oman
hmmm...thats not a horrible idea...
johnnietheblack
It may be better to include all template files at the beginning, whether used or not, then simply call the appropriate function in the switch. I say this because the *_once() functions are slow because they have to consult the list of previously included files.
Lucas Oman
good point. what about potentially using fopen, etc and saving the contents of the files to a variable each? is that slower?
johnnietheblack
I would imagine that reading the file and saving to variables would be slower. It would also require running eval() on the code, which is slow and insecure.
Lucas Oman
+1  A: 

If your concern is performance, I personally wouldn't get into this loop at all.

Presuming that items in the list are viewed more frequently than they're created, I would generate the HTML as the items are created and store the output alongside the data in the database. (Or in memcache or files or somewhere else - but generate it and store it.)

In doing so you'd move this logic back into the creation process of the items so you're doing the same switch statement, but only for one item at a time - you're cutting out the loop.

Then at display time, instead of calling display_listing(), you just echo out the HTML you've already stored.

drewm
so, say i have 100,000 listings...doesn't that create a TON of storage for my db, in which case having the template on a single external file is more system friendly? am i missing something in your answer?
johnnietheblack
Storage is really cheap. If you don't want to put HTML in your database, you could store it in a flat file on the file system. Even if each listing was 8k of HTML, that's less than 1 MB to store 100k listings. If you had 1 million listings, you're still under 10MB of storage.The alternative is instead of generating 100k listings once, you generate each listing time and time again and keep throwing the work away. Your `display_listing` function takes an ID argument. That suggests that you're querying the DB to get the details. That's going to be more expensive that including a file of HTML.
drewm
+1  A: 

I think you ought to look at a few things. Firstly, is there even a possibility you will include the same "template" 100 times? That seems like a massive amount of content to put on a page.

If I'm using tiny "snippets" - say, a table row that's repeated many times - I tend to use a function that returns a string, like this:

/* snippets.php */
function tableRow( $row )
{
    return
        '<tr><td>' . $row['var1'] . '</td>' .
        '<td>' . $row['var2'] . '</td></tr>';
}

/* main page/view */
foreach ( $data as $row )
    echo tableRow( $row );

The main advantage here is you can have loops and all sorts in the function.

If you're looking for a straight variable-replacement, you could create your own mini-templates in an HTML file; something like:

<p>{name}<br />
  Posted {date} by {author}</p>

Then read in the template, pass the whole string to your display_listing function, which runs str_replace on all the {variables}. I don't think there would be too much of a performance hit, but you'd have to try it to be sure.

DisgruntledGoat
I agree. My only suggestion would be to not put the HTML inline -- I think a separate template file that you could read once, then perform string replacement against in each iteration of the loop would be best. Then you are starting to move towards a real template engine! Anyway, any operation involving reading/writing to the filesystem is expensive, so something like this should speed things up pretty substantially.
sparkey0
@sparkey: that's exactly what I said - read in the HTML file to a string and pass the string to the functions to replace the variables.
DisgruntledGoat