所以我正在建立一个包含大量电子邮件和变量替换的系统,所以我正在编写一个类来管理存储在数据库中的模板的变量替换。
以下是一个简短的例子:
// template is stored in db, so that's how this would get loaded in
$template = "Hello, %customer_name%, thank you for contacting %website_name%";
// The array of replacements is built manually and passed to the class
// with actual values being called from db
$replacements = array('%customer_name%'=>'Bob', '%website_name%'=>'Acme');
$rendered = str_replace(array_keys($replacements), $replacements, $template);
现在,这对于单个var替换,基本的东西来说效果很好。但是,有些地方应该有一个for循环,而我却失去了如何实现它。
这个想法是有这样的模板:
“你好,%customer_name%,谢谢 请求{products}“
的信息
其中,{products}将是一个传递给模板的数组,该数组循环使用所请求的产品,格式如下:
我们的产品%product_name%有成本 %product_price%。了解更多信息 %product_url%。
因此,示例的渲染版本为:
“你好,鲍勃,谢谢你的要求 有关的信息:
我们的产品WidgetA的成本为1美元。 在example / A
了解更多信息我们的产品WidgetB的成本为2美元。 在example / B
了解更多信息我们的产品WidgetC的成本为3美元。 在example / C中了解更多信息。
实现这一目标的最佳方法是什么?
答案 0 :(得分:4)
好吧,我真的没有看到使用repalcements / regex的模板引擎中的重点
PHP已经是模板引擎,当您编写<?php echo $var?>
时就像执行<{$var}>
或{$var}
以这种方式思考,PHP已经通过其引擎将<?php echo '<b>hello</b>'?>
转换为<b>hello</b>
,所以为什么要让它完成所有操作2次。
我实现模板引擎的方式就是这样
首先创建一个模板类
class Template
{
var $vars = array();
function __set($key,$val)
{
$this->vars[$key] = $val;
}
function __get($key)
{
return isset($this->vars[$key]) ? $this->vars[$key] : false;
}
function output($tpl = false)
{
if($tpl === false)
{
die('No template file selected in Template::output(...)');
}
if(!file_exists(($dir = 'templates/' . $tpl . '.php')))
{
die(sprintf('Tpl file does not exists (%s)',$dir));
}
new TemplateLoader($dir,$this->vars);
return true;
}
}
这是您在登录时使用的内容,例如index.php,如果您不确定,您将像 stdClass 一样设置数据。当你运行输出命令时,它会将数据和tpl发送到下面的下一个类。
然后创建一个独立的类来编译tpl文件。
class TemplateLoader
{
private $vars = array();
private $_vars = array(); //hold vars set within the tpl file
function __construct($file,$variables)
{
$this->vars = $variables;
//Start the capture;
ob_start();
include $file;
$contents = ob_get_contents();
ob_end_clean(); //Clean it
//Return here if you wish
echo $contents;
}
function __get($key)
{
return isset($this->vars[$key]) ? $this->vars[$key] : (isset($this->_vars[$key]) ? $this->_vars[$key] : false) : false;
}
function __set($key,$val)
{
$this->_vars[$key] = $val;
return true;
}
function bold($key)
{
return '<strong>' . $this->$key . '</string>';
}
}
我们保持这个分离的原因是它有自己的空间来运行,你只需将你的tpl文件作为一个包装在你的构造函数中,这样它只能加载一次,然后当包含文件时它有权访问TemplateLoader中的所有数据和方法。
<?php
require_once 'includes/Template.php';
require_once 'includes/TemplateLoader.php';
$Template = new Template();
$Template->foo = 'somestring';
$Template->bar = array('some' => 'array');
$Template->zed = new stdClass(); // Showing Objects
$Template->output('index'); // loads templates/index.php
?>
现在我们真的不想将html和这个页面混合在一起,因为通过分离php和视图/模板,你确保所有的php都已经完成,因为当你发送html或使用html时它会阻止你的脚本的某些方面运行
header
<h1><?php $this->foo;?></h1>
<ul>
<?php foreach($this->bar as $this->_foo):?>
<li><?php echo $this->_foo; ?></li>
<?php endforeach; ?>
</ul>
<p>Testing Objects</p>
<?php $this->sidebar = $this->foo->show_sidebar ? $this->foo->show_sidebar : false;?>
<?php if($this->sidebar):?>
Showing my sidebar.
<?php endif;?>
footer
现在我们可以看到将html与php混合但是这没关系,因为你应该只使用Foreach,For等基本内容和变量。
注意:在TemplateLoader类中,您可以添加类似..
的功能function bold($key)
{
return '<strong>' . $this->$key . '</string>';
}
这将允许您在模板中增加您的操作,使其变为粗体,斜体,atuoloop,css_secure,striplashs ..
你仍然拥有所有常规工具,例如stripslashes / htmlentites等。
这是一个大胆的小例子。
$this->bold('foo'); //Returns <strong>somestring</string>
你可以在TempalteLoader类中添加很多工具,比如inc()来加载其他tpl文件,你可以开发一个帮助系统,这样你就可以去$this->helpers->jquery->googleSource
如果您有任何其他问题,请随时问我。
存储在数据库中的示例。
<?php
if(false != ($data = mysql_query('SELECT * FROM tpl_catch where item_name = \'index\' AND item_save_time > '.time() - 3600 .' LIMIT 1 ORDER BY item_save_time DESC')))
{
if(myslq_num_rows($data) > 0)
{
$row = mysql_fetch_assc($data);
die($row[0]['item_content']);
}else
{
//Compile it with the sample code in first section (index.php)
//Followed by inserting it into the database
then print out the content.
}
}
?>
如果你希望存储你的tpl文件包括PHP 那么这不是问题,在模板中传入tpl文件名只是搜索db而不是文件系统
答案 1 :(得分:0)
$products = array('...');
function parse_products($matches)
{
global $products;
$str = '';
foreach($products as $product) {
$str .= str_replace('%product_name%', $product, $matches[1]); // $matches[1] is whatever is between {products} and {/products}
}
return $str;
}
$str = preg_replace_callback('#\{products}(.*)\{/products}#s', 'parse_products', $str);
我们的想法是在{products}和{products}之间找到字符串,将其传递给某个函数,做任何你需要做的事情,迭代$ products数组。 无论函数返回什么,都会替换整个“{products} [这里的任何东西] {/ products}”。
输入字符串如下所示:
Requested products: {products}%product_name%{/products}