多层控制器的使用
TP3.23对控制器做了更加细致的分层,除了默认的Controller
层,还可以自定义事件控制层Event
。
建立的方法是:
- 在模块第一级目录,即
Controller
层的同级目录,新建文件夹Event
- 在
Event
文件夹中,新建文件AdminEvent.class.php
- 在新建文件中输入代码
namespace Admin\Event;
// use Think\Controller;
// 如果不使用到controller的功能
// 可以不用添加该命名空间引用 也不用继承controller
class AdminEvent extents Controller {
public function test() {
echo 'Hello World !';
}
}
系统默认的控制器(即访问控制器)是Controller
,因此在浏览器输入http://localhost/forum/index.php/Admin/admin/test
是无法进行访问的,Event
控制器仅在内部方法方法中进行访问。当然可以通过DEFAULT_C_LAYER
来修改访问控制器的名称。但建议不这么做。
定义了事件控制器,对系统模块的构建有很大的好处。可以将浏览器的请求与内部事件处理相隔离,使整个业务逻辑看上去更为简洁明了。
如一个后台系统,有多个页面,都需要对用户是否登录进行检测,如果这个检测都在Controller
进行的话,整个业务逻辑会显得很混乱,而且可能会有代码冗余。
这时可以通过Event
事件控制器来定义一个检测方法,然后在Controller
层进行调用,这样代码不仅更为简练,业务逻辑也更为清晰。
Event控制器
namespace Admin\Event;
use Think\Controller;
class AdminEvent extents Controller {
/**
* 登录检测 检测session中用户
* @param 类型 参数名 含义
* @return 类型
*/
function checkLogin() {
$value = session('UserName');
if(!isset($value)) {
$this->error('请登录后访问' , U('admin/login'));
}
}
}
Controller控制器
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
/**
* 后台首页面
*/
public function index(){
A('Admin' , 'Event')->checkLogin();
$this->assign('title','后台');
$this->display();
}
/**
- 文章显示控制
- @param
- @return
*/
public function article() {
A('Admin' , 'Event')->checkLogin();
$this->assign('title','文章-后台');
$this->display();
}
}
这样无论是从那种链接登入后台,都会进行用户检测,不仅提高了安全性,而且代码逻辑清晰。
我们可以将数据库添加操作在Event
控制器中完成,在Controller
控制器只进行模板输出,接受表单提交。
以上。
初始化操作
在Think\Controller.class.php
的构造方法中进行视图类实例化后,会检测是否存在一个_initialize
方法。如果存在,就会事先调用该方法。
这就是控制器的初始化操作。在控制器类中定义_initialize
方法,在操作其他方法之前都会先执行该方法。
如:
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
# 初始化操作
public function _initialize() {
echo '页面初始化,请等待<br/>';
}
# 主页
public function index() {
echo 'index';
}
}
在浏览器输入http://localhost/forum/index.php/Admin
,则会输出
页面初始化,请等待
index
有了这个方法,我们上面所讲的登录检测就能够再次进行简化了。即在_initialize
方法中进行是否登录的检测。但在_initialize
添加了登录检测后,我们不能够将与登录有关的内容放入AdminController
控制器中了,否则会不停的跳转到error页面,而无法显示登陆页面。
我们可以将登录
与注册
同时拿出,新建一个控制器,名字叫UserController
。在这个页面进行登陆与注册功能,而error页面跳转到该页面。
UserController
namespace Admin\Controller;
use Think\Controller;
class UserController extends Controller {
/**
* 用户登录
*/
public function login() {
echo '用户登录';
}
/**
* 用户注册
*/
public function register() {
echo '用户注册';
}
}
AdminController
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
# 初始化操作
public function _initialize() {
A('Admin' , 'Event')->checkLogin();
}
/**
* 后台首页面
*/
public function index(){
$this->assign('title','后台');
$this->display();
}
/**
- 文章显示控制
- @param
- @return
*/
public function article() {
$this->assign('title','文章-后台');
$this->display();
}
}
AdminEvent
namespace Admin\Event;
use Think\Controller;
class AdminEvent extents Controller {
/**
* 登录检测 检测session中用户
* @param 类型 参数名 含义
* @return 类型
*/
function checkLogin() {
$value = session('UserName');
if(!isset($value)) {
$this->error('请登录后访问' , U('user/login'));
}
}
}
这样一个比较完善的后台登陆就完成了(当然还未实现具体的业务逻辑),比较最原始的方法,代码简洁清晰了很多,也完全符合MVC
设计思想。
前置与后置
与初始化设置类似,TP提供了前置与后置操作,要进行前后置操作需要真实的方法,系统在执行该方法前会进行检测,如果定义了前后置操作,则会安照顺序进行。定义的方法是在执行方法前加入_before_
或_after_
,如
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
public function _before_index() {}
public function index() {}
public function _after_index() {}
}
在访问http://localhost/forum/index.php/Admin
的时候,就会先执行_before_index
,在执行index
,最后执行_after_index
。注意的是如果在index
中定义了断点方法,如die
,exit
,跳转方法error
,success
就不会继续执行_after_index
。
这个前后置方法,我暂时还未想到有什么比较有用的地方,先写出来,以后想到了,在进行添加。
空操作与空控制器
按照ThinkPHP
的URL
命名规则(pathinfo模式),入口文件之后的操作成为
模块
控制器
操作
如http://servername/index.php/模块/控制器/操作/[参数名/参数值…]
空操作
就是指系统找不到url指定的操作方法,此时就会报错,可以使用定义空操作方法来进行避免。如:
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
/**
* 后台首页面
*/
public function index(){
$this->assign('title','后台');
$this->display();
}
/**
- 空操作防止
- @param 无
- @return 无
*/
public function _empty() {
$this->display('404');
}
}
定义后,在输入错误的操作方法时,就会显示_empty
中的内容。你可以定义一个404页面
,在_empty
中进行跳转。
空控制器
与空操作类似,访问不存在的控制器就会产生空控制器操作,避免该错误的方法是,在Controller
中定义一个EmptyController
控制器,在该控制器中同样定义_empty
方法。
namespace Admin\Controller;
use Think\Controller;
class EmptyController extends Controller {
/**
- 空操作防止
- @param 无
- @return 无
*/
public function _empty() {
$this->display('admin/404');
}
}
404页面
对于404
页面的定制,如果没有特殊需求的同学,建议使用腾讯的404公益页面
,只要嵌入一小段JS
代码,就会跳转到腾讯的寻找失踪儿童的404
页面。虽然可能和你的网站风格不统一,但是每多一个看见该页面的人,就可能加大找回一个失踪儿童的几率。一小段代码,献出一段爱心,你值得拥有。
// 腾讯404页面
<script type="text/javascript" src="http://www.qq.com/404/search_children.js" charset="utf-8" homePageUrl="http://yoursite.com/yourPage.html" homePageName="回到我的主页"></script>
20160529162719.png
Action参数绑定
在说Action
参数绑定前,先说说与之相关的问题。在学习Action参数绑定
之前,我获取GET
参数的方式是怎么样?
自然是利用$_GET
来获取URL
上的参数,如:
# 我要点击一个文章链接,来进行文章编辑 url如下
http://localhost/forum/index.php/Admin/admin/article/act/edit/aid/2
# 然后通过$_GET来获取act aid得到参数,来判断进行的操作以及操作的文章id
if($_GET['act'] == 'edit') {
# 文章编辑 利用 aid从数据库取出文章进行编辑
}
这种方式虽然可行,却不够优雅,作为一个立志成为攻城狮的程序员来说,要想尽方法使你的代码更加简洁,更加优雅。
这时来看看Action参数绑定
,什么是Action参数绑定
?
Action参数绑定是通过直接绑定URL地址中的变量作为操作方法的参数, 可以简化方法的定义甚至路由的解析。
说的还挺绕,其实就是把GET
形式传递的参数直接绑定到你的操作方法上,你就能够直接访问,而不用通过$_GET
。
举个例子,还是上面的链接http://localhost/forum/index.php/Admin/admin/article/act/edit/aid/2
。
定义操作方法如下:
namespace Admin\Controller;
use Think\Controller;
class AdminController extends Controller {
/**
* 文章显示控制器
* @param:$act string 进行的文章操作 默认值空 -> 显示所有文章
* @param:$aid int 文章的主键id 默认值0 -> 显示所有文章
*/
public function article($act='' , $aid = 0) {
if(empty($act) || empty($aid) ) {
# 显示所有文章
} else {
# 进行想要的操作
echo '你要进行的操作是'.$act.',你想要'.$act.'的文章id是'.$aid;
}
}
}
访问上面的链接,结果为
你要进行的操作是edit,你想要edit的文章id是2
这就是Action参数绑定
。要使用这种方式需要开启URL_PARAMS_BIND
设置(默认设置true)。
Action参数绑定
有两种形式
按照变量名绑定
按照变量顺序绑定
将URL_PARAMS_BIND_TYPE
的值设置成0,按照变量名绑定,设置成1,按照变量顺序绑定。
按照字面意思也可以理解,按照变量名绑定即寻找get参数时,按照操作方法中定义的变量名去寻找相应的值。如果没有就报错。这也是最常用的方式。
按照变量顺序绑定,即按照url上get参数的顺序去给操作方法上的变量赋值,这样在url上的参数就能够随意变换位置,同时url上get参数也可以隐藏变量名。
举例说明一下:
按照变量名绑定
链接 http://localhost/forum/index.php/Admin/admin/article/act/edit/aid/2
那么打印出来的结果
你要进行的操作是edit,你想要edit的文章id是2
链接 http://localhost/forum/index.php/Admin/admin/article/aid/2/act/edit
那么打印出来的结果
你要进行的操作是edit,你想要edit的文章id是2
可以看出无论怎么变换位置,得到的结果是一样的。
按照变量顺序绑定
链接 http://localhost/forum/index.php/Admin/admin/article/edit/2
那么打印出来的结果
你要进行的操作是edit,你想要edit的文章id是2
链接 http://localhost/forum/index.php/Admin/admin/article/2/edit
那么打印出来的结果
你要进行的操作是2,你想要edit的文章id是edit
因此如果采用变量顺序绑定时,一定要确保url上的get参数顺序与操作方法上的参数顺序一致。
值得注意的是按照变量名绑定仅对类似于pathinfo
方式的地址有效。如pathinfo模式
与兼容模式
。
伪静态
伪静态是相对于静态页面来说的,主要是为了更好的SEO效果,并不是真正的静态,而是在URL的结尾添加了类似html
,htm
等的后缀。在TP中默认是开启伪静态的。可以通过URL_HTML_SUFFIX
来设置静态的后缀名,如:
'URL_HTML_SUFFIX'=>'shtml'
访问:
http://localhost/forum/index.php/Admin/admin/article/2/edit.shtml
就是有效的,而访问.html
则会报错。也可以设置多个静态后缀,使用**|**
来进行分割。
// 多个伪静态后缀设置 用|分割
'URL_HTML_SUFFIX' => 'html|shtml|htm'
也可以对某些后缀进行禁止访问,利用URL_DENY_SUFFIX
'URL_HTML_SUFFIX' => 'html'
设置后就不能访问任何以html
为后缀的url。可以将URL的模式改成rewrite
模式来配合伪静态,否则一个链接上既有.php
,也有.html
看上去很别扭。
注意:使用伪静态模式必须开启httpd.conf
的mod_rewrite.so
模块。
rewire模式切换
开启rewrite
模式需要配合修改apache
的重写内容。
打开httpd.conf
文件,搜索mod_rewrite.so
,将该模块前面的#
删除。然后新建.htaccess
,放入如下代码:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>
保存到项目入口文件同级目录。重启apache
即可。
apache的部分重写规则
实现伪静态多数是利用apache
的URL重写规则(RewriteRule),我不是很懂RewriteRule
,只是从网上找来了一些资料。
上述代码中,RewriteEngine
表示是否开启重写引擎,RewriteCond
重写应用条件,RewriteRule
表示重写规则。从代码中明显看出,利用了正则表达式的功能。
正则表达式含义
表达式 | 含义 | 表达式 | 含义 |
---|---|---|---|
? | 0-1个字符 | $ | 段落结束字符 |
. | 1个字符 | \ | 转移字符 |
* | 0-x个字符 | ! | 取反 |
+ | 1-x个字符 | () | 内存限定传值 |
^ | 段落开始字符 | [0-9] | 所有数字字符 |
[a-z] | 所有小写字母 | [A-Z] | 所有大写字母 |
{n} | 重复n次 | {n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
在被匹配的字符串中$N
表示与表达式中第一个()
进行匹配,上述代码中RewriteRule
中的$1
就表示与表达式中的第一个()
进行匹配,这样所有index.php/
都会被其他字符或空字符匹配。
RewriteRule
我确实不怎么懂,网上的资料也很乱,就不多写了,英语好的同学去看官方文档吧。
总结
今天关于控制器的学习就写到这里,还有很多想要说的,但没必要都一一赘述。下次会将阅读源码的一些想法写出来。最后想吐槽一下,简书关于PHP
的程序员好像很少,每次找很长时间才能看见一篇关于PHP
的,醉了醉了。
网友评论