Yii用户手册基础篇

作者:Jeen 发布于:2013-6-8 9:30 Saturday 分类:叽叽歪歪

(ps: 往日翻译,弃之可惜,于是在这边做个简略的记录吧 :)  )

Yii 1.1.13 版用户手册翻译

部分省略 (结合个人思维描述,如有雷同,纯属用词不当)

2、 基础知识
2.1 模型-视图-控制器(MVC)
    Yii框架使用的是MVC设计模式,MVC模式在Web开发中应用的相当广泛,其主要目的就是实现业务逻辑、用户界面操作 以及 底层数据操作的分离。这样开发者在编码的过程中,就能更清晰的有针对性的修改代码,从而实现相关的业务需求。MVC中,模型层主要实现底层数据的操作及与控制器的交互; 视图层主要是显示用户界面,展示相关的数据和表单;控制层主要是实现用户界面操作与底层数据操作的中间层业务逻辑。
    在基础的MVC结构上,Yii还实现了一些自定义的控制器前奏动作,实现用户请求到对应控制器的分发。简单的解析一下Yii的MVC逻辑流程: 用户输入url访问对应的web应用,服务端执行入口文件,执行控制器前奏操作,分发用户相关的请求到对应的控制器,控制器完成逻辑处理后将相关数据及挂件传递到视图文件中,视图层解析布局及控制层传递过来的数据后生成用户所请求的页面,最终显示在了用户的浏览器中。
2.2 入口文件脚本
    Yii的入口文件即网站根目录下的index.php文件:
    // remove the following line when in production mode
    defined('YII_DEBUG') or define('YII_DEBUG',true);
    // include Yii bootstrap file
    require_once('path/to/yii/framework/yii.php');
    // create application instance and run
    $configFile='path/to/config/file.php';
    Yii::createWebApplication($configFile)->run();
    脚本首先加载了框架应用文件,然后根据相关配置执行创建相应的Web应用并执行。
2.2.1 调试模式
    Yii应用通过静态变量YII_DEBUG控制应用的使用环境(调试环境或生产环境)。默认设置为false,即应用处于生产环境。如果需要设置为调试环境,在引用框架文件yii.php之前,将YII_DEBUG设置为true就可以了。调试模式下应用的执行效率相对较低,因为在应用执行的过程中生成并保留了相当多的信息日志。当然,调试环境在开发的过程中还是相当有用的,出错的时候,我们可以通过它找到更多的信息,从而提高问题的解决效率。
2.3 Yii应用
    应用对象在相关请求执行前,先完成了必要的信息处理。比如url的参数规范化,应用配置的加载与保存,分发请求到指定的控制器。所以我们可以称这个应用对象为 执行控制器的一个前奏。前奏控制在入口脚本文件中已经有调用,不过我们可以通过使用Yii::app() 随时调用它。
2.3.1 应用配置
    默认情况下,应用对象是CWebApplication的一个实例。我们通常设定一个配置文件来初始化这个对象,以实现相关的需求。配置就是由多对'键——值'构成的数组。键名均为应用示例化对象中的属性,键值即为对象属性的初始值。例如,下面的数组就是应用 名称及默认控制器的配置.
    array(
    'name'=>'Yii Framework',
    'defaultController'=>'site',
    )
    配置通常保存在protected/config/main.php文件中,当然你也可以自己指定。然后在入口脚本文件中进行调用就ok了。
2.3.2 应用基目录
    应用基目录是保存逻辑代码和数据的目录,默认名称即为 protected ,当然这个也可以同过修改 basePath 属性 设置为其他目录。目录下的文件需要考虑其安全性,通常是禁止用户直接访问的。在Apache环境下我们可以在 目录下添加 .htaccess 文件 并设置 deny from all ,起到简单的保护作用。
