Simple Template System?

How to create simple template system like this for example.

Code


$data['result'] = $this->DB->getData();
$data['title'] = "this is a title!";
$this->view('home.tp',$data);

and the tp file look like this:


echo $title;
foreach($result as $row) .....

The view method could look like:

function view($Filename, $Data){
    extract($Data);
    include 'Templates/' . $Filename;
}

And your code would work.

Cheers…

How would you deal with session, for ‘user login checking’ between code and view?

I don’t think you need sessions in views. But you can send a boolean variable telling if the user is logged in to the view:

$data['loggedIn'] = (true === is logged in) ? true : false;

The most simple templating system is to use vsprintf() function.
Create an html file, insert placeholders in the form of %1$s, %2$s, etc…
Then generate array of replacement vars, pass it to your view object.

Your ‘view’ object would load template and run vsprintf($tpl, $aVars);
You have to make sure that number of vars in aVars matches the number of replacement vars in template.

This is simple and fast.

Guys… how would you design a simple Template System without using third party framework?

However you want :stuck_out_tongue:

It usually involves making your own framework, which is easier than it sounds. Here’s how I approach it.

First of all decide on a directory structure. You need a place to store your template files and your core application files. How I do it is have a plugins folder which allows you to literally drag and drop directories (containing models (objects storing database data) and action/template files) into the plugins directory to instantly install them.

My latest framework has the following directory structure:


-Root-
    .htaccess
    index.php
    Application/
        System/
            Classes/
                Application.php
                Request.php
            Abstract/
                Database.php
            Interfaces/
                Model.php
        Plugins/
            Content/
                Model.php
                Actions/
                    View.php
                    Add.php
                    Edit.php
            Products/
                Model.php
                Actions/
                    View.php
                    Add.php
                    Edit.phop

The .htaccess file redirects all requests which don’t point to an existent file into index.php. Index.php starts an Application object, passing superglobals and the request into a Request object. This allows you to test your application by creating dummy objects based on non-real requests.

The Application.php file is in charge of the application as a whole. It uses the Request.php file to route the request into a Model name, an Action and possibly an Identifier, for example:


Request: /Products/View/Some_Product : Model = Product; Action = View; Identifier = Some_Product
 
Request: / : Model = Content; Action = View; Identifier = Default
 
Request: /Products : Model = Product; Action = View; Identifier = Default

The Application object instantiates a Database object which is passed to the model to load data in. The model is loaded using an include if the file exists, otherwise a 404 page is shown.

Inside the plugin folder you’ll see two directories - Content and Products.

Inside these directories are files. The first file is called “Model.php” and this contains the object for the model. This model loads data from the database using the database object passed to it. The ‘Actions’ directory contains possible actions.

For example, the ‘view.php’ file would output the HTML to the browser displaying the content as you’d like. The ‘add.php’ file is in control of adding content, and displays a form if no form data is submitted, otherwise it handles it and may redirect if it desires.

For example, view.php:

<?php
$Model = new ContentModel();
$Model->LoadFromDatabase($Database, $Identifier); //$Database and $Identifier are set in the function which includes this action file, in Application.php
?>
<h2><?php echo $Model->getTitle(); ?></h2>
<?php echo $Model->getContent(); ?>

And add.php:

<?php
if($Request->Post('Action') == 'addContent'){
    $Model = new ContentModel();
    $Model->setTitle($Request->Post('Title'));
    $Model->setContent($Request->Post('Content'));
    if($Model->Save()){
        Redirect('Content', 'View', $Model->getTitle());
    }
}
?>
<form action="" method="post">
    <ul>
        <li>
            <label for="Title">Title</label>
            <input type="text" name="Title" />
        </li>
        <li>
            <label for="Content">Content</label>
            <textarea name="Content"></textarea>
        </li>
        <li>
            <input type="hidden" name="Action" value="addContent" />
            <input type="submit" value="Submit" />
        </li>
    </ul>
</form>
Off Topic:

This code is example - I’m not at my work laptop at the moment so I can’t give you a direct real-life example. So if you see mistakes… I apologise :stuck_out_tongue:

And that is, basically, it. There is obviously permission control involved too, but my posting time is limited at current.

It isn’t a perfect system, but it should help you see how to implement your own framework. Without a framework, views etc aren’t as useful as they would be with a framework, and writing your own framework means that you know your own system inside out.

There are two different mainstream approaches to views. The first is to use views specifically for output and do any processing elsewhere. The second is to do processing in the view - reclassifying it as an ‘action’ rather than a view. My favorite is the second, it adds less complication.

Why design yet another templating system? There are many already designed, just use them.
Try this one
http://www.phpsavant.com/
It looks good enough to me.

Here’s a little bit more complex templating class:

http://www.phpro.org/classes/Template-Class.html

Maybe you will find an inspiration there for your own.

What I did for mine was to create a template class which is responsible for the loading and caching of the templates.

I have a top template, a bottom template, and a middle template. The templates are written using PHP’s alternate syntax allowing for easy modification (straight HTML with only the minimum PHP required to output variables, do logic tests, unwind arrays, etc.).

