Chaining smarty templates and clean multiline strings - PHP - php

I know that the first part of this is subjective, but I'd like to hear some different techniques people use. This is a two part question: what do you use for complex multiline strings in PHP? And, can I use a composition type of relationship with smarty?
Question 1: I know there is heredoc and the "." operator. I'm looking for fresh, more readable ideas if there are any.
Question 2: To be more specific, here is what I would like to do with smarty.
Say I have a template, base.tpl:
<html>
<head><title></title></head>
<body>
{$main_content}
</body>
</html>
Can I chain templates, i.e. another template that represents $main_content, say main.tpl:
<div id="header">$header</div>
<div id="container">
<h1>{$first_header}</h1>
<p>{$first_paragraph}</p>
<h1>{$second_header}</h1>
<p>{$second_paragraph}</p>
I want in whatever.php to load one template into the other, so i.e.:
// ... including smarty and all other boiler plate things ...
$smarty -> assign('first_header', "Foo");
$smarty -> assign('first_paragraph', "This is a paragraph");
$smarty -> assign('second_header', "Bar");
$smarty -> assign('second_paragraph', "This is another paragraph");
$main_content = $smarty->load('main.tpl');
$smarty -> display('base.tpl');
I know that there is "template inheritance" in smarty, but I'm not familiar with it. Can it give me similar functionality to this?
Note: I think my biggest problem with heredoc is that I can't get syntax highlighting for html (if i specify html in the heredoc string). Without the highlighting, the html that I want to pass through smarty is much harder to read, which kind of defeats the purpose of smarty.

You'll want to use {include} to call templates (fragments) within a template.
http://www.smarty.net/docsv2/en/language.function.include.tpl
<html>
<head>
<title>{$title}</title>
</head>
<body>
{include file='page_header.tpl'}
{* body of template goes here, the $tpl_name variable
is replaced with a value eg 'contact.tpl'
*}
{include file="$tpl_name.tpl"}
{include file='page_footer.tpl'}
</body>
</html>
Passing variables into included templates:
{include file='links.tpl' title='Newest links' links=$link_array}
{* body of template goes here *}
{include file='footer.tpl' foo='bar'}
In terms of multi-line strings, I tend to use this pattern:
$my_string = "Wow, this is going to be a long string. How about "
. "we break this up into multiple lines? "
. "Maybe add a third line?";
As you said, it's subjective. Whatever you feel comfortable with and as long as its easily readable...

I found some documentation on template inheritance this morning...
To expand on my example above, you can have a base layout (base.tpl)
<html>
<head><title>{$title|Default="title"}</head>
<body>
<nav>{block name=nav}{/block}</nav>
<div id="main">{block name=main}Main{/block}</div>
<footer>Copyright information blah blah...</footer>
</body>
</html>
You can extend a template and override blocks now with the new version of smarty (home.tpl)
{extends file=base.tpl}
{block name=nav}
<ul style="list-style:none">
{foreach $links as $link}
<li>{$link.txt}</li>
{/foreach}
</ul>
{/block}
{block name=main}
{foreach $paragraphs as $p}
<h2>{$p.header}</h2>
<p>{$p.content}</p>
{/foreach}
{/block}
http://www.smarty.net/inheritance

Related

Smarty Template 3.1 ERROR: variable template file names not allow within {block} tags