2.3.3 应用组件
    通过应用组件的配置与调用,我们可以简单的实现一些功能丰富的应用。我们可以设置组件的初始值来配置适合于自己的组件,例如下面的代码是针对CMemCache组件 多服务器的一个配置范例:
    array(
        ......
        'components'=>array(
            ......
            'cache'=>array(
                'class'=>'CMemCache',
                'servers'=>array(
                    array('host'=>'server1', 'port'=>11211, 'weight'=>60),
                    array('host'=>'server2', 'port'=>11211, 'weight'=>40),
                ),
            ),
        ),
    )
    通过上面的配置,我们在组件列表中加入了cache组件,此组件使用CMemCache类,并初始化了CMemCache类中的servers属性值。
    我们可以通过类似Yii::app()->ComponentID的模式对组件进行调用。例如这边的ComponentID为 cache, 那么我们要使用这个组件时,使用代码Yii::app()->cache即可。
    如果我们需要禁用一个组件,那么我们在配置文件中设置其 enabled 属性为 false 即可。
    注:组件通常是按需求加载的,如果有些组件是必要的,我们通常将其添加到preload中。
2.3.4 Yii的核心组件
    Yii框架为常见的应用开发需求提供了一整套的功能组件。比如,request 组件提供了 收集用户信息及请求等功能。Yii框架下,我们可以通过设置组件的初始值实现各个方面的行为控制。
    以下是CWebApplication 所提供的核心组件列表:
    assetManager:CAssetManager  管理资源文件
    authManager:CAuthManager  管理用户权限(RBAC)
    cache: CCache  提供数据缓存功能。注意,这边你需要指定一个特定的类(如CMemCache,CDbCache),否则使用这个组件没有任何实际效果。
    clientScript:CClientScript 管理客户端脚本(js 和 css)
    coreMessages:CPhpMessageSource 提供用于Yii框架的核心信息翻译
    db:CDbConnection 提供数据库连接。注意,使用组件时必须指定connectionString
    errorHandler:CErrorHandler 处理未捕获的PHP错误及异常
    format:CFormatter   格式化数据的值用于显示
    messages:CPhpMessageSource 提供用于 应用的翻译信息
    request:CHttpRequest 提供用户请求的相关信息
    securityManager:CSecurityManager 提供安全相关的功能,如哈希 加密等
    session:CHttpSession  提供session相关的功能
    statePersister:CStatePersister 提供操作全局状态的功能
    urlManager:CUrlManager 提供Url解析和创建功能
    user:CWebUser   当前用户身份相关的信息
    themeManager:CThemeManager   管理主题模板
2.3.5 应用生命周期
    在处理一个用户请求的过程中,应用的生命周期过程演示如下:
    1 使用CApplication::preinit() 对应用进行初始化
    2 挂载 自动加载及错误处理类
    3 注册核心应用组件
    4 加载应用配置
    5 使用CApplication::init()初始化应用
        注册应用行为
        加载静态应用组件
    6 执行一个onBeginRequest动作
    7 执行用户的请求:
        收集请求相关信息
        创建关联控制器
        执行控制器动作
    8 执行一个onEndRequest动作
2.4控制器
    一个控制器是 CController类(子类)的实例,控制器运行的过程中通常是根据请求执行一个动作,完成数据层和视图层的逻辑处理。动作(action)就是控制器中以action开头的方法。
    一般控制器都有一个默认的动作,当用户没有指定执行的动作时,执行默认动作。缺省设置下,默认动作的名称为Index,当然,你可以通过修改CController::defaultAction属性来设置。下面的代码定义了一个 site控制器及index contact两个动作
    class SiteController extends CController
    {
        public function actionIndex()
        {
            // ...
        }
        public function actionContact()
        {
            // ...
        }
    }
2.4.1 路由
    控制器和动作都是通过其ID来确定调用的,例如访问一个path/to/xyz的控制器,我们需要在类文件protected/controllers/path/to/XyzController.php中实现该控制器类。当然名称xyz是自定义的,比如post控制器,则实现protected/controllers/PostController.php中的类。动作的ID是控制器中不包含action前缀的部分。例如,一个控制器包含方法actionEdit,则该动作的ID就是edit。
    用户请求特定 的控制器和动作即路由。一个路由通常由控制器和动作加斜线连接而成。比如 post/edit 这个路由就是请求 PostController 中的 actionEdit 。注意:默认情况下路由是大小写敏感的,我们可以通过修改CUrlManager::caseSensitive 为false 使路由忽略大小写。在敏感状态下,你需要确保请求路由中的控制器和动作大小写是否正确,否则请求可能出现异常。
    应用还可以包含模块,一个模块的路由格式如:moduleID/controllerID/actionID,更多说明可以参见 模块文档
