- 自定义应用配置
- 如何应用视图模板
- 在这个例子中我们会小试视图布局
- 数据持久化存储
- 个性化的错误处理
文件目录说明
在helloworld的应用中,我们采用了windframework的默认配置。在这个实例中,我们将尝试自定义配置来构建 我们的应用。像helloworld一样,我们需要在根目录中创建一个文件夹blog作为应用目录,并把解压好的框架放到 blog/目录中。创建好的目录结构如下:
/var/www/blog/- wind/ 框架目录
- data/ 可写目录,用于存放编译后的模板,系统日志,缓存等
- conf/ 配置文件目录目录
- controller/ 应用控制器目录
- service/ 存放业务服务
- template/ 模板目录
- index.php 程序入口脚本
从以上的目录结构我们可以看出,比helloworld应用多了‘config.php’,‘service/’,‘data/’。‘config.php’用于 存放应用的自定义配置。而‘service’目录则是我们为Blog的业务进行了简单的分层,分为‘业务服务层’和‘视 图逻辑处理层’。将‘业务服务’统一放到‘service’目录中。而‘controller’下面则存放‘视图逻辑相关的处 理’。当然这种分层处理,只是本实例的写法。在实际的工程项目中,可以灵活的选取分层方案。‘data/’ 是一 个可写目录,用于存放模板编译后的文件(如果采用WindViewer视图渲染引擎,则会产生中间编译模板。在默认情 况下编译文件会放在data/compile/下)以及缓存日志等。
应用配置config.php
在应用目录下创建应用配置文件config.php。应用配置支持多种格式(xml/ini/php/properties)。应用配置中包括 ‘components’,‘web-apps’两个基本属性字段。‘components’是组件配置信息,wind框架有一份默认的组件配置 ‘wind/components_config.php’。默认应用会按照默认的组件启动。当应用配置中有components项时,他会重载掉系统 的组件定义。 ‘web-apps’属性标签下定义了应用的运行相关信息。具体内容如下:
*应用配置的具体说明可以参见配置介绍return array( //重载了系统组件中的db组件的定义,将db组件的config指向应用根目录下的db_config.php //我们可以通过这种方式重载任何系统组件的定义,也可以定义新的组件。组件名称不能重复。 //支持resource的配置方式 'components' => array( 'db' => array( 'config' => array( 'resource' => 'conf.config.php'))), //应用配置,支持多个应用配置。一个应用支持多个modules(业务模块),每个modules都有一个别名用于访问。 //当不输入任何modules时访问‘default’默认模块 'web-apps' => array( 'blog' => array( 'modules' => array( 'default' => array( //应用控制器访问路径定义,当前定义的路径是当前应用根目录下的‘controller/’ 'controller-path' => 'controller', //应用控制器后缀定义 'controller-suffix' => 'Controller', //模板目录定义 'template-dir' => 'template', //编译文件目录定义 'compile-dir' => 'data.compile', //错误处理句柄定义 'error-handler' => 'BLOG:controller.ErrorController',)), //过滤器配置,在这里部署了一个form表单过滤器 'filters' => array( 'user' => array( 'class' => 'WIND:web.filter.WindFormFilter', 'pattern' => 'default/Index/(login|dreg)', 'form' => 'BLOG:model.UserForm')))));
我们在上面的应用配置中,db组件配置引入了一个外部的配置资源文件“db_config”。内容如下:
return array( 'dsn' => 'mysql:host=localhost;dbname=test', 'user' => 'root', 'pwd' => 'phpwind.net', 'charset' => 'utf8');*这里db链接的表达形式采用pdo dsn方式。事实上我们的DB组件是基于PDO开发的。
入口脚本index.php
在应用目录下创建入口文件index.php,内容如下:
//定义错误报告级别 error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING); //定义框架运行模式,0为关闭debug define('WIND_DEBUG', 1); //加载框架 require_once '../../wind/Wind.php'; //注册路径别名,为service目录注册访问别名 Wind::register('SER','../../service'); //创建并启动应用,在这里我们为应用定义了一个名称‘blog’。并将对应的应用配置文件载入。 Wind::application('blog', 'config.php')->run();
用户服务
确定了工程目录结构,层次结构,应用配置等问题,我们需要来构建我们的用户服务。用户的登录,注册都是用户 相关的服务,我们暂且用一个 UserService 服务来进行封装。代码如下:
/** * @author Qiong Wu2012-3-15 * @copyright © 2003-2103 phpwind.com * @license http://www.windframework.com * @version $Id$ * @package demos * @subpackage blog.service */ class UserService { protected $cookieName = 'blogloginUser'; /** * 判断用户是否登录 * * @return boolean|UserForm */ public function isLogin() { /* @var $user UserForm */ $user = WindCookie::get($this->cookieName); if (!$user) return false; $stmt = $this->_getConnecion()->createStatement('SELECT * FROM user WHERE username=:username'); if (!$stmt->getValue(array('username' => $user->getUsername()))) return false; return $user; } /** * 用户退出 * * @return boolean */ public function logout() { return WindCookie::set($this->cookieName, '', -1); } /** * 用户登录服务 * * @param UserForm $userInfo * @return boolean */ public function login($userInfo) { $db = $this->_getConnecion(); $stmt = $db->createStatement('SELECT * FROM user WHERE username=:username AND password =:password'); if (!$stmt->getValue(array('username' => $userInfo->getUsername(), 'password' => $userInfo->getPassword()))) { return false; } } /** * 用户注册服务 * *@param UserForm $userInfo *@return boolean */ public function register($userInfo) { $db = $this->_getConnecion(); $stmt = $db->createStatement('SELECT * FROM user WHERE username=:username'); if ($stmt->getOne(array(':username' => $userInfo->getUsername()))) $this->showMessage('该用户已经注册.'); return $db->execute( "INSERT INTO user SET " . $db->sqlSingle( array('username' => $userInfo->getUsername(), 'password' => $userInfo->getPassword()))); } /** * @return WindConnection */ private function _getConnecion() { return Wind::getApp()->getWindFactory()->getInstance('db'); } }
上面代码中的UserForm,实际上是一个用户表单实体对象。在这里没有复杂的用户业务,而对于用户数据也只是建 立了简单的 “username”,“password”两个字段。所以并没有建立复杂的用户实体对象,而是共用了用户form 实体。Userform实体,还承担了用户数据的表单验证职责。UserForm.php 代码如下:
/** * 用户表单,供登录和注册时验证用 * * @author Shi Long* @copyright © 2003-2103 phpwind.com * @license http://www.windframework.com * @version $Id: UserForm.php 3403 2012-03-15 11:21:02Z yishuo $ * @package wind */ class UserForm extends WindEnableValidateModule { private $username; private $password; /** * @return string 返回用户名 */ public function getUsername() { return $this->username; } /** * @return string 返回用户密码 */ public function getPassword() { return $this->password; } /** * @param string $username */ public function setUsername($username) { $this->username = $username; } /** * @param string $password */ public function setPassword($password) { $this->password = $password ? md5($password) : $password; } /* (non-PHPdoc) * @see WindEnableValidateModule::validateRules() */ public function validateRules() { return array( WindUtility::buildValidateRule("username", "isRequired"), WindUtility::buildValidateRule("password", "isRequired")); } }
视图逻辑处理
实际上视图逻辑部分我们统一封装在controller层。我们在controller包中创建IndexController.php类文件,在这里 IndexController继承自WindController。WindController支持多action处理,即在一个controller里面封装可以封装一组 相关的acion处理,除了run方法以外(run是所有的controller的默认处理方法,无论是WindController类型还是 WindSimplerController类型)其它的方法均采用Action后缀方式命名,当然Action后缀不算作action名称的一部分,这 样处理只是出于安全的考虑。在具体的章节我们会讲解如何重载掉这个默认的行为。IndexController中我们定义了 “run”,“login”,“logout”,“reg”,“dreg”五个action,命名上似乎有些抽象。
run 默认首页login 登录业务处理
logout 退出业务处理
reg 引导用户进入注册界面
dreg 处理用户注册业务
IndexController代码如下。在controller的基类中我们定义了“beforeAction”,“afterAction”两个空方法,开发者可以通过重载这两个方法的实现,在每次请求的action之前或之后进行一些公共业务逻辑的处理。在这里我们重载了“beforeAction”这个方法,用于实现公共布局管理,全局变量定义等。
定义布局 | $this->setLayout('layout') | 这个方法用于定义布局管理,其中的参数‘layout’指向布局文件。它可以是一个绝对的路径地址,也可以是一个相对的路径地址,相对路径地址是相对当前定义的模板目录而言 |
设置模板 | $this->setTemplate('index') | 设置当前请求响应的模板文件,其中的参数‘index’表示模板文件名的路径地址,也可以是一个相对地址,相对地称。它可以是一个绝对的路径地址,也可以是一个相对地址,相对地址是相对当前定义的模板目录而言。默认的模板文件后缀为‘htm’。可以通过修改‘WindView’组件的相关配置来改变默认的模板文件后缀。*在之前的应用配置中我们统一配置了模板目录,编译目录。 |
设置变量输出 | $this->setOutput($userInfo,'userInfo'); | 变量输出,将变量输出到模板中。可以接受一个字符串(需指定key),数组,对象(没有指定key的情况下,数组的key作为变量访问的key)
例如: // 第一参数为变量值,第二个为key值,当指定了key值,则在模板中通过key值访问变量 $this->setOutput($userInfo, 'userInfokey'); //模板中访问如下 {$userInfokey} $array = array('a' => 'aaaaa' , 'b' => 'bbbbb'); $this->setOutput($array); //没有设置key值 //模板中访问如下 {$a} {$b} |
获取变量输入 | $this->getInput('userInfo'); | 获取输入变量,通过该方法可以获取 get,post,cookie,session的变量,支持类型指定。支持批量获取。
例如: $this->getInput('userInfo'); $this->getInput('userInfo','post'); //制定post类型的变量 list($a,$b,$c) = $this->getInput(array('a','b','c')); |
错误消息输出,与自定义错误处理句柄 | $this->showMessage('登录失 当我们发生一个错误时,可以调用该方法显示一段错误信息。我们在败.'); | 当我们发生一个错误时,可以调用该方法显示一段错误信息。我们在上面的应用配置中自定义了错误处理句柄‘ErrorController’。自定义错误句柄的实现需要继承自‘WindErrorHandler’类。如果不自定义错误处理,系统会调用‘WindErrorHandler’进行处理。 |
/** * 默认的 controller * * @author Shi Long* @copyright © 2003-2103 phpwind.com * @license http://www.windframework.com * @version $Id: IndexController.php 3403 2012-03-15 11:21:02Z yishuo $ * @package demos.blog.controller */ class IndexController extends WindController { /* (non-PHPdoc) * @see WindSimpleController::beforeAction() */ public function beforeAction($handlerAdapter) { parent::beforeAction($handlerAdapter); $this->setLayout('layout'); $this->setOutput('utf8', 'charset'); $this->setGlobal($this->getRequest()->getBaseUrl(true) . '/template/images', 'images'); $this->setGlobal($this->getRequest()->getBaseUrl(true) . '/template/images', 'css'); } /* (non-PHPdoc) * @see WindController::run() */ public function run() { Wind::import('service.UserForm'); $userService = $this->load(); $userInfo = $userService->isLogin(); $this->setOutput($userInfo, 'userInfo'); $this->setTemplate('index'); } /** * 访问用户注册页面 */ public function regAction() { $this->setTemplate('reg'); } /** * 用户登录 */ public function loginAction() { $userService = $this->load(); $userInfo = $userService->isLogin(); if ($userInfo) $this->showMessage('已登录~'); /* @var $userForm UserForm */ $userForm = $this->getInput("userForm"); if (!$userForm) $this->showMessage('获取用户登录数据失败'); if (!$userService->login($userForm)) $this->showMessage('登录失败.'); $this->forwardRedirect(WindUrlHelper::createUrl('run')); } /** * 处理用户注册表单 */ public function dregAction() { $userService = $this->load(); $userForm = $this->getInput("userForm"); if (!$userService->register($userForm)) $this->showMessage('注册失败.'); $this->setOutput($userForm, 'userInfo'); $this->setTemplate('reg'); } /** * 用户退出 */ public function logoutAction() { $this->load()->logout(); $this->forwardRedirect(WindUrlHelper::createUrl('run')); } /** * @return UserService */ private function load() { return Wind::getApp()->getWindFactory()->createInstance(Wind::import('service.UserService')); } } /** * 自定义errorController * * @author Shi Long * @copyright © 2003-2103 phpwind.com * @license http://www.windframework.com * @version $Id: ErrorController.php 3403 2012-03-15 11:21:02Z yishuo $ * @package wind */ class ErrorController extends WindErrorHandler { /** * (non-PHPdoc) * @see WindErrorHandler::run() */ public function run() { $this->setLayout('layout'); $this->setGlobal($this->getRequest()->getBaseUrl(true) . '/template/images', 'images'); $this->setGlobal($this->getRequest()->getBaseUrl(true) . '/template/images', 'css'); $topic = "Blog Error"; $this->setOutput($topic, "errorHeader"); $this->setOutput($this->urlReferer, "baseUrl"); $this->setOutput($this->error, "errors"); $this->setTemplate('error'); } }
视图模板与页面布局
关于视图模板上面讲到了,统一存放到‘template’目录,包括布局文件‘layout’。默认的模板后缀是‘htm’。我们 支持自定义的模板目录(力度细到你可以为你的每个module定义不同的模板路径),也支持绝对路径寻址。我想着几个 概念应该相对简单。下面我们给出一个布局文件和模板文件的例子:
<div class="wrap"> <div id="header" class="mb10"> <div class="header"> <table width="100%"> <tr> <td><h2 class="fl logo"><a href="{@WindUrlHelper::createUrl('default/index/run')}"><img src="{@G:images}/logo.png" width="198" height="80" class="fl" /></a></h2></td> <td align="right"><div class="login_header fr"> <dl class="cc login_dlA"> <dt></dt> <dd></dd> <dd> </dd> </dl> <dl class="cc login_dlA"> <dt>{@index_run:userInfo.username}</dt> </dl> </div></td> </tr> </table> <div id="navA"> <div class="navA"> <ul class="cc"> <li class="current"><a href="{@WindUrlHelper::createUrl('default/index/run')}"> 首页</a></li> <li><a href="{@WindUrlHelper::createUrl('default/index/run')}">关于本demo</a></li> <li class="tail"> </li> </ul> </div> </div> </div> </div> <div class="main"> </div> <div id="footer"> <div class="footer"> <p class="f10">Powered by phpwind windframework group ©2003-2103 http://www.windframework.com</p> </div> </div> </div>
上面是一个布局文件的示例代码。布局文件实际上也是一个html模板文件,不同的是通过设置布局文件,我们可以为一组页面设置公共的页面结构(统一的头部,尾部)。我们展示的这个布局文件,并未进行非常细粒度的页面切片。事实上我们可以将页面中的 ‘head’,‘header’,‘footer’用子模板切片的方式进行包含访问和管理,达到更灵活的部署需求。我们应该注意到很高亮的几行代码,它描述的是加载模板的主体内容。上面这份代码示例中还为我们展示了几个很重要的模板标签用法(关于模板标签是一个很有意思的概念,虽然系统默认只提供了少数的几个标签供使用,但是它提供了非常易用的扩展方案实现方案。它给模板布局,业务部署带来了非常大 的灵活和便利)。一下介绍本模板涉及到的标签:
模板变量访问 | {$var} {@$var} {@G:var1.var2} |
这是几个模板变量访问方式,{$var} 这个是最常用的方式。{@$var} 这个是对模板变量访问的一个加强实现,他不仅支持模板变量,而且支持常用表达式。{@G:var1} 这种标签是指定了一个模板域来进行变量查找访问,支持跨模板的变来那个访问,‘G’可以用一个模板名称替换。在这里‘G’代表全局变量。 实际上该布局模板中访问了几个全局变量,这几个全局变量在IndexController中的beforeAction中进行了定义。 例如: {@index_run:userInfo.username} 的username变量。 |
总结: 以上我们基于blog应用,介绍了一些框架的用法。我们来介绍这个blog应用,不是想去说明一个web应用如何去开发。针对于应用业务逻辑处理方面我们没有做过多的设计和介绍。我们意在通过这个实例展示wind框架的使用方式。问题反馈:https://github.com/phpwind/windframework/issues