注:个人用于自学回顾用,勿喷。
yii-MVC整个请求流程
1.用户发出了访问 URL http://localhost/yii1/yyi1first/index.php?r=user/index&id=2 的请求, Web 服务器通过执行入口脚本 index.php 处理此请求。
2.入口脚本创建了一个 应用 实例并执行。
3.应用从一个叫做 request 的 应用组件 中获得了用户请求的详细信息。
4.应用在一个名叫 urlManager 的应用组件的帮助下,决定请求的 控制器 和 动作 。在这个例子中,控制器是 user,它代表 UserController 类; 动作是 index ,其实际含义由控制器决定。
5.应用创建了一个所请求控制器的实例以进一步处理用户请求。控制器决定了动作 index 指向控制器类中的一个名为 actionIndex 的方法。然后它创建并持行了与动作关联的过滤器(例如访问控制,基准测试)。 如果过滤器允许,动作将被执行。
6.动作从数据库中读取一个 ID 为 1 的 User 模型。
7.动作通过 User 模型渲染一个名为 index 的 视图。
8.视图读取并显示 User 模型的属性。
9.视图执行一些 小物件。
10.视图的渲染结果被插入一个 布局。
11.动作完成视图渲染并将其呈现给用户。
入口文件理解
设置测试模式,引入YII框架,并且根据指定的配置(main.php)创建一个web实例并且执行。
其实就是实例CWebApplication对象
应用(前端控制器)main.php
应用配置
array(
‘name’=>’Yii Framework’,
‘defaultController’=>’site’,
)
可通过web实例访问name
Yii::app()->name;
应用基础目录
1.指包含了所有安全敏感的PHP脚本和数据的根目录。
2.默认状态下,它是一个位于含有入口脚本目录的名为 protected 的子目录。
3.它可以通过设置config/main中的 basePath 属性自定义
4.作用是为了防止被客户直接访问。
5.Apache可以通过.htaccess文件实现该目的
应用组件
要访问一个应用组件,使用 Yii::app()->ComponentID ,其中的 ComponentID 是指组件的ID(例如 Yii::app()->cache)。
注:应用组件如果未被访问默认是未创建的,如果无论是否访问都要创建则需要将其ID列在应用的preload属性中。
如:main中的'preload'=>array('log'). (*这个如何实现需要关注下)
核心应用组件
这个需要查看CWebApplication预定义的组件
先关心几个组件
CLogRouter 未访问就创建
CHttpRequest
CUrlManager
CHttpSession
CWebUser
后期会跟踪原代码
*应用的生命周期(这个需要重点查看)
当处理用户请求时,应用将经历如下声明周期:
1.通过 CApplication::preinit() 预初始化应用;
2.设置类的自动装载器和错误处理;
3.注册核心类组件;
4.加载应用配置;
5.通过 CApplication::init() 初始化应用:
注册应用行为;
载入静态应用组件;(预计跟前端差不多)
6.触发 onBeginRequest 事件;
7.处理用户请求:
解析用户请求;
创建控制器; (访问时候创建*)
运行控制器;
8触发 onEndRequest 事件。
控制器
CController或者子类的对象,动作最简单形式就是action开头的控制器方法
路由:控制器和动作以 ID 识别
如:控制器ID'path/to/xyz'对应的控制器类文件 protected/controllers/path/to/XyzController.php;
动作ID:如果控制器中有actionEdit方法,动作ID就是edit
路由 post/edit 代表 PostController 及其 edit 动作,请求方式http://hostname/index.php?r=post/edit
注:路由默认是大小写敏感,可通过CUrlManager::caseSensitive 为 false 使大小写不敏感。
控制器实例化
在CWebApplication处理到来的请求时创建。
三种确定控制器的类以及类文件位置
1.指定CWebApplication::catchAllRequest,控制器将给予此属性创建,而用户指定的被忽略.这个常用于维护系统时候使用。
2. CWebApplication::controllerMap中找到了 ID, 相应的控制器配置将被用于创建控制器实例;
3.上面所说的路由;
动作
除了上面说的通过动作ID访问,还可以通过继承CAction实现动作类如下
class UpdateAction extends CAction
{
public function run()
{
// place the action logic here
}
}
再在控制类中覆盖实现actions()方法,引入动作类,如下
class PostController extends CController
{
public function actions()
{
return array(
'edit'=>'application.controllers.post.UpdateAction',
);
}
}
动作参数绑定
1.动作类的参数必须和$_GET传入的参数名字一直,如果参数未定义并且没有默认值则会被 CHttpException (错误代码 400) 捕获异常。
注:这个是否存在风险?
2.如果声明了参数类型array,如果传入的还是字符串格式,则会被强制转换成数组。
过滤器
1.过滤器可在控制器之前或者之后执行校验,一个动作可以有多个过滤器。
2.过滤器可以定义控制器类方法,方法名称必须filter开头;
如filterAccessControl 定义了accessControl的过滤器。
3.过滤器可以阻止动作以及后面其他过滤器的执行。
4.过滤器方法格式如下:
1).方法参数必须是 CFilterChain 的实例
public function filterAccessControl($filterChain)
{
//filterChain->run(); 这个方法为了继续执行过滤器和动作 *
}
2).过滤器实现或者继承CFilter的实体
class PerformanceFilter extends CFilter
{
protected function preFilter($filterChain)
{
// 动作被执行之前应用的逻辑
return true; // 如果动作不应被执行,此处返回 false
}
protected function postFilter($filterChain)
{
// 动作执行之后应用的逻辑
}
}
5.实现动作应用过滤器需要覆盖CController::filters() 方法:
class PostController extends CController
{
public function filters()
{
return array(
'postOnly + edit, create',
array(
'application.filters.PerformanceFilter - edit, create',
'unit'=>'second',
),
);
}
}
*上面指定了两个过滤器: postOnly 和 PerformanceFilter。
postOnly 过滤器是基于方法(在CController中定义过了)
PerformanceFilter基于对象,指定的过滤器文件protected/filters/PerformanceFilter,使用数组并且属性unit可以被初始化。
‘+’说明只过滤edit,create方法,‘-’说明除了edit,create其他都过滤,如果不定义就是所有方法都校验。
模型
模式是CModel或其子类的实例
YII实现两种模型:表单模型和 Active Record,都继承相同的CModel;
表单模型是 CFormModel 的实例,单模型用于保持从用户的输入获取的数据。 这些数据经常被获取,使用,然后丢弃。
Active Record是CActiveRecord 或其子类的实例,代表数据表中的一行(这个有点疑问,为什么不是表对象)
视图
视图逻辑和界面最好分开,逻辑放于控制器或者模型中。
视图的名称和视图脚本名称是一样的。CController::render()是渲染时候调用,会在protected/views/ControllerID目录下寻找视图
视图脚本名称(views下面的文件)可以通过
this访问控制器实例(这个是对应的控制器),通过
this->propertyName方法拉取控制器任何属性(包括私有属性么?)。
如:
this−>render(‘edit′,array(‘var1′=>
value1,
‘var2’=>
value2,));这样渲染给edit.php这个视图脚本,在这个脚本中可以直接访问
var1和$var2。
1.布局
通常包含视图通用部分,比如头尾部分,然后可以把内容嵌入其中。
......header here......
<?php echo $content; ?>
......footer here......
$content这个是存储被渲染的视图结果。
使用render()方法默认使用布局。默认的布局文件是protected/views/layouts/main.php。
可以通过CController::layout和CWebApplication::layout方法改变默认布局文件
使用renderPartial()则不带布局的视图。
2.小物件
使用方式
<?php $this->beginWidget('path.to.WidgetClass'); ?>
...可能会由小物件获取的内容主体...
<?php $this->endWidget(); ?>
或者
<?php $this->widget('path.to.WidgetClass'); ?>
后者用于不需要任何 body 内容的组件.
小物件可通过配置来定制它的表现.这是通过调用 CBaseController::beginWidget 或 CBaseController::widget 设置其初始化属性值来完成的。
<?php
$this->widget('CMaskedTextField',array(
'mask'=>'99/99/9999'
));
?>
可以初始化小物件类。
创建一个新的小物件
继承 CWidget 并覆盖其init() 和 run() 方法,
class MyWidget extends CWidget
{
public function init()
{
// 此方法会被 CController::beginWidget() 调用
}
public function run()
{
// 此方法会被 CController::endWidget() 调用
}
}
小物件可以自己拥有视图。视图位置位于包含小物件类文件目录的views子目录下。可以通过CWidget::render()渲染,但是没有布局文件支持。小物件视图中的$this指向小物件实例。
3.系统视图
通常用于张氏YII错误和日志信息
命名规则:errorXXX:
如果CHttpException跑出404错误,那么error404会显示出来。
系统默认在protected/views/system下创建同名视图文件进行自定义
组件 (目前组件在开发中不是很重要,后期再深入学习)
组件是 CComponent 或其子类的实例。可通过CComponent查看
模块(很重要)
模块是单独的软件单位,包含模型,视图,控制器以及其他组件;
不能单独部署,必须存在于应用中。
通用的模块有利于复用。
1.创建模块
模块的目录名称即模块的唯一ID,例如:
forum/ //模块ID
ForumModule.php 模块类文件
components/ 包含可复用的用户组件
views/ 包含小物件的视图文件
controllers/ 包含控制器类文件
DefaultController.php 默认的控制器类文件
extensions/ 包含第三方扩展
models/ 包含模块类文件
views/ 包含控制器视图和布局文件
layouts/ 包含布局文件
default/ 包含 DefaultController 的视图文件
index.php 首页视图文件
模块必须有一个继承自 CWebModule 的模块类;
类的名字通过表达式 ucfirst($id).'Module' 确定,其中的 $id 代表模块的 ID (或者说模块的目录名字),如上面的ForumModule.php;
模块类是存储模块代码间可共享信息的中心位置。例如,我们可以使用 CWebModule::params 存储模块参数,使用 CWebModule::components 分享模块级的 应用组件 .
2.使用模块
要使用模块,首先将模块目录放在 应用基础目录 的 modules 中;
在应用的 modules 属性中声明模块 ID ;
如:
return array(
......
'modules'=>array('forum',...),
......
);
模块也可以设置初始属性值
return array(
......
'modules'=>array(
'forum'=>array(
'postPerPage'=>20,
),
),
......
);
可以通过直接访问模块属性值
$postPerPage=Yii::app()->controller->module->postPerPage;
*模块中的控制器动作可以通过 路由 moduleID/controllerID/actionID 访问
比如上面的DefaultController控制器,可以通过forum/default/create访问动作,
URL对应的如:http://xxx/index.php?r=forum/default/create.
3.嵌套模块
模块可以无限极嵌套;
一个模块包含另一个模块,前者为父模块,后者为子模块;
子模块必须定义在父模块的modules属性中;
访问子模块中的控制器动作
parentModuleID/childModuleID/controllerID/actionID。
*路径别名与名字空间
1.Yii预定义了一下几个别名
1).system: 表示 Yii 框架目录;
2).zii: 表示 Zii 库 目录;
3).application: 表示应用的 基础目录;
4).webroot: 表示 入口脚本 文件所在的目录。
5).ext: 表示包含了所有第三方 扩展 的目录。
如果定义了模块,模块ID定义了根别名,指向相依的模块根目录,如上面定义的模块forum
可以通过YiiBase::getPathOfAlias()把别名翻译为其相对应的路径。
如system.web.CController 会被翻译为 yii/framework/web/CController;
2.引导类(importing Classes)
Yii::import('system.web.CController');
导入的类只有它第一次被引用才会被包含进来;
YII框架定义的类已经提前被导入。
预先导入机制
使用Class Map这个类,YII内置类也是使用这个方法。
如:
Yii::$classMap=array(
'ClassName1' => 'path/to/ClassName1.php',
'ClassName2' => 'path/to/ClassName2.php',
......
);
必须在CWebApplication::run()执行前执,这样在YII应用中无需显式导入或者包含文件就可以使用。
3.导入整个目录
Yii::import('system.web.*');
4.命名空间(Namespace)(这个文档材料有点少)
命名空间是指对一些类名的一个逻辑组合。
路径别名用于指向一个类文件或者目录。
命名空间和路径别名不冲突。
使用命名空间
比如说,类application\components\GoogleMap 在命名空间application\components下的类。
若自动导入使用命名空间的类,命名空间的格式必须和路径别名相似
比如说,类application\components\GoogleMap 所对应的路径必须和别名application.components.GoogleMap一致。