2.4.2 控制器实例
    当CWebApplication处理一个请求时,控制器实例就随之创建。获取控制器标识后,应用程序会通过以下途径搜索对应的控制器。
        如果 CWebApplication::catchAllRequest 有设置,那么控制器就会基于这个属性创建,而用户请求的控制器标识就直接被忽略了。(主要用于应用程序维护时,重定向所有请求到通知页面)
        如果请求的控制器标识存在于CWebApplication::controllerMap中,那么配置规则将用于创建控制器实例
        如果请求的ID格式为“path/to/xyz”,那控制器类名为XyzController,文件路径为:protected/controllers/path/to/XyzController.php。 例如,一个标识为 admin/user的控制器 就是定向到 protected/controllers/admin/UserController.php 文件中的 UserController类.如果文件不存在就会报404异常。
    在模块中,上面的过程是略有不同的。通常,应用程序会检测请求的标识是否在某个模块中,如果是则先创建模块实例,而后控制器实例才会被创建。
2.4.3 动作
    上面提到过,动作就是以action作为前缀的一些方法。一个可定义性更强的方式就是创建一个动作类,在路由请求的时候加载它。这就使得动作的可定制性更自由更强大了。 例如下面的代码创建了一个动作类
    class UpdateAction extends CAction
    {
        public function run()
        {
            // 一些逻辑代码
        }
    }
    然后通过 actions() 方法将此动作挂载到需要的控制器中
    class PostController extends CController
    {
        public function actions()
        {
            return array(
                'edit'=>'application.controllers.post.UpdateAction',
            );
        }
    }
    上面我们使用了指向对应动作类文件的别名
    动作的参数绑定
    Yii从版本1.1.4开始,添加的自动绑定参数的功能。就是一个控制器动作可以定义一些自定义参数,并使用$_GET对其赋值。示范一下,我们在PostController中定义一个动作create 包含两个参数 :
        category: 一个整型变量,表示新日志创建时 所属的分类id;
        language: 一个字符型变量,表示 新日志所使用的语言。
    我们使用如下代码实现参数值的获取
    class PostController extends CController
    {
        public function actionCreate()
        {
            if(isset($ GET['category']))
                $category=(int)$ GET['category'];
            else
                throw new CHttpException(404,'invalid request');
            if(isset($ GET['language']))
                $language=$ GET['language'];
            else
                $language='en';
            // ... 一些功能代码 ...
        }
    }
    如果使用函数传参的形式,我们可以更方便的实现需求
    class PostController extends CController
    {
        public function actionCreate($category, $language='en')
        {
            $category=(int)$category;
            // ... 一些功能代码 ...
        }
    }
    注意,这边$language默认为en,而$category参数没有设置默认值,如果在使用的过程中,没有传递该参数,则会出现400错误。
    Yii自1.1.5版本在执行动作时开始支持数组类型的参数传递
    class PostController extends CController
    {
        public function actionCreate(array $categories)
        {
            // Yii will make sure that $categories is an array
        }
    }
    即我们在参数前添加了array类型标识.这样,当我们传递的categories是一个字符串时,它将被自动被转换为一个包含此字符串的数组。注意:如果没有明确的标识参数类型,那么参数必须是一个纯变量,如果传递数组则会导致异常。
    版本1.1.7起,动作类中的run方法 也实现了参数传递的功能
