PHP Bulletin Board Home
News About Home
Features of phpBB Test drive phpBB Downloads Support for phpBB The phpBB Community Styles for customising phpBB 3rd party modifications to phpBB

Support Home | Knowledge Base Home | Submit Article | Search Articles | Browse Articles
 phpBB Template Tutorial 
Description: Information on how the phpBB Template system works
Author: Xore
Date: Wed May 12, 2004 11:58 pm
Type: Tutorial
Keywords: template,tutorial,loop,switch,variable,variables
Category: MODifications
This article will hopefully shed some light on the usage of phpBB 2.0.x template system to enable mod authors to be able to write their own template files properly without much confusion.

  1. Template Basics
    Before creating a new template file, first consider whether or not you should. If it's very similar or almost identical to another template that already exists, except perhaps that you add a column here or there, or add another table with some extra data at the top or bottom of the page, it might just be a better idea to alter the current template. If you're not creating a new template, but instead altering an old one, disregard points (2) and (3), and skip right to (4)

  2. Creating a new template file
    A template file can have any extension, but by phpBB standards, we prefer that you limit it to the extension .tpl (makes it compatible with most/all filesystems and allows other programmers to know where the templates are stored in an easier fashion). The file itself is plaintext/html, with just a couple of extra html-style tags that allow you to format your template data properly.

  3. Specifying a template file
    To begin with, you need to include the standard phpBB code (if it hasn't been included already) to gain access to the template, amongst other things.
    Code:
    define('IN_PHPBB', true);
    $phpbb_root_path = './';
    include($phpbb_root_path . 'extension.inc');
    include($phpbb_root_path . 'common.'.$phpEx);

    //
    // Start session management
    //
    $userdata = session_pagestart($user_ip, PAGE_PROFILE);
    init_userprefs($userdata);
    //
    // End session management
    //

    The template inclusion is fairly easy:
    Code:
          $template->set_filenames(array(
             'body' => 'your_template.tpl')
          );


    It's also a good idea at this point to get the header specified (if it hasn't been already), assuming you want the normal phpBB menu and stuff at the top that you get in all the regular phpBB pages. This will also let you set the title of the page:
    Code:
          $page_title = $lang['My_title']; // You'll have to set this in a $lang file somewhere
          include($phpbb_root_path . 'includes/page_header.'.$phpEx);

    Just be sure that you remember to render it at the end of the file:
    Code:
          $template->pparse('body');

    Arrow Don't do this before you've finished passing variables to the template engine... anything you do afterwards won't be included!
    Arrow The 'body' in pparse() corresponds to the 'body' in set_filenames()

    Finally, you want to include the page footer (if it hasn't been already):
    Code:
          include($phpbb_root_path . 'includes/page_tail.'.$phpEx);


  4. Basic template variables
    There are two sides to making a template variable. The first side is the .php side, to generate the data that goes into the variable. The second is the .tpl side, which shows where in the document the variable goes.
    • PHP-side
      Once you've specified your template file, you can set a template variable as follows:
      Code:
      $template->assign_var('VARIABLE' , $value );

    • Template-side
      Inside the template, you would put the associated variable wherever you will in the html, surrounded by curly braces { }:
      Code:
       {VARIABLE}

    The names of the template variables can contain both lower and uppercase letters (a-zA-Z), numerical digits (0-9), the hyphen (-), and the underscore (_), however, standard phpBB code uses only uppercase letters, numbers, and underscore.

    If you want to set more than one variable at the same time, you can do this as follows:
    • PHP-side
      Code:
      $template->assign_vars(array( 'VARNAME1' => $phpstringval1,
                                    'VARNAME2' => $phpstringval2,
                                    'VARNAME3' => $phpstringval3,
                                    ... etc ..
                                    'VARNAMEn' => $phpstringvaln
                                  )
                             );

    • Template-side
      Code:
       {VARNAME1} {VARNAME2} {VARNAME3} ... etc ... {VARNAMEn}

  5. Switches
    Switches are like "optional" parts of the template. They only appear if the switch is set. If the switch is not set, it's simply not shown.
    • PHP-side
      Creating a switch is pretty easy. Just test whether you want it turned on or not, then use the template's assign_block_vars() to set it accordingly:
      Code:
      if ( $userdata['session_logged_in'] )
      {
           $template->assign_block_vars('switch_user_logged_in',array() );
      }

    • Template-side
      Inside the template, you would set the associated tags as follows:
      Code:
      <!-- BEGIN switch_user_logged_in -->
      ... stuff that only shows up when the user is logged in...
      <!-- END switch_user_logged_in -->

      Arrow Note that it's important that the <!-- BEGIN ... --> and <!-- END ... --> appear on their own lines otherwise they might not work

    Unfortunately there is no "if - else" kind of switch, but there are ways of getting around that, as you'll see:
    • PHP-side
      Code:
      if ( $userdata['session_logged_in'] )
      {
           $template->assign_block_vars('switch_user_logged_in',array() );
      }
      else
      {
           $template->assign_block_vars('switch_user_logged_out',array() );
      }

    • Template-side
      Code:
      <!-- BEGIN switch_user_logged_in -->
      ... stuff that only shows up when the user is logged in...
      <!-- END switch_user_logged_in -->
      <!-- BEGIN switch_user_logged_out -->
      ... stuff that only shows up when the user is logged out...
      <!-- END switch_user_logged_out -->


  6. Loops
    Now we're going to start getting into some more complicated stuff. What happens if you want to show something more than once? You could duplicate the html in your template, which is easy enough, but what happens if this is non-static, and instead specified in the PHP?

    For example, lets say we want to repeat some text 10 times:
    • PHP-side
      This is very similar to making a switch, except you're calling the same string in assign_block_vars() multiple times.
      Code:
      $n = 10;
      for ( $i = 0; $i < $n; $i++ )
      {
           $template->assign_block_vars('my_loop',array() );
      }

    • Template-side
      This side is pretty much the same:
      Code:
      <!-- BEGIN my_loop -->
      This text will show up ten times
      <!-- END my_loop -->

    That was pretty simple. But it's not very useful... unless of course, you have different data in each of the loop iterations, which brings us to...

  7. Loop variables
    Have you been wondering what that empty array() in assign_block_vars() is all about? This is it. Lets say you want in each loop iteration to say which iteration it is, out of however many there are:
    • PHP-side
      similar to the way we had regular template variables, we specify loop variables inside the loop array()
      Code:
      $n = 10;
      for ( $i = 0; $i < $n; $i++ )
      {
           $template->assign_block_vars('my_loop',array( 'THIS_LOOP' => ($i + 1),
                                                         'TOTAL_LOOPS' => $n
                                                       )
                                         );
      }

    • Template-side

      Code:
      <!-- BEGIN my_loop -->
      This text will show up ten times (this is #{my_loop.THIS_LOOP} of {my_loop.TOTAL_LOOPS} )
      <!-- END my_loop -->

      Arrow An important thing to notice here is that before the variable, you need to put the loop name followed by a dot (.), if you don't, the template engine will thing it's a regular template variable, and (probably) won't find a match, and so it won't display anything.

    Exclamation You'll notice that the value of TOTAL_LOOPS is always the same. This is a bit of a waste, because we're storing TOTAL_LOOPS for each loop, but there's only one value. As long as it's within the same loop structure at a higher level, (if you don't understand what that means, don't worry) you can display variables that aren't in that loop's scope:
    • PHP-side
      Code:
      $n = 10;
      $template->assign_vars(array ( 'TOTAL_LOOPS' => $n ) );
      for ( $i = 0; $i < $n; $i++ )
      {
           $template->assign_block_vars('my_loop',array( 'THIS_LOOP' => ($i + 1) ) );
      }

    • Template-side
      Code:
      <!-- BEGIN my_loop -->
      This text will show up ten times (this is #{my_loop.THIS_LOOP} of {TOTAL_LOOPS} )
      <!-- END my_loop -->


    Here's another, more complicated (and more applicable) example:
    List all the users with user_id between 1 and 25
    • PHP-side
      Code:
      $sql = "SELECT user_id, username FROM " . USERS_TABLE . " WHERE user_id >= 1 AND user_id <= 25";
      if ( ! ( $result = $db->sql_query($sql) ) )
      {
         message_die(GENERAL_ERROR, 'Error retrieving user data', '', __LINE__, __FILE__, $sql);
      }
      while ( $row = $db->sql_fetchrow($result) )
      {
         $template->assign_block_vars('user_row',array( 'USERNAME' => htmlspecialchars($row['username']),
                                                        'USER_ID' => $row['user_id']
                                                      )
                                     );
      }

    • Template-side
      Code:
      <table>
      <!-- BEGIN user_row -->
      <tr><td>{user_row.USER_ID}</td><td>{user_row.USERNAME}</td></tr>
      <!-- END user_row -->
      </table>



  8. Nested loops/switches
    This is where it sometimes gets tricky, and code authors get confused. What happens if you want a loop or a switch inside another loop or switch?

    For example, you want to show a list of the first twenty-five users, and their emails, but only show the emails if the user looking at the page is an admin or a moderator? Let's add a bit to our last example:
    • PHP-side
      Code:
      $sql = "SELECT user_id, username, user_email FROM " . USERS_TABLE . " WHERE user_id >= 1 AND user_id <= 25";
      if ( ! ( $result = $db->sql_query($sql) ) )
      {
         message_die(GENERAL_ERROR, 'Error retrieving user data', '', __LINE__, __FILE__, $sql);
      }
      while ( $row = $db->sql_fetchrow($result) )
      {
         $template->assign_block_vars('user_row',array( 'USERNAME' => htmlspecialchars($row['username']),
                                                        'USER_ID' => $row['user_id'],
                                                        'USER_EMAIL' => $row['user_email']
                                                      )
                                     );
         if ( ($userdata['user_level'] == ADMIN) || ($userdata['user_level'] == MOD) )
         {
            $template->assign_block_vars('user_row.switch_admin_or_mod',array());
         }
      }

    • Template-side
      Code:
      <table>
      <!-- BEGIN user_row -->
      <tr><td>{user_row.USER_ID}</td><td>{user_row.USERNAME}</td>
      <!-- BEGIN switch_admin_or_mod -->
      <td>{user_row.USER_EMAIL}</td>
      <!-- END switch_admin_or_mod -->
      </tr>
      <!-- END user_row -->
      </table>


    Exclamation Some important things to notice are that in the PHP, the name of the switch is 'user_row.switch_admin_or_mod', but in the template, the name is only 'switch_admin_or_mod', which inside 'user_row'. This means that you can't create two switches in the same scope and expect them to work inside each other:
    • PHP-side
      Code:
      $template->assign_block_vars('switch_user_logged_in',array() );
      $template->assign_block_vars('switch_admin_or_mod',array() );

    • Template-side
      Code:
      <!-- BEGIN switch_user_logged_in -->
      <!-- BEGIN switch_admin_or_mod -->
      some text if it's an admin or mod, logged in
      <!-- END switch_admin_or_mod -->
      <!-- END switch_user_logged_in -->

    this won't work, because the template engine will look for 'switch_user_logged_in.switch_admin_or_mod', and it won't find it. One way to do get around this would be:
    Code:
    $template->assign_block_vars('switch_user_logged_in',array() );
    $template->assign_block_vars('switch_user_logged_in.switch_admin_or_mod',array() );

    Alternatively, you could have a separate switch altogether. There are actually several different wats of doing this properly, if you think about it.

  9. Nested loop variables

    Just so that you know this works (to probably any depth)

    Lets say for example you have an array of users, each of which has a name and id attribute, as well as a 'friends' attribute which is an array of the names of their friends, and you want to show the list of users, and who their friends are:

    • PHP-side
      Code:
      for ($i = 0; $i < count($dataset); $i++ )
      {
           $template->assign_block_vars('user_row', array( 'USER_ID' => $dataset[$i]['user_id'],
                                                           'USER_NAME' => $dataset[$i]['user_name']
                                                         )
                                       );
           for($j = 0; $j < count($dataset[$i]['friends']); $j++ )
           {
                $template->assign_block_vars('user_row.friend_row',array( 'NAME' => $dataset[$i]['friends'][$j] ) );
           }
      }

    • Template-side
      Code:
      <table>
      <!-- BEGIN user_row -->
      <tr><td>{user_row.USER_ID}</td><td>{user_row.USERNAME}</td><td><table>
      <!-- BEGIN friend_row -->
      <tr><td>{user_row.friend_row.NAME}</td></tr>
      <!-- END friend_row -->
      </table></td>
      </tr>
      <!-- END user_row -->
      </table>


  10. Assigning Vars from handle
    You can also create independant "sub" template files to avoid massive switch statements. For example, the pollbox in viewtopic. It either doesn't show up, shows up with the option to vote, or shows up with the vote results.
    • PHP-side
      The subtemplate takes normal template variables, so you can assign them as normal. To specify a subtemplate, you merely call on it as you would a normal template file.
      Code:
      if ( $poll_exists )
      {
         if ( $user_voted)
         {
            $template->set_filenames(array(
               'pollbox' => 'viewtopic_poll_result.tpl')
            );
         }
         else
         {
            $template->set_filenames(array(
               'pollbox' => 'viewtopic_poll_ballot.tpl')
            );
         }
         $template->assign_var('POLL_NAME' => $poll_name); // Fictional example
         for ($i = 0; $i < count($poll_options); $i++ )
         {
            $template->assign_block_vars('poll_option',array('OPTION_NAME' => $poll_options[$i]) ); // Another fictional example
         }
         $template->assign_var_from_handle('POLL_DISPLAY', 'pollbox');
      }

      Arrow the variables you want displayed in the subtemplate must be assigned before you call the assign_var_from_handle()
    • Template-side
      Just display it like a normal variable.
      Code:
      {POLL_DISPLAY}

    Arrow You can't assign loop variables from handle.
    Arrow The key in the set_filenames() call (ie, 'pollbox') must be identical to the value in the assign_var_from_handle() call.

That about wraps up this tutorial. I hope I've shed some light on this subject for you Smile

Username: Password:
News | Features | Demo | Downloads | Support | Community | Styles | Mods | Links | Merchandise | About | Home
 © Copyright 2002 The phpBB Group.