关于Yaf Plugins的介绍资料都比较简单,介绍请看文档,下面说说使用的实例。
新建一个 Publicblock 文件 application/plugins/Publicblock.php :
现将上面的方法注释,去 Plugin 中实现,并能根据不同的 module 实现不同的 layout:
如果为了避免部分 Action 未设置 TDK 而影响页面需使用默认值时,在主控制器中设置默认值:
Rewrite.php
以上是实际中使用 Plugin 解决的方法。
plugin 的路径位于 application/plugins/
一、验证登录
由于不是所有的控制器都需要验证是否登录,在此选择 routerShutdown 路由完成后对具体访问的控制器作是否登录的判断,在 Bootstrap 中注册一个插件:
class Bootstrap extends Yaf_Bootstrap_Abstract
{
public function _initPlugins(Yaf_Dispatcher $dispatcher)
{
$dispatcher->registerPlugin(new PublicblockPlugin());
}
}
新建一个 Publicblock 文件 application/plugins/Publicblock.php :
class PublicblockPlugin extends Yaf_Plugin_Abstract
{
/**
* 是否登录
*/
public function routerShutdown(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response)
{
$curModule = ucfirst(strtolower($request->module));
$curController = ucfirst(strtolower($request->controller));
//这里区分了 module, 也可直接使用 $curModule == 'Index',如果没有设置 module 可取消判断
if( $curModule == ucfirst($config->application->dispatcher->defaultModule) ){
$checkLogin = array('Apply', 'User'); //需要验证登录的controller
$freeAction = array('payreturn', 'paynotify'); //如果验证的 Controller 中有无需验证登录的 action
if(in_array($curController, $checkLogin)){
//ucenter 的验证方式
$userInfo = isset($_COOKIE['site_author']) ? UTools::getUserLoginStatus($_COOKIE['site_author']) : array();
//如果要区分Ajax, $request->isXmlHttpRequest() 在此不生效
if(!isset($userInfo['uid']) && !in_array($request->action, $freeAction)){
$ajaxAction = array('submit'); //Ajax 请求的 Action
if (in_array($request->action, $ajaxAction)) {
CTools::ajaxReturn(0, '请您先登录');
}else{
header('Location: http://localhost/logging.php');
exit;
}
}
}
}
}
}
二、将 Layout 实例化移出 Bootstrap 在Plugin中实现,并支持页面 TDK 的方式:
在 Bootstrap 中的 _initLayout
public function _initLayout(Yaf_Dispatcher $dispatcher)
{
$layout = new Layout(Yaf_Application::app()->getConfig()->application->layout->directory);
$dispatcher->setView($layout);
}
现将上面的方法注释,去 Plugin 中实现,并能根据不同的 module 实现不同的 layout:
class PublicblockPlugin extends Yaf_Plugin_Abstract
{
public $layout;
public function dispatchLoopStartup(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response)
{
try {
$config = Yaf_Application::app()->getConfig();
$modulesArr = Yaf_Application::app()->getModules();
$layoutPath = $config->application->layout->directory;
//根据 module 设定 layout
if(in_array($request->module, $modulesArr) && $request->module != ucfirst($config->application->dispatcher->defaultModule)){
$layoutPath = str_replace('views', 'modules/'. $request->module .'/views', $layoutPath);
}
$this->layout = new Layout($layoutPath);
//如果需要设置 SEO信息, 避免改动各页面相关的控制器
//$this->_seoSets($request, $response);
Yaf_Application::app()->getDispatcher()->setView($this->layout);
} catch (Exception $e) {
//异常处理
CTools::processException($e);
}
}
/**
* SEO TDK
*/
protected function _seoSets($request, $response)
{
//Key: Controller_Action
$_seoRule = array(
'Index_index' => array(
'title' => '【标题】文字_%city站',
'keywords' => '关键词',
'description' => '文字_%city站'
),
'List_index' => array(
'title' => '【标题】第%page页_%city站',
'keywords' => '关键词',
'description' => '文字_%city站'
)
);
try {
//如果开启了重写需处理一下
$curAction = strpos($request->action, '.html') ? str_replace('.html', '', $request->action) : $request->action;
$curModule = ucfirst(strtolower($request->module));
$curController = ucfirst(strtolower($request->controller));
if($curModule == 'Index'){
//定位城市
$cityInfo = CTools::getCurrentCity();
$cityName = isset($cityInfo['cityName']) ? str_replace('市', '', $cityInfo['cityName']) : '北京';
$_seoVal = array();
$_accessKey = $curController .'_'. $curAction;
if(isset($_seoRule[$_accessKey])){
$_rule = $_seoRule[$_accessKey];
/* TDK 参数, CUrl 见前面的文章介绍 */
if(CUrl::isOpenRewrite()){
$page = (int) $request->getParam('page');//页码
}else{
$page = (int) $request->getQuery('page');//页码
}
$pageStr = $page > 0 ? '_第'. $page .'页' : '';
foreach ($_rule as $key=>$val)
{
//使用 sprintf 不大好处理,故使用 %变量名
$_seoVal[$key] = str_replace(array('%page', '%city'), array($pageStr, $cityName), $val);
}
}
$_seo = CTools::getSeoCode($_seoVal);
//assign
$this->layout->assign('_seo', $_seo);
}
} catch (Exception $e) {
CTools::processException($e);
}
}
}
<!DOCTYPE html>
<html>
<head>
<?=$this->_seo ?>
</head>
如果为了避免部分 Action 未设置 TDK 而影响页面需使用默认值时,在主控制器中设置默认值:
class ApplicationController extends Yaf_Controller_Abstract
{
public function init()
{
//...
//Set SEO
if(!$this->getView()->getVar('_seo'))
$this->getView()->_seo = CTools::getSeoCode(array('title' => 'site'));
}
}
三、Rewrite
在项目开发完成,链接已经 Rewrite 后,SEO 要求在域名后面接城市字母简拼为前缀,即将 http://localhost/index.html 改成 http://localhost/bj/index.html ,如果改 Rewrite 规则麻烦,还得各种测试,于是利用 Plugin 实现,在 Bootstrap 中注册一个插件:
public function _initPlugins(Yaf_Dispatcher $dispatcher)
{
$dispatcher->registerPlugin(new RewritePlugin()); //注册 RewritePlugin
$dispatcher->registerPlugin(new PublicblockPlugin());
}
Rewrite.php
<?php
/**
* 链接处理
* 解决以城市拼音 为开头 被当做 控制器名解析
* /bj/index.html
*
* @author Shixue
* @date 2017-11-10
* Yaf version 2.3.5
*/
class RewritePlugin extends Yaf_Plugin_Abstract
{
/**
* 路由之前处理
* HTTP/1.1 200 OK
* HTTP/1.1 301 Moved Permanently
* HTTP/1.1 404 Not Found
*/
public function routerStartup(Yaf_Request_Abstract $request, Yaf_Response_Abstract $response)
{
$uri = addslashes($request->getServer("REQUEST_URI"));
list($uriLink) = explode('#', $uri);
list($uriUrl) = explode('?', $uriLink);
$urlArr = explode('/', trim($uriUrl, '/'));
$city = $urlArr ? strtolower(array_shift($urlArr)) : '';
//获取所有城市拼音数据
$cityData = CTools::getCityPinyin();
//当前定位的或选择的城市
$cityInfo = CTools::getCurrentCity();
if(empty($city) || !array_key_exists($city, $cityData) ){
$notCity = !$city || !preg_match('/(Ajax|pay|app|add)/i', $city) ? true : false; //排除 Ajax 和 带 .html
if($notCity){
$listname = isset($cityInfo['listname']) && $cityInfo['listname'] ? trim($cityInfo['listname']) : 'bj';
//header("HTTP/1.1 301 Moved Permanently");
header('Location: /'. $listname . $request->getServer("REQUEST_URI"));
exit;
}else{
array_unshift($urlArr, $city);
}
}else{
//设置城市ID 和 listname
//$currentCityId = (int) $cityData[$city];
//$request->setParam('listname', $city);
//$request->setParam('cityId', $currentCityId);
//var_dump($request->getParam('listname'));exit;
}
$newUri = '/'. implode('/', $urlArr);
$request -> setRequestUri($newUri);
}
}
以上是实际中使用 Plugin 解决的方法。
Layout 中自定义了一个 getVar() 方法,/application/library/Layout.php 文件如下:
<?php
/**
* Layout class used for render layouts and views.
*
* Layout class allows to use of a base layout skeleton and render views inside
* this layout template.
* The concept is to not render and display the view template directly but storing it
* in {@link $content} property. Then will render the skeleton layout and
* pass the {@link $content} property to it.
* <code>
* application/views/layouts/front.phtml
*
* <html>
* <head>
* <title><?php echo $title ?></title>
* </head>
* <body>
* <?= $_content_ // This is where your view from each action
* will be displayed ?>
* </body>
* </html>
* </code>
*
* If no layout is defined then returns the renderd action view template.
*
* Also allows to set variable to views and access them from the base
* skeleton layout. In above layout $title variable is set to title tag.
* Then in a view template we can set the value for $title variable.
* <code>
* application/views/index/index.phtml
*
* <?php $this->title = "Index Action" ?>
* <h1>Index Action</h1>
* </code>
*
* @author Andreas Kollaros <mail@dot.com>
*
*/
class Layout implements Yaf_View_Interface
{
/**
* The template engine to render views and layout templates.
*
* Default engine is Yaf\View\Simple
*
* @var Yaf\View\Simple
*/
public $engine;
/**
* Options to be passed to template engine.
*
* @var array
*/
protected $options=array();
/**
*
*/
protected $layout_path;
/**
* The name of layout file without extension.
*
* @var string
*/
protected $layout;
/**
* Handles the rendered action view data.
*
* @var string
*/
protected $content;
/**
* Array with assigned template variables.
*
* @var array
*/
protected $tpl_vars = array();
/**
* Template directory.
*
* @var string
*/
protected $tpl_dir;
/**
* Constructor
*
* @param array $options key/value pair of options to be assigned to
* template engine.
*
* @return void
*/
public function __construct($path, $options=array())
{
$this->layout_path = $path;
$this->options = $options;
}
/**
* Return the instance of a template engine.
*
* @return Yaf\View\Simple
*/
protected function engine()
{
$this->engine = $this->engine ? $this->engine : new Yaf_View_Simple(
$this->tpl_dir,
$this->options
);
return $this->engine;
}
/**
* Create engine instance and set the path of views and layout templates.
*
* Layout path is set by default to layout directory inside views path.
*
* @param string $path The directory to set as the path.
*
* @return void
*/
public function setScriptPath($path)
{
if (is_readable($path)) {
$this->tpl_dir = $path;
$this->engine()->setScriptPath($path);
// Overwirte layouts path by setting it where views path is.
// This will force layout in modules to be placed in
// modules/views/layouts directory
$this->layout_path = $path . "/layouts";
return true;
}
throw new Exception("Invalid path: {$path}");
}
/**
* Getter method for views path.
*
* @return string
*/
public function getScriptPath()
{
return $this->engine()->getScriptPath();
}
/**
* Setter for Layout::layout variable
*
* @param string $name the name of layout file without extension.
*
* @return void
*/
public function setLayout($name)
{
$this->layout = $name;
}
/**
* Getter for Layout::layout variable
*
* @return string the name of layout file without extension
*/
public function getLayout()
{
return $this->layout;
}
public function setLayoutPath($path)
{
$this->layout_path = $path;
}
/**
* Get full layout path with filename and extension.
*
* @return string
*/
public function getLayoutPath()
{
$config = Yaf_Application::app()->getConfig()->get('application');
return $this->layout_path . "/" . $this->layout . ".{$config->view->ext}";
}
/**
* Assign a variable to the template
*
* @param string $name The variable name.
* @param mixed $value The variable value.
*
* @return void
*/
public function __set($name, $value)
{
$this->assign($name, $value);
}
/**
* Allows testing with empty() and isset() to work
*
* @param string $name
*
* @return boolean
*/
public function __isset($name)
{
return (null !== $this->engine()->$name);
}
/**
* Allows unset() on object properties to work
*
* @param string $name
*
* @return void
*/
public function __unset($name)
{
$this->engine()->clear($name);
}
/**
* Assign variables to the template
*
* Allows setting a specific key to the specified value, OR passing
* an array of key => value pairs to set en masse.
*
* @see __set()
*
* @param string|array $name The assignment strategy to use (key or
* array of key => value pairs)
* @param mixed $value (Optional) If assigning a named variable,
* use this as the value.
*
* @return void
*/
public function assign($name, $value = null)
{
$this->tpl_vars[$name] = $value;
$this->engine()->assign($name, $value);
}
public function getVar($name)
{
return isset($this->tpl_vars[$name]) ? $this->tpl_vars[$name] : null;
}
/**
* Assign variables by reference to the template
*
*/
public function assignRef($name, &$value)
{
$this->tpl_vars[$name] = $value;
$this->engine()->assignRef($name, $value);
}
/**
* Clear all assigned variables
*
* Clears all variables assigned to Yaf\View either via
* {@link assign()} or property overloading
* ({@link __get()}/{@link __set()}).
*
* @return void
*/
public function clearVars() {
$this->tpl_vars = array();
$this->engine()->clear();
}
/**
* Processes a view and returns the output.
*
* This method called once from controller to render the given view.
* So render the view at $this->content property and then render the
* layout template.
*
* @param string $tpl The template to process.
* @param array $tpl_vars Additional variables to be assigned to template.
*
* @return string The view or layout template output.
*/
public function render($tpl, $tpl_vars=array()) {
$tpl_vars = array_merge($this->tpl_vars, $tpl_vars);
$this->content = $this->engine()->render($tpl, $tpl_vars);
// if no layout is defined,
// return the rendered view template
if (null == $this->layout) {
return $this->content;
}
// If we assign some variables into view template, then maybe we need
// them to layout view.
// Hack??
// Get template vars from view via Reflection class and assign them to
// layout view
#$ref = new ReflectionClass($this->engine());
#$prop = $ref->getProperty('_tpl_vars');
#$prop->setAccessible(true);
#$view_vars = $prop->getValue($this->engine());
#$tpl_vars = array_merge($tpl_vars, $view_vars);
$tpl_vars['_content_'] = $this->content;
return $this->engine()->render(
$this->getLayoutPath(),
$tpl_vars
);
}
/**
* Directly display the constens of a view / layout template.
*
* @param string $tpl The template to process.
* @param array $tpl_vars Additional variables to be assigned to template.
*
* @return void
*/
public function display($tpl, $tpl_vars=array()) {
echo $this->render($tpl, $tpl_vars);
}
}