2.4.4 过滤器
    过滤器是一段实现在控制器动作执行之前或(和)之后的代码。比如,一个入口控制器通常在用户请求执行某个动作之前先对用户的身份进行判断;一个性能控制器可能用来判断动作消耗的执行时间。一个动作可以有多个过滤器。过滤器会按过滤器列表依次执行。一个过滤器可以控制某个动作以及其余的控制器是否执行。
    过滤器可以定义为一个控制器类的方法,当然,这个方法需要以filter为前缀。比如名为filterAccessControl的方法就是一个名为accessControl过滤器的定义。过滤器方法必须有正确的签名:
    public function filterAccessControl($filterChain)
    {
        // call $filterChain->run() to continue filter and action execution
    }
    $filterChain是CFilterChain的实例,代表与此动作相关的过滤器列表。过滤器方法中,我们使用$filterChain->run()实现过滤器与动作的关联执行。
    过滤器也可以是CFilter或其子类的实例。以下代码即一个控制器类的定义
    class PerformanceFilter extends CFilter
    {
        protected function preFilter($filterChain)
        {
            // 一些在动作执行之前的逻辑代码
            return true; // false 如果控制不被执行
        }
        protected function postFilter($filterChain)
        {
            // 一些在动作执行之后的逻辑代码
        }
    }
    如果希望将过滤器用于动作,我们需要重写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过滤器是一个对象。路径别名application.filters.PerformanceFilter说明这个过滤器类文件为protected/filters/PerformanceFilter.php。我们使用数组来配置某个过滤器从而实现一些必要属性的初始化。比如这边PerformanceFilter过滤器的unit属性被初始化为second
    使用+、-标识符,控制某些动作执行或者不执行相应的过滤器。拿上面的代码来说,postOnly过滤器在edit,create动作中有效,而PerformanceFilter过滤器在除edit,create之外的动作中有效。如果没有+-标识,则该过滤器在所有的动作中均有效。
2.5 模型
    模型是CModel的实例或者子类。模型一般用于实现数据保存以及保存数据的一些逻辑规则。一个模型代表一个单一数据对象。它可以是数据表中的一条记录,也可以是用户提交的一个html表单数据。数据对象的每一个字段都代表着模型中的一个属性。各个属性有其对应的标签和验证规则。
    Yii框架提供两种模型:表单模型和有效活动记录模型。他们都继承自同一个基类,CModel。表单模型是CFormModel的实例化。表单模型用于收集用户输入数据(收集——使用——丢弃)。例如,在登录页,我们可以使用一个表单模型收集用户输入的用户名和密码信息。详细请参考Yii关于表单的文档。活动记录(AR)是用于数据库操作面向对象化的一种设计模式。每个AR对象都是是CActiveRecord或其子类的实例,代表着数据库中的某条记录。记录中的字段即为该AR对象的属性。详细可参考ActiveRecord的说明文档
2.6 视图
    视图就是一个php文件,主要包含一些用户界面元素。他可以包含PHP数据报表,但是不建议在视图中直接操作数据模型,且应该保证其相对独立。在逻辑与视图分离的主旨下,大部分的逻辑代码都应该在控制器或者模型层实现,而不应该放到视图中。
    视图有一个用于渲染调用时使用的名称,即该视图的文件名。比如,edit视图指向的是一个edit.php文件。使用CController::render()对某个视图进行渲染。这个方法将在protected/views/ControllerID目录下查找对应名称的视图文件。
    视图文件中,我们可以直接使用$this操作其控制器的对象。使用$this->propertyName引入我们需要的控制器属性。我们也可以使用以下方法向视图中传递参数:
    $this->render('edit', array(
        'var1'=>$value1,
        'var2'=>$value2,
    ));
    上述代码中,render()方法将把第二个数组参数转换为变量,这样我们就可以在视图中使用变量$var1 和 $var2了。
2.6.1 布局
    布局是一个用来控制视图布局的特殊视图。它通常包含一系列用户视图中通用的模块。比如,一个视图应该包含:头部和脚部,并在中间嵌入相关视图:
    ......header here......
    <?php echo $content; ?>
    ......footer here.....
    这边$content就是视图文件渲染后的效果。布局是在调用render()方法时隐式加载的。一般情况下,protected/views/layouts/main.php为默认布局。我们通过修改CWebApplication::layout()或者CController::layout来自定义布局。如果在渲染视图时不希望用到布局文件,就使用 renderPartial()方法。