these days I am trying to improve my templates by using the Smarty template engine and its OOP template tag {block}
Its working pretty good, but I also noticed when I use {block} - tags the {include file="[...]" variableX="[..]"} breaks when I use nested variables.
PHP Fatal error: [...] variable template file names not allow within {block} tags
Some examples:
WORKING (without nested variable inside "param")
{block name=CONTENT}
{assign "extra" value="test"}
DropDown: {include "dropdown.html" param="product_name" items=DS::PRODUCTS()}
{/block}
NOT WORKING #1 (with nested variable using `` )
{block name=CONTENT}
{assign "extra" value="test"}
DropDown: {include "dropdown.html" param="product_name_`$extra`" items=DS::PRODUCTS()}
{/block}
NOT WORKING #2 (with nested variable using {} )
{block name=CONTENT}
{assign "extra" value="test"}
DropDown: {include "dropdown.html" param="product_name_{$extra}" items=DS::PRODUCTS()}
{/block}
When I am using {include} without {block} all examples are ok
Question: is there any other method to allow nested variables or is it a known issue?
Thank you in advance
Simple answer is - don not use such fancy techniques. If you really have no way around it, this likely means your design idea is flawed and needs to be rethought, or, in case of Smarty, you need a longer inheritance chain.
Check something like this.
Index template(index.tpl):
<html><head>
<title>{block 'page-title'}Silly title{/block}</title>
</head><body>{block 'page-content'}Silly content{/block}</body></html>
Single article content(index-content.tpl):
{extends 'index.tpl'}
{block 'page-title'}{$title|escape}{/block}
{block 'page-content'}{$content|render}{/block}
List/paged article content(list-content.tpl):
{extends 'list-index.tpl'}
{block 'page-title'}{$title|escape}{/block}
{block 'pager'}{somepaging}{/block}
{block 'list-content'}{somecontent}{/block}
The wrapper that the list is inherited from(list-index.tpl):
{extends 'index.tpl'}
{block 'page-content'}
{block 'pager'}[1] [2] [3]{/block}
{block 'list-content'}x{/block}
{block 'pager'}[1] [2] [3]{/block}
{/block}
When I'm rendering a simple article, I call index-content.tpl. When I need a paged list, I call list-content.tpl. I never call any of the base templates directly.
However, if your theme requere to enable or disable specific blocks of content on a page, you can always wrap selection in {if} blocks. But honestly, I would urge you to reconsider your base design.

Codeigniter + Smarty integration, header/footer templating

I've implemented smarty on my CI installation via this tutorial: http://www.coolphptools.com/codeigniter-smarty
It works fine, except that the header and footer is loaded by including the files from within the template.
ie.
{include file="header.tpl" title="Example Smarty Page" name="$Name"}
...
{include file="footer.tpl"}
Is there a way to load them from the controller or from the Smarty library class?
To give a clearer example of what I want; Without a templating engine I would just extend the view method via a custom loader.
eg.
class MY_Loader extends CI_Loader {
function view( $template, $data = array(), $return = false ) {
$content = parent::view( 'header', $data, $return );
$content .= parent::view( $template, $data, $return );
$content .= parent::view( 'footer', $data, $return );
if( $content )
return $content;
}
}
This has always worked for me, but now I'm trying out smarty and I could not for the life of me figure out how to make it work like this one.
If anyone could point me to the right direction. That'd be great.
PS. Apologies if this has already been answered before, I've been Googling this for the past 2 hours and I can't seem to find anything. My PHP skills are intermediate at best.
I'm no expert but I did have to go through this not too long ago.
What I did is something like this:
header.tpl
<html>
<head>
</head>
<body>
content.tpl
{include file="header.tpl"}
{include file="$content"}
{include file="footer.tpl"}
footer.tpl
</body>
</html>
Then you could just have smarty call content.tpl always and pass the actual body content through $content.
Like I said, though, I'm not expert so the syntax might be off and there might be a more "correct" way of doing this, but I think the idea is there.
Hope that helps.
You should use the {extends} syntax to take advantage of template inheritance.
Start with your base template file (we'll call it template.php). This file will include your header and footer code, and will specify the location for the main content (and any other blocks of template code you'd like to specify, such as an aside for instance).
(I'm using really bare bones HTML here, just for example purposes. Please don't code like this.)
template.php:
<html>
<head>
<title>{$title|default:'Default Page Title'}</title>
<meta>
<meta>
</head>
<body>
<header>
<h1>My Website</h1>
<nav>
<!-- Navigation -->
</nav>
</header>
<section class="content">
{block name="content"}{/block}
</section>
<footer>
© 2013 My Website
</footer>
</body>
Then, your individual pages would {extend} the main template file, and specify the necessary content for all of the blocks. Note that you can make blocks optional and with default values, so there is no requirement to fill all blocks (unless you code it that way).
content_page.php:
{extends file="template.php"}
{block name="content"}
<h2>Content Page</h2>
<p>Some text.</p>
{/block}
you can user following way to include smarty tpl view in your controller:
$this->smarty->view('content.tpl')
and header.tpl in content page:
{include file='header.tpl'}

Can smarty block names be derived from smarty variables

