PHP开发实例大全(提高卷) 中文完整pdf扫描版[244MB]
给smarty 中引入widget
转载请注明出处:(http://hi.baidu.com/yiqing95)
官网上面有一个实现了 可以参考之(http://www.smarty.net/forums/viewtopic.php?p=74711)
*
widget 是一个比较完整的功能单元,它封装了贯穿视图层到逻辑层,数据访问层的逻辑,可以在别的视图中反复使用;
常见的用途比如:页脚下的网站链接(可以从数据库中增删),最近上传的图片,最热门的评论。这样的逻辑可以被
其他视图包含,你可以在多处以黑盒的方式使用它;
使用方式:
{widget class='TopComments' maxDisplay='15'}
*
**
实现思路:
首先定义一个smarty插件函数, 在插件函数中实例化指定的类, 并调用“虚”方法run
<?php
function smarty_function_widget($params, $template) {
if (empty($params['class'])) {
throw new Exception('请指定Widget的类名称.');
}
/**
* 这个要求预定义widgets存放的路径 如果不存在用eval动态生成子类 此处不用它
*
if (file_exists($widgetClassFile = sprintf('%s.php', CONF_DIR_WIDGETS . strtolower($params['class'])))) {
require_once ($controller);
} else {
eval(sprintf('class %s extends SmartyWidget {}', $params['class']));
}
*/
//传递smarty 给Widget作为实例化参数
$params['smarty'] = $template->smarty;
//调用Widget子类的工厂方法生成类实例 ,所有的Widget类都继承根类 所以都有factory方法
$widget = call_user_func($params['class'] . '::factory', $params);
//调用Widget实例的run方法 子类可以复写改方法来实现特定于自己的功能
$widget->run();
}
//上面的内容改自那个官方文章 好像是供smarty3用的(对3.x不熟 我在使用 2.6)下面给出
<?php
function smarty_function_widget($params, &$smarty) {
if (empty($params['class'])) {
throw new Exception('Widget is missing name.');
}
/*
if (file_exists($widgetClassFile = sprintf('%s.php', CONF_DIR_WIDGETS . strtolower($params['class'])))) {
require_once ($controller);
} else {
eval(sprintf('class %s extends SmartyWidget {}', $params['class']));
} */
$params['smarty'] = $smarty;
if (class_exists($params['class'] )) {
$widget = call_user_func(array($params['class'] ,'factory'), $params);
$widget->run();
}else{
throw new Exception('Widget class '.$params['class'].'does not exist!.');
}
}
这里有一个问题需要注意就是类的自动加载 类必须能够找到 如果你要在某个页面中(view视图)使用某个widget那么可以考虑
在改页面对应的控制器中预先(include|require)(_once)? 该类 或者使用spl的自动类加载机制。这里假设你使用类MVC设计模式
所有的视图都是通过控制器来渲染的
**
***
SmartyWidget基类的设计:
该基类是所有widget的公共父类 ,包含大量通用逻辑和模板方法(GOF95中的模板方法设计模式的实现),主要提供了工厂方法用来创建
子类 重新设置smarty的模板路径。
Widget的文件结构遵从Yii的结构 每个widget类对应的视图路径位于和类文件同目录下的views文件夹
/MyWidget.php
/views/someViewName.php
一下是完整类
<?php
class SmartyWidget
{
/**
* @var null|\Smarty
*/
protected $smarty;
/**
* @var string
*/
protected $oldSmartyTplPath ;
/**
* @static
* @param array $params
* @return mixed
*/
public static function factory($params = array())
{
/*
* 使用了php5.3的后期静态绑定 版本限制!
$className = get_called_class();
return new $className($params);
*/
$className = $params['class'];
$smarty = isset($params['smarty']) ? $params['smarty'] : null;
unset($params['class'], $params['smarty']);
//实例化类
$widget = new $className($smarty);
$properties = $params;
foreach ($properties as $name => $value) {
$widget->$name = $value;
}
$widget->init();
return $widget;
}
//------------------------------------------------------------------------------------------------
/**
* @var array view paths for different types of widgets
* ----------------
* 静态变量做缓存
* ----------------
*/
private static $_viewPaths;
/**
* @param null $smarty
*/
public function __construct($smarty = null)
{
if ($smarty == null) {
include(SMARTY_DIR . 'Smarty.class.php');
$this->smarty = new Smarty();
$this->smarty->caching = true;
//其他相关配置 跟主视图tpl中smarty的配置可以也可以不一致
//比如模板编译目录 插件目录 smarty的配置路径等
} else {
//其实必要时可以选择clone当前smarty对象进行这样被类中的所有配置修改不会影响原始smarty实例
// 如果你在widget中进行了修改 那么接下来的使用会受前面设置的影响(比如新的模板路径 编译,插件配置路径等)
$this->smarty = $smarty;
}
}
/**
*初始化Widget类 这时widget其声明的公共变量已经被赋值了
*
*/
public function init()
{
}
/**
* 执行 widget.
*-------------------------------------------------------------------------
* 捕获某个widget的输出 种用法使得widget可以嵌套使用
* ob_start();
* ob_implicit_flush(false);
* $widget = MyWidget::factory($className,$properties);
* $widget->run();
* return ob_get_clean();
*---------------------------------------------------------------------------
*/
public function run()
{
}
/**
* Returns the directory containing the view files for this widget.
* The default implementation returns the 'views' subdirectory of the directory containing the widget class file.
* @return string the directory containing the view files for this widget.
*/
public function getViewPath()
{
$className = get_class($this);
if (isset(self::$_viewPaths[$className])) {
return self::$_viewPaths[$className];
} else
{
$class = new ReflectionClass($className);
return self::$_viewPaths[$className] = dirname($class->getFileName()) . DIRECTORY_SEPARATOR . 'views';
}
}
/**
*返回视图文件路径
* 由于修改自yii 它可以用多种模板 后缀的配置是在viewRender类中进行的
* 这里没有必要 简化起见 总是需要带后缀 或者你弄个配置文件 可以配置
* 模板后缀 使用时只是用模板名字
* --------------------------------
* $extension = getFromConfig('smartyTplSuffix');
*---------------------------------
* @param $viewName
* @return bool|string
*/
public function getViewFile($viewName)
{
$extension = '';
$viewFile = $this->getViewPath() . DIRECTORY_SEPARATOR . $viewName;
if (is_file($viewFile . $extension)) {
return $viewFile . $extension;
} else {
return false;
}
}
/**
* Renders a view.
*
* The named view refers to a PHP script (resolved via {@link getViewFile})
* that is included by this method. If $data is an associative array,
* it will be extracted as PHP variables and made available to the script.
*
* @param string $view name of the view to be rendered. See {@link getViewFile} for details
* about how the view script is resolved.
* @param array $data data to be extracted into PHP variables and made available to the view script
* @param boolean $return whether the rendering result should be returned instead of being displayed to end users
* @return string the rendering result. Null if the rendering result is not required.
* @throws CException if the view does not exist
* @see getViewFile
*/
public function render($view, $data = null, $return = false)
{
if (($viewFile = $this->getViewFile($view)) !== false) {
return $this->renderFile($viewFile, $data, $return);
} else {
throw new Exception(strtr('{widget} cannot find the view "{view}".',
array('{widget}' => get_class($this), '{view}' => $view)));
}
}
/**
* @param $sourceFile
* @param null $data
* @param bool $return
* @return string
* @throws Exception
*/
public function renderFile($sourceFile, $data = null, $return = false)
{
// 当前类的属性可以通过{this.property}访问
$data['this'] = $this;
//检查视图文件是否存在
if (!is_file($sourceFile) || ($file = realpath($sourceFile)) === false) {
throw new Exception(strtr('View file "{file}" does not exist.', array('{file}' => $sourceFile)));
}
//assign data
$this->smarty->assign($data);
//render or return
if ($return) {
//保存原始的模板路径 用完后恢复之
$this->oldSmartyTplPath = $this->smarty->template_dir;
//需要复写模板路径
$this->smarty->template_dir = '';
$content = $this->smarty->fetch($sourceFile);
//恢复smarty模板路径
$this->smarty->template_dir = $this->oldSmartyTplPath;
return $content;
} else {
//保存原始的模板路径 用完后恢复之
$this->oldSmartyTplPath = $this->smarty->template_dir;
//需要重置模板路径
$this->smarty->template_dir = '';
$this->smarty->display($sourceFile);
//恢复smarty模板路径
$this->smarty->template_dir = $this->oldSmartyTplPath;
}
}
/**
* Renders a view file.
* This method includes the view file as a PHP script
* and captures the display result if required.
* @param string $_viewFile_ view file
* @param array $_data_ data to be extracted and made available to the view file
* @param boolean $_return_ whether the rendering result should be returned as a string
* @return string the rendering result. Null if the rendering result is not required.
*/
public function renderInternal($_viewFile_, $_data_ = null, $_return_ = false)
{
// we use special variable names here to avoid conflict when extracting data
if (is_array($_data_))
extract($_data_, EXTR_PREFIX_SAME, 'data');
else
$data = $_data_;
if ($_return_) {
ob_start();
ob_implicit_flush(false);
require($_viewFile_);
return ob_get_clean();
}
else
require($_viewFile_);
}
}
***
****
使用方法:
1.将插件放入smarty的插件目录:Smarty/plugins/ ,文件名function.widget.php 看看其他的插件后缀是否带php自己调整
2.新建一个专门存放widget的目录 比如ROOT_DIR.'/widgets' 根目录下建立一个专用目录,将SmartyWidget.php 基类文件放入
其中;新建一个views目录用来存放模板。
3.新建一个类继承SmartyWidget类:
比如(TestWidget.php):
<?php
class TestWidget extends SmartyWidget{
public function run()
{
echo __FILE__,__METHOD__;
}
}
4. 在某个视图中引入此widget 比如:
<body>
<div>
hi这是widget测试!
<{widget class='TestWidget'}>
</div>
</body>
5.在你的浏览其中观看结果 ,
6.测试 带视图的情形 新建另一个测试子类:
<?php
class TestWidget2 extends SmartyWidget{
public function run()
{
$this->render('test2.html');
}
}
用法参考4 <{widget class='TestWidget2'}>
7. 测试公共变量传递
<?php
class TestWidget3 extends SmartyWidget{
public $someVar;
public function run()
{
echo $this->someVar;
}
}
用法同4 : <hr>
<{widget class='TestWidget3' someVar="hi this string will passe to the widget!"}>
观看输出情况
8. 最后一个测试请自己做吧 , 任务:测试变量传递到widget的视图 并测试$this->xxx 看好用不;
截图:
文件结构:
放入smarty插件路径:
视图层用法:
<div>
hi这是widget测试!
<{widget class='TestWidget'}>
<hr>
<{widget class='TestWidget2'}>
<hr>
<{widget class='TestWidget3' someVar="hi this string will passe to the widget!"}>
</div>
效果图:
****
****
后记 上面的策略均来自yii 模板设计模式的应用是体现在init方法和run方法上 这两个方法推迟到子类中实现了,一般在
run方法中可以调用$this->render('viewName.tpl',array('k1'=>$v1,'k2'=>$v2.....)); 上面的测试中你其实还可以从模型层
数据库查询东西 输出到widget的视图上 这个任务也留给感兴趣的朋友们了; widget 在各个主流框架中已经作为司空见惯的东西了 这个方案只是为了使得以前的项目也可以
应用widget 大家感兴趣可以参考下yii的widget实现 ,在ThinkSns中也实现了Widget类(可以下下来看下源码 它是开源的)
转载请注明:谷谷点程序 » yii widget 引入到smarty的环境中