2.6.2 挂件
    挂件是CWidget或其子类的实例化。它是一个主要用于前台展示的组件。挂件通常被嵌入到视图中,实现部分效果。例如,一个日历挂件可以用于某个复杂的日历相关视图的渲染。挂件的主要功能就是,方便用户视图模块的复用。挂件的调用方法如下所示:
    <?php $this->beginWidget('path.to.WidgetClass'); ?>
        ...body content that may be captured by the widget...
    <?php $this->endWidget(); ?>
    或者
    <?php $this->widget('path.to.WidgetClass'); ?>
    挂件可以通过配置初始化属性值在调用CBaseController::beginWidget或者CBaseController::widget时实现一些行为的自定义。例如,使用CMaskedTextField挂件时,我们通常希望设置一个范例值。为了实现这个,我们可以传递一个初始化的属性数组(数组的键值分别对应挂件的属性名和初始化值),如下:
    <?php
    $this->widget('CMaskedTextField',array(
        'mask'=>'99/99/9999'
    ));
    ?>
    挂件的自定义可以通过继承CWidget,重写其init()和run()方法来实现,如:
    class MyWidget extends CWidget
    {
        public function init()
        {
            // this method is called by CController::beginWidget()
        }
        public function run()
        {
            // this method is called by CController::endWidget()
        }
    }
    与控制器类似,挂件也可以有自己的视图。默认情况下,挂件的视图文件位于挂件声明类同目录下的views目录中。与控制器类似这些挂件视图可以通过使用CWidget::render()调用。不同的是,挂件的视图不会加载任何布局,并且$this代表的是widget实例而非控制器实例。提示:使用CWidgetFactory::widgets可以实现站内自由配置挂件,使挂件设置更灵活,更多说明参见主题文档。
2.6.3 系统视图
    Yii框架系统视图通常用于错误提示以及记录日志信息。比如,在用户请求不存在的控制器或动作时,Yii会抛出异常及错误提示,而这些说明提示就是使用系统视图进行渲染展示的。系统视图也遵循一定的命名规则,像名称为errorXXX的视图通常用于显示代码为XXX的(CHttpException)异常信息,比如CHttpException的异常代码为404,则显示名为error404的视图。Yii框架提供了一些默认的系统视图,位于framework/views目录下。你可以在protected/views/system目录下创建同名文件进行自定义

2.7 组件
    Yii应用是由一些规范的组件对象构成的。组件是CComponent或其子类的实例化,组件的使用主要涉及到属性的访问以及事件的加载与处理。CCompoent基类指定了定义属性和事件的方法。
2.7.1 组件属性   
    组件属性与对象的公共成员变量类似,我们可以读取和赋值。比如:
    $width=$component->textWidth; // get the textWidth property
    $component->enableCaching=true; // set the enableCaching property   
    声明组件属性,我们可以直接在组件类中定义公共成员变量。还有个更灵活的方法,就是使用getter和setter魔术方法,如下:
    public function getTextWidth()
    {
        return $this-> textWidth;
    }
    public function setTextWidth($value)
    {
        $this-> textWidth=$value;
    }
    上述代码定义了一个可写的属性,名为textWidth(不区分大小写)。读取属性值时getTextWidth()方法被调用;设置属性值时setTextWidth()方法被调用。如果setter方法未实现,则该属性为只读,如果对齐进行赋值就会出现异常。使用getter和setter方法可以方便我们在读取或设置相关属性时添加一些逻辑代码(比如验证,载入事件)。注意:直接声明和使用魔术方法声明的属性在大小写敏感上有所区别。
2.7.2 组件事件
    组件事件是一些特殊属性,它的值是方法(事件处理)。将方法附加到事件上,可以使方法在事件触发时自动执行。这样,我们在开发组件的过程中,就可以使用一些不确定行为(而无需预先规划好整个逻辑流程及其具体实现的功能)。
    组件事件的声明由一个on为前缀的方法声明来完成。与魔术方法声明属性类似,事件名称不区分大小写。如下代码声明了一个onClicked事件:
    public function onClicked($event)
    {
        $this->raiseEvent('onClicked', $event);
    }
    这边$event是CEvent或者其代表事件参数的子类。我们可以使用如下格式的代码将某个方法关联到此事件:$component->onClicked=$callback;  这边$callback代表的是一个PHP回调方法。它可以是一个全局方法,也可以是某个类中的方法。如果是类的方法,则$callback是类似于array($object,'methodName')的一个数组。
    一个事件处理的签名必须采用如下格式:
    function methodName($event)
    {
        ......
    }
    这边$event是描述此事件的参数(源于raiseEvent()调用方法),$event参数是CEvent或其子类的实例,它至少包含了事件调用源的信息。PHP5.3后,可以使用匿名函数定义事件处理。例如:
    $component->onClicked=function($event) {
        ......
    }
    现在如果调用onClicked(),那么onClicked事件将会被触发(包含在onClicked()中),而且关联的事件处理函数将会自动调用。一个事件可以关联多个处理函数(句柄)。当事件被触发时,事件中关联的函数会被自动调用。如果某个处理函数希望终止事件的执行,那么在这个处理函数中设置$event->handled 为true就OK了。