Within the appropriate logic file for the functionality being requested, a template class object is created and the top, middle, and bottom template file variables are assigned to an array using the appropriate values stored in constants.

When it is time to load the templates and output content, a class method is called to load the templates. The main function calls a few other functions each of which does one discrete thing. ob_start() is called before any templates are included to buffer the output. This allows for caching if desired, string substitutions, or anything else I want to do with it. The top template is loaded using include(). Then the middle template is loaded, then the bottom. Including each template separately allows any one of the templates to be cached to file or the entire page. Additional logic or templates can be called from the middle template (or any of them) allowing for a high level of flexibility. Likewise, a different top or bottom template could be assigned based on the page being requested.

To output the entire page to the browser, each template (top, middle, and bottom) is appended to a variable which is echoed out at the appropriate time.


//  Simplified example class methods.

function load_template_sequential($template_files = array())
	{
		foreach ($this->template_array as $template)
		{
			$this->html_content .= this->include_template($template);
		}
                // Entire page could be cached using this variable.
		return $this->html_content;
                
	}

function include_template($template_file)
	{
		// 	Start the buffer
		ob_start();
		// 	Include the template file
		include ($template_file);
		// 	Assign the contents of the buffer to a variable
		$content = ob_get_contents();
		//	Flush the buffer clean.
		ob_end_clean();
                //  Individual template could be cached or a string substitution into another template for example.
		return $content;
	}

It’s quite simple and efficient to do it this way. If I want to output an error template, I just call a function like $obj->output_error(‘Text of error message.’) and that handles it in one line. As of now I don’t set http error headers. I could, I suppose, add another parameter to the function.

What I wanted was maximum flexibility and re-usability. There may be better ways. But I am an intermediate PHP coder at best. I am no expert and am always open to suggestions. :slight_smile:

When you get a templating system written that works well and allows flexibility, it really makes you much more efficient.

I consider mine pretty bare bone. It provides the ability to assign data, get data, fetch template HTML contents and that is pretty much it (besides for a few little goodies).


class Template  {
	
	private
	
	$_arrTemplateData = array()
	
	/*
	* styles and links extracted from modules
	* to be placed in head of master template file.
	*/
	,$_arrStyles = array()
	,$_arrLinks = array();

	public function __construct() {
	}
	
	/*
	* Assign a variable by name to scope of template
	*
	* @param str variable name within template
	* @param mix variable value
	*/
	public function assign($strName,$mixValue) {
		$this->_arrTemplateData[$strName] = $mixValue;		
	}
	
	/*
	* Get variable assigned to the template
	* 
	* @param str name
	* @return mix variable value
	*/
	public function getTemplateVars($strName) {
		return isset($this->_arrTemplateData[$strName])?$this->_arrTemplateData[$strName]:null;
	}
	
	/*
	* Get contents of a Template
	*
	* @param str template file path
	* @return str template contents
	*/
	public function fetch($strTemplateFile,Module $objModule=null) {
	
		if(!file_exists($strTemplateFile)) {
			trigger_error("Template file $strTemplateFile doesn't exist.");
		}
		
		$strFileContents = file_get_contents($strTemplateFile);
		
		ob_start();
		$this->_compileTemplate($strFileContents,$objModule);
		$strTemplateContents = ob_get_contents();
		
		/*
		* Remove all style and link tags from document
		* and store in array to be moved to head of master template.
		*/
		if($objModule !== null) {
			$strTemplateContents = preg_replace_callback('/(<style.*?>.*?<\\/style>|<link.*?>)/si',array($this,'parseTags'),$strTemplateContents);
		}
		
		ob_end_clean();
		
		/*
		* When executing master template embed links and styles in head of document
		*/
		if($objModule === null) {
			return preg_replace('/<\\/head>/',"\
".implode("\
",$this->_arrLinks)."\
".implode("\
",$this->_arrStyles)."\
</head>",$strTemplateContents,1);
		}
		
		return $strTemplateContents;
	
	}
	
	/*
	* Makes it possible for the template to return without
	* affecting anything. 
	* 
	* @param str file contents
	* @param obj module
	*/
	private function _compileTemplate($strFileContents,$objModule=null) {
		
		// Extract associated module variables into global scope
		extract($this->_arrTemplateData);
		if($objModule !== null) {
			extract($this->_arrTemplateData[$objModule->getName()]);
		}
		
		eval('?>'.$strFileContents);
	}
	
	/*
	* Parse out and isolate style and link tags
	* from executed template contents.
	*
	* @param arr pattern matches
	* @return str empty string
	*/
	public function parseTags($arrMatches) {
		
		/*
		* Determine appropriate array to place data in
		*/
		if(strpos($arrMatches[0],'<style') === 0) {
			$this->_arrStyles[] = $arrMatches[0];
		} else {
			$this->_arrLinks[] = $arrMatches[0];
		}
		
		return '';
	}
	
}

$template = new Template();
$template->assign('title','My Title');
echo $template->fetch('index.tpl');

/* or more practical */

$template->assign('CONTENT',$template->fetch('about.php'));
echo $template->fetch('master.php'); // where master echos $CONTENT global