I would like to define smarty block names according to smarty data, but I can't seem to do it.
Example:
{foreach $array as $code}
{block name=block_$code}
<div id='{$code}'></div>
{/block}
{/foreach}
My purpose is to extend a specific block_$code block by a child template. Is this possible or is there some other trick I could use to do this?
Thanks.
You can use the {assign} block and the cat modifier. For example
{foreach $array as $code}
{assign var=foo value="block_"|cat:$code}
{block name=$foo}
<div>
{/block}
{/foreach}
N.b. I've not tested this, but it should work. You might also be able to short-circuit this and just use {block name="block_"|cat:$code}.
I was able to find the following link from 2011 indicating that this wasn't possible at that time. I suspect it still isn't:
http://www.smarty.net/forums/viewtopic.php?t=19805&highlight=block%20variable%20name
The good news it that I was able to figure out how to make my code work without it. I wanted to be able to override just one of the divs defined by the foreach. Here's how I can do it:
Parent:
{foreach $array as $code}
{block name=code_loop}
<div>Normal Stuff</div>
{/block}
{/foreach}
Child:
{block name=code_loop}
{if $code == 'code of interest'}
<div>New Stuff</div>
{else}
{$smarty.block.parent}
{/if}
{/block}

How to get rid of whitespace in Smarty {block}s?

I've run into a problem where defining the value of a {block} introduces a lot of unnecessary whitespace.
I've a main template, let's call it main.html, which looks like this (simplified):
<html>
<title>{block name=title}{$default_title}{/block}</title>
...
</html>
Then I inherit from it in let's say topics.html, and I define the title block in it:
{extends file="main.html"}
{block title}
{if $topic}
{if $topic == "all"}
{eval $Config['titles']['topics']['all']}
{else}
{eval $Config['titles']['topics']['particular']}
{/if}
{else}
{eval $Config['titles']['topics']['list']}
{/if}
{/block}
Now when I compile the topics.html template, there is so much whitespace inside of <title>...</title> tag.
For example, it looks like this:
<title>
Showing all wiki topics </title>
How could I trim/strip the whitespace from the result of evaluating a block so it looked like the following:?
<title>Showing all wiki topics</title>
I tried adding {strip}...{/strip} around the {block title}...{/block} like this:
{strip}
{block title}
...
{/block}
{/strip}
But that didn't change anything.
I also tried this:
{block title|strip}
...
{/block}
But that was a syntax error. I also tried this:
{block title|trim}
...
{/block}
But it also was a syntax error.
I also tried:
{block title}
{strip}
...
{/strip}
{/block}
But that didn't help either as I already introduced a new-line after {block title}so it stays there in the compiled template.
Any help appreciated!
As of Smarty 3.1 you cannot wrap {block}s in other constructs. (This might change with Smarty 3.2)
Have you tried putting the {strip} tags inside the {block}s? Otherwise have a look at my answer here

Is something wrong with wrapping whole Smarty templates in {strip} tags?

I want to keep my templates tidy and nicely intented but would like to deliver only very compact HTML to the browser.
Missing a better idea, I was wondering if there is something wrong with wrapping whole Smarty templates in {strip} tags like so?
{strip}
<div class="albums">
<h2>Alle Foto-Alben</h2>
<ul class="photos clearfix">
{foreach $albums as $album}
{if $album->publishedPhotos|count > 0}
<li>
<div>
<a href="album.php?id={$album->id}">
<img src="{$album->titlepic->thumb}" alt="{$album->titlepic->title}" />
<span class="title">{$album->title}</span>
<span class="counter">{$album->publishedPhotos|count} Foto{if $album->publishedPhotos|count != 1}s{/if}</span>
</a>
</div>
</li>
{/if}
{/foreach}
</ul>
</div>
{/strip}
It smells a bit unprofessional to me but I could not come up with something better.
One downside definitely is that you have to wrap every single one of your templates in those tags.
I'm happy to be corrected and would love to hear different approaches to keeping delivered code compact.
While it's not wrong i would suggest using a pre-filter instead. A pre-filter only runs when the template is compiling (so it doesn't slow down the server) and you don't need to wrap every template in {strip}. The following rows of code is taken from a project I am working on. It effectively strips most whitespace.
/* Minify the html */
function smarty_pre_minify($tpl_source, $smarty) { return preg_replace('/[ \t\n\r]+/s', ' ', $tpl_source); }
$smarty->registerFilter('pre', 'smarty_pre_minify');
Nope, there's nothing wrong with this.
Do it in a header/footer pair of templates, rather than wrapping each page individually.
Alternately, you could trigger the stripping on the PHP side of things.
Notice: Striptags will ignore includes. Free tip of the day. :-)

Categories