2.7.3 组件行为
    组件支持混入模式并且可以关联多个行为。行为是一个对象,它的方法可以被组件以收集的方式实现继承,而不必拘泥于特定的格式规范(即常规的类继承)。一个组件可以关联多个行为,继而实现多重继承。
    行为类必须实现IBehavior接口。一般的行为可以直接继承CBehavior。如果一个行为需要与一个模型进行关联,它可以继承实现了一些特定方法的CModelBehavior或者CActiveRecordBehavior类。
    使用行为时,必须先将行为方法附加到组件中(attach()--IBehavior::attach),然后我们就可以在组件中调用行为方法了:
    // $name uniquely identifies the behavior in the component
    $component->attachBehavior($name,$behavior);
    // test() is a method of $behavior
    $component->test();
    附加的行为可以当作组件的属性来用。比如,我们在组件中关联了一个tree的行为,我们可以用下面的方法来使用这个行为对象:
    $behavior = $component->tree;
    //与下述方法相同
    // $behavior = $component->asa('tree');
    行为可以自由启用或禁用,这样就实现了控制其方法在组件中是否有效。
    $component->disableBehavior($name);
    //the following statement will throw an exception
    $component->test();
    $component->enableBehavior($name);
    //it works now
    $component->test();
    有时候附加到组件中的行为包含同名的方法,这种情况下,会执行先加载行为的方法。与事件一起使用,会让行为的作用得到更有效的发挥。当组件附加了某个行为时,这个行为的方法可以附加关联到组件的事件中。如此一来,行为就可以执行或改变组件的常规执行流程。
    行为的属性也可以在其附加的组件中进行调用,包括公共成员变量以及使用魔术方法定义的变量。例如,某个行为拥有一个名为xyz的属性并且被附加到组件$a中,那么我就可以使用$a->xyz来访问这个行为的属性。
2.8 模块
    模块是一个自足的软件单元,它包含模型,视图,控制器以及组件等。从各个方面来看,模块与应用类似,主要的区别就是模块无法独立部署,它必须包含于应用之种。用户可以采用与访问应用控制器类似的形式访问模块中的控制器。模块在几种场景下比较有用。开发大型应用时,我们可以将其分割为多个模块,进行独立的开发和维护。一些通用的需求,例如用户管理,评论管理,可以使用模块的形式进行开发,方便在以后的项目中复用。
2.8.1 创建模块
    模块通常组织在一个目录下,目录名称即模块的独立标识。模块的目录结构与应用的基础目录结构类似。如下是一个名为forum的模块目录结构:
    forum/
        ForumModule.php 模块类文件
        components/ containing reusable user components
        views/ containing view files for widgets
        controllers/ containing controller class files
            DefaultController.php the default controller class file
        extensions/ containing third-party extensions
        models/ containing model class files
        views/ containing controller view and layout files
            layouts/ containing layout view files
            default/ containing view files for DefaultController
                index.php the index view file
    每个模块都需要有一个继承自CWebModule类的子类。类名通常格式为ucfirst($id).'Module',这边$id即为模块的标识(模块的目录名)。模块类是模块代码及信息的核心位置。我们可以使用CWebModule::params来保存模块参数,使用CWebModule::components来分享应用的组件到模块中。提示:我们可以使用Gii来创建一个模块的骨架。
2.8.2 使用模块
    使用模块,首先要将模块的文件放置到应用的模块目录,然后将模块id包含到应用的modules属性中。比如这边我们需要加载上面的forum模块,我们可以使用如下配置:
    return array(
        ......
        'modules'=>array('forum',...),
        ......
    );
    模块也可以配置初始化属性值。与组件的配置类似,例如forum模块类中有一个postPerPage属性,我们可以使用如下方式配置其初始值:
    return array(
        ......
        'modules'=>array(
            'forum'=>array(
                'postPerPage'=>20,
            ),
        ),
        ......
    );
   

标签: Yii

发表评论:

©2010-2024 Jeen All Rights Reserved.Powered by emlog 京ICP备15058100号-1