diff --git a/docs/guide-zh-CN/README.md b/docs/guide-zh-CN/README.md index d405671..9d2f4e7 100644 --- a/docs/guide-zh-CN/README.md +++ b/docs/guide-zh-CN/README.md @@ -28,7 +28,7 @@ Yii 2.0 权威指南 应用结构 -------- -* **已定稿** [结构总览](structure-overview.md) +* **已定稿** [结构概述](structure-overview.md) * **已定稿** [入口脚本](structure-entry-scripts.md) * **已定稿** [应用](structure-applications.md) * **已定稿** [应用组件](structure-application-components.md) @@ -44,11 +44,12 @@ Yii 2.0 权威指南 请求处理 -------- -* **待定中** [引导(Bootstrapping)](runtime-bootstrapping.md) -* **待定中** [路由(Routing)](runtime-routing.md) -* **待定中** [请求(Request)](runtime-requests.md) -* **待定中** [响应(Response)](runtime-responses.md) -* **待定中** [Sessions(会话)和 Cookies](runtime-sessions-cookies.md) +* **已定稿** [运行概述](runtime-overview.md) +* **已定稿** [引导(Bootstrapping)](runtime-bootstrapping.md) +* **已定稿** [路由(Routing)](runtime-routing.md) +* **已定稿** [请求(Request)](runtime-requests.md) +* **已定稿** [响应(Response)](runtime-responses.md) +* **已定稿** [Sessions(会话)和 Cookies](runtime-sessions-cookies.md) * **编撰中** [URL 解析和生成](runtime-url-handling.md) * **编撰中** [错误处理](runtime-handling-errors.md) * **编撰中** [日志](runtime-logging.md) @@ -89,7 +90,7 @@ Yii 2.0 权威指南 显示数据 -------- -* **待定中** [格式化输出数据](output-formatting.md) +* **待定中** [格式化输出数据](output-formatter.md) * **待定中** [分页(Pagination)](output-pagination.md) * **待定中** [排序(Sorting)](output-sorting.md) * **编撰中** [数据提供器](output-data-providers.md) diff --git a/docs/guide-zh-CN/intro-upgrade-from-v1.md b/docs/guide-zh-CN/intro-upgrade-from-v1.md index 82d42ab..a2bab42 100644 --- a/docs/guide-zh-CN/intro-upgrade-from-v1.md +++ b/docs/guide-zh-CN/intro-upgrade-from-v1.md @@ -11,7 +11,7 @@ 安装 ------------ -Yii 2.0 完全拥抱 [Composer](https://getcomposer.org/),它是PHP中的一个依赖管理工具。核心框架以及扩展的安装都通过 Composer 来处理。想要了解更多如何安装 Yii 2.0 请参阅本指南的 [安装 Yii](start-installation.md) 章节。如果你想创建新扩展,或者把你已有的 Yii 1.1 的扩展改写成兼容 2.0 的版本,你可以参考 [创建扩展](extend-creating-extensions.md) 章节。 +Yii 2.0 完全拥抱 [Composer](https://getcomposer.org/),它是事实上的 PHP 依赖管理工具。核心框架以及扩展的安装都通过 Composer 来处理。想要了解更多如何安装 Yii 2.0 请参阅本指南的 [安装 Yii](start-installation.md) 章节。如果你想创建新扩展,或者把你已有的 Yii 1.1 的扩展改写成兼容 2.0 的版本,你可以参考 [创建扩展](extend-creating-extensions.md) 章节。 PHP 需求 @@ -33,17 +33,17 @@ Yii 2.0 需要 PHP 5.4 或更高版本,该版本相对于 Yii 1.1 所需求的 命名空间 --------- -Yii 2.0 里最明显的改动就数命名空间的使用了。几乎每一个核心类都引入了命名空间,比如 `yii\web\Request`。1.1 版用于类名前的字母 “C” 已经不再使用。当前的命名规范与目录结构相吻合。例如,`yii\web\Request` 就表明对应的类文件是 Yii 框架文件夹下的 `web/Request.php` 文件。 +Yii 2.0 里最明显的改动就数命名空间的使用了。几乎每一个核心类都引入了命名空间,比如 `yii\web\Request`。1.1 版类名前缀 “C” 已经不再使用。当前的命名方案与目录结构相吻合。例如,`yii\web\Request` 就表明对应的类文件是 Yii 框架文件夹下的 `web/Request.php` 文件。 -(有了 Yii 的类自动加载器,你可以直接使用全部核心类而不需要显式包含具体文件。) +有了 Yii 的类自动加载器,你可以直接使用全部核心类而不需要显式包含具体文件。 组件(Component)与对象(Object) -------------------- -Yii 2.0 把 1.1 里的 `CComponent` 类拆分成了两个类:[[yii\base\Object]] 和 [[yii\base\Component]]。[[yii\base\Object|Object]] 类是一个轻量级的基类,你可以通过 getters 和 setters 来定义 [object 的属性](concept-properties.md)。[[yii\base\Component|Component]] 类继承自 [[yii\base\Object|Object]],同时进一步支持 [事件](concept-events.md) 和 [行为](concept-behaviors.md)。 +Yii 2.0 把 1.1 中的 `CComponent` 类拆分成了两个类:[[yii\base\Object]] 和 [[yii\base\Component]]。[[yii\base\Object|Object]] 类是一个轻量级的基类,你可以通过 getters 和 setters 来定义[对象的属性](concept-properties.md)。[[yii\base\Component|Component]] 类继承自 [[yii\base\Object|Object]],同时进一步支持 [事件](concept-events.md) 和 [行为](concept-behaviors.md)。 -如果你不需要用到事件或行为,应该考虑使用 [[yii\base\Object|Object]] 类作为基类。这通常是表示基本数据结构的类。 +如果你不需要用到事件或行为,应该考虑使用 [[yii\base\Object|Object]] 类作为基类。这种类通常用来表示基本的数据结构。 对象的配置 @@ -65,12 +65,12 @@ class MyClass extends \yii\base\Object { parent::init(); - // ...配置生效后的初始化过程 + // ... 配置生效后的初始化过程 } } ``` -在上面的例子里,构造方法的最后一个参数必须输入一个配置数组,包含一系列用于在方法结尾初始化相关属性的键值对。你可以重写 [[yii\base\Object::init()|init()]] 方法来执行一些需要在配置生效后进行的初始化工作。 +在上面的例子里,构造方法的最后一个参数必须传入一个配置数组,包含一系列用于在方法结尾初始化相关属性的键值对。你可以重写 [[yii\base\Object::init()|init()]] 方法来执行一些需要在配置生效后进行的初始化工作。 你可以通过遵循以下约定俗成的编码习惯,来使用配置数组创建并配置新的对象: @@ -95,11 +95,11 @@ $event = new \yii\base\Event; $component->trigger($eventName, $event); ``` -要给事件附加一个事件句柄(Event Handler 或者叫事件处理器),需要使用 [[yii\base\Component::on()|on()]] 方法: +要给事件附加一个事件事件处理器,需要使用 [[yii\base\Component::on()|on()]] 方法: ```php $component->on($eventName, $handler); -// 要解除相关句柄,使用 off 方法: +// 解除事件处理器,使用 off 方法: // $component->off($eventName, $handler); ``` @@ -174,7 +174,7 @@ public function actionView($id) } ``` -请查看 [控制器(Controller)](structure-controllers.md) 章节了解有关控制器的更多细节。 +请查看[控制器(Controller)](structure-controllers.md)章节了解有关控制器的更多细节。 小部件(Widget) @@ -283,7 +283,7 @@ Yii 2.0 很多常用的静态助手类,包括: 表单 ----- -Yii 2.0 引进了**表单栏(field)**的概念,用来创建一个基于 [[yii\widgets\ActiveForm]]的表单。一个表单栏是一个由标签、输入框、错误消息(可能还有提示文字)组成的容器,被表示为 [[yii\widgets\ActiveField|ActiveField]] 对象。使用表单栏建立表单的过程比以前更整洁利落: +Yii 2.0 引进了**表单栏(field)**的概念,用来创建一个基于 [[yii\widgets\ActiveForm]] 的表单。一个表单栏是一个由标签、输入框、错误消息(可能还有提示文字)组成的容器,被表示为一个 [[yii\widgets\ActiveField|ActiveField]] 对象。使用表单栏建立表单的过程比以前更整洁利落: ```php @@ -327,14 +327,14 @@ Yii 2.0 的[活动记录](db-active-record.md)改动了很多。两个最显而 1.1 中的 `CDbCriteria` 类在 Yii 2 中被 [[yii\db\ActiveQuery]] 所替代。这个类是继承自 [[yii\db\Query]],因此也继承了所有查询生成方法。开始拼装一个查询可以调用 [[yii\db\ActiveRecord::find()]] 方法进行: ```php -// 检索所有 *活动的* 客户和订单,并以 ID 排序: +// 检索所有“活动的”客户和订单,并以 ID 排序: $customers = Customer::find() ->where(['status' => $active]) ->orderBy('id') ->all(); ``` -要声明一个关联关系,只需简单地定义一个 getter 方法来返回一个 [[yii\db\ActiveQuery|ActiveQuery]] 对象。getter 方法定义的属性名(译者注:即 getOrders() 中的 orders)表示关联关系名。如,以下代码声明了一个名为 `orders` 的关系(1.1 中必须在 `relations()` 方法内声明关系): +要声明一个关联关系,只需简单地定义一个 getter 方法来返回一个 [[yii\db\ActiveQuery|ActiveQuery]] 对象。getter 方法定义的属性名代表关联表名称。如,以下代码声明了一个名为 `orders` 的关系(1.1 中必须在 `relations()` 方法内声明关系): ```php class Customer extends \yii\db\ActiveRecord @@ -360,7 +360,7 @@ $orders = $customer->getOrders()->andWhere('status=1')->all(); $customers = Customer::find()->asArray()->all(); ``` -另一个改变是你不能再通过公共数据定属性(Attribute)的默认值了。如果你需要这么做的话,可以在你的记录类的 `init` 方法中设置它们。 +另一个改变是你不能再通过公共变量定义属性(Attribute)的默认值了。如果你需要这么做的话,可以在你的记录类的 `init` 方法中设置它们。 ```php public function init() @@ -370,11 +370,41 @@ public function init() } ``` -曾几何时,在 1.1 中重写一个活动记录类的构造方法(Constructor)会导致一些问题。它们不会在 2.0 中出现了。需要注意的是,如果你需要在构造方法中添加一些参数,恐怕必须重写 [[yii\db\ActiveRecord::instantiate()]] 方法。 +曾几何时,在 1.1 中重写一个活动记录类的构造方法会导致一些问题。它们不会在 2.0 中出现了。需要注意的是,如果你需要在构造方法中添加一些参数,恐怕必须重写 [[yii\db\ActiveRecord::instantiate()]] 方法。 活动记录方面还有很多其他的变化与改进,请参考[活动记录](db-active-record.md)章节以了解更多细节。 +活动记录行为(Active Record Behaviors) +------------------------------------ + +在 2.0 中遗弃了活动记录行为基类 `CActiveRecordBehavior`。如果你想创建活动记录行为,需要直接继承 `yii\base\Behavior`。如果行为类中需要表示一些事件,需要像这样覆写 `events()` 方法: + +```php +namespace app\components; + +use yii\db\ActiveRecord; +use yii\base\Behavior; + +class MyBehavior extends Behavior +{ + // ... + + public function events() + { + return [ + ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate', + ]; + } + + public function beforeValidate($event) + { + // ... + } +} +``` + + 用户及身份验证接口(IdentityInterface) ------------------------------------- @@ -401,4 +431,4 @@ Yii 2.0 的 URL 管理跟 1.1 中很像。一个主要的改进是现在的 URL 同时使用 Yii 1.1 和 2.x ---------------------- -如果你有一些遗留的 Yii 1.1 代码,需要跟 Yii 2.0 一起使用,可以参考 [1.1 和 2.0 共用](extend-using-v1-v2.md)章节。 +如果你有一些遗留的 Yii 1.1 代码,需要跟 Yii 2.0 一起使用,可以参考 [1.1 和 2.0 共用](tutorial-yii-integration.md)章节。 diff --git a/docs/guide-zh-CN/intro-yii.md b/docs/guide-zh-CN/intro-yii.md index 0843e2a..db28804 100644 --- a/docs/guide-zh-CN/intro-yii.md +++ b/docs/guide-zh-CN/intro-yii.md @@ -1,30 +1,32 @@ Yii 是什么 =========== -Yii 是一个高性能,基于组件的 PHP 框架,用于快速开发现代 Web 应用程序。名字 Yii (读作 `易`)在中文里有 “极致简单与不断演变” 两重含义,也可看作 **Yes It Is**! 的缩写。 +Yii 是一个高性能,基于组件的 PHP 框架,用于快速开发现代 Web 应用程序。名字 Yii (读作 `易`)在中文里有“极致简单与不断演变”两重含义,也可看作 **Yes It Is**! 的缩写。 Yii 最适合做什么? --------------------- -Yii 是一个通用的 Web 编程框架,即可以用于开发各种基于 PHP 的 Web 应用。因为基于组件的框架结构和设计精巧的缓存支持,Yii 特别适合开发大型应用,如门户网站、论坛、内容管理系统(CMS)、电子商务项目和 RESTful Web 服务等。 +Yii 是一个通用的 Web 编程框架,即可以用于开发各种基于 PHP 的 Web 应用。因为基于组件的框架结构和设计精巧的缓存支持,它特别适合开发大型应用,如门户网站、社区、内容管理系统(CMS)、电子商务项目和 RESTful Web 服务等。 Yii 和其他框架相比呢? ------------------------------------------- +如果你有其它框架使用经验,那么你会很开心看到 Yii 所做的努力: + - 和其他 PHP 框架类似,Yii 实现了 MVC(Model-View-Controller)设计模式并基于该模式组织代码。 -- Yii 的代码简洁优雅,这是 Yii 的编程哲学。它永远不会为了要迎合某个设计模式而对代码进行过度的设计。 -- Yii 是一个全栈框架,提供了大量久经考验,开箱即用的特性,例如:对关系型和 NoSQL 数据库都提供了查询生成器(QueryBuilders)和 ActiveRecord;RESTful API 的开发支持;多层缓存支持,等等。 +- Yii 的代码简洁优雅,这是它的编程哲学。它永远不会为了严格遵照某种设计模式而对代码进行过度的设计。 +- Yii 是一个全栈框架,提供了大量久经考验,开箱即用的特性:对关系型和 NoSQL 数据库都提供了查询生成器和 ActiveRecord;RESTful API 的开发支持;多层缓存支持,等等。 - Yii 非常易于扩展。你可以自定义或替换几乎任何一处核心代码。你还会受益于它坚实可靠的扩展架构,使用、再开发或再发布扩展。 - 高性能始终是 Yii 的首要目标之一。 -Yii 不是一场独角戏,它由一个[强大的开发者团队](http://www.yiiframework.com/about/)提供支持,也有一个庞大的专家社区,持续不断地对 Yii 的开发作出贡献。Yii 开发者团队始终对 Web 开发最新潮流和其他框架及项目中的最佳实践和特性保持密切关注,那些有意义的最佳实践及特性会被不定期的整合进核心框架中,并提供简单优雅的接口。 +Yii 不是一场独角戏,它由一个[强大的开发者团队](http://www.yiiframework.com/about/)提供支持,也有一个庞大的专家社区,持续不断地对 Yii 的开发作出贡献。Yii 开发者团队始终对 Web 开发趋势和其他框架及项目中的最佳实践和特性保持密切关注,那些有意义的最佳实践及特性会被不定期的整合进核心框架中,并提供简单优雅的接口。 Yii 版本 ------------ -Yii 当前有两个主要版本:1.1 和 2.0。 1.1 版是上代的老版本,现在处于维护状态。2.0 版是一个完全重写的版本,采用了最新的技术和协议,包括依赖包管理器(Composer)、PHP 代码规范(PSR)、命名空间、Traits(特质)等等。 2.0 版代表了最新一代框架,是未来几年中我们的主要开发版本。本指南主要基于 2.0 版编写。 +Yii 当前有两个主要版本:1.1 和 2.0。 1.1 版是上代的老版本,现在处于维护状态。2.0 版是一个完全重写的版本,采用了最新的技术和协议,包括依赖包管理器 Composer、PHP 代码规范 PSR、命名空间、Traits(特质)等等。 2.0 版代表新一代框架,是未来几年中我们的主要开发版本。本指南主要基于 2.0 版编写。 系统要求和先决条件 @@ -32,5 +34,5 @@ Yii 当前有两个主要版本:1.1 和 2.0。 1.1 版是上代的老版本, Yii 2.0 需要 PHP 5.4.0 或以上版本支持。你可以通过运行任何 Yii 发行包中附带的系统要求检查器查看每个具体特性所需的 PHP 配置。 -使用 Yii 需要对面向对象编程(OOP)有基本了解,因为 Yii 是一个纯面向对象的框架。Yii 2.0 还使用了 PHP 的最新特性,例如 [命名空间](http://www.php.net/manual/en/language.namespaces.php) 和 [Trait(特质)](http://www.php.net/manual/en/language.oop5.traits.php)。理解这些概念将有助于你更快地掌握 Yii 2.0。 +使用 Yii 需要对面向对象编程(OOP)有基本了解,因为 Yii 是一个纯面向对象的框架。Yii 2.0 还使用了 PHP 的最新特性,例如[命名空间](http://www.php.net/manual/en/language.namespaces.php)和[Trait(特质)](http://www.php.net/manual/en/language.oop5.traits.php)。理解这些概念将有助于你更快地掌握 Yii 2.0。 diff --git a/docs/guide-zh-CN/start-databases.md b/docs/guide-zh-CN/start-databases.md index 178de25..113c922 100644 --- a/docs/guide-zh-CN/start-databases.md +++ b/docs/guide-zh-CN/start-databases.md @@ -1,7 +1,7 @@ 使用数据库 ====================== -本章节将介绍如何如何创建一个从数据表 `country` 中获取国家数据并显示出来的页面。为了实现这个目标,你将会配置一个数据库连接,创建一个[活动记录](db-active-record.md)类,并且创建一个[操作](structure-controllers.md)及一个[视图](structure-views.md)。 +本章节将介绍如何如何创建一个从数据表 `country` 中读取国家数据并显示出来的页面。为了实现这个目标,你将会配置一个数据库连接,创建一个[活动记录](db-active-record.md)类,并且创建一个[操作](structure-controllers.md)及一个[视图](structure-views.md)。 贯穿整个章节,你将会学到: @@ -16,7 +16,7 @@ 准备数据库 -------------------- -首先创建一个名为 `yii2basic` 的数据库,应用将从这个数据库中获取数据。你可以创建 SQLite,MySQL,PostregSQL,MSSQL 或 Oracle 数据库,Yii 内置多种数据库支持。简单起见后面的内容将以 MySQL 为例做演示。 +首先创建一个名为 `yii2basic` 的数据库,应用将从这个数据库中读取数据。你可以创建 SQLite,MySQL,PostregSQL,MSSQL 或 Oracle 数据库,Yii 内置多种数据库支持。简单起见,后面的内容将以 MySQL 为例做演示。 然后在数据库中创建一个名为 `country` 的表并插入简单的数据。可以执行下面的语句: @@ -39,7 +39,7 @@ INSERT INTO `country` VALUES ('RU','Russia',146934000); INSERT INTO `country` VALUES ('US','United States',278357000); ``` -于是便有了一个名为 `yii2basic` 的数据库,在这个数据库中有一个包含三个字段的数据表 `country`,表中有十行数据。 +此时便有了一个名为 `yii2basic` 的数据库,在这个数据库中有一个包含三个字段的数据表 `country`,表中有十行数据。 配置数据库连接 --------------------------- @@ -62,7 +62,7 @@ return [ `config/db/php` 是一个典型的基于文件的[配置](concept-configurations.md)工具。这个文件配置了数据库连接 [[yii\db\Connection]] 的创建和初始化参数,应用的 SQL 查询正是基于这个数据库。 -上面配置的数据库连接可以在应用中通过 `Yii::$app->db` 访问。 +上面配置的数据库连接可以在应用中通过 `Yii::$app->db` 表达式访问。 > 补充:`config/db.php` 将被包含在应用配置文件 `config/web.php` 中,后者指定了整个[应用](structure-applications.md)如何初始化。请参考[配置](concept-configurations.md)章节了解更多信息。 @@ -70,7 +70,7 @@ return [ 创建活动记录 ------------------------- -创建一个继承自[活动记录](db-active-record.md)类的类 `Country`,把它放在 `models/Country.php`,去表示和获取 `country` 表的数据。 +创建一个继承自[活动记录](db-active-record.md)类的类 `Country`,把它放在 `models/Country.php` 文件,去代表和读取 `country` 表的数据。 ```php 补充:如果类名和数据表名不能直接对应,可以重写 [[yii\db\ActiveRecord::tableName()|tableName()]] 方法去显式指定相关表名。 +> 补充:如果类名和数据表名不能直接对应,可以覆写 [[yii\db\ActiveRecord::tableName()|tableName()]] 方法去显式指定相关表名。 使用 `Country` 类可以很容易地操作 `country` 表数据,就像这段代码: @@ -107,7 +107,7 @@ $country->name = 'U.S.A.'; $country->save(); ``` -> 补充:活动记录是面向对象、功能强大的访问和操作数据库数据的方式。你可以在[活动记录](db-active-record.md)章节了解更多信息。除此之外你还可以使用另一种更原生的称做[数据访问对象](db-dao)的方法操作数据库数据。 +> 补充:活动记录是面向对象、功能强大的访问和操作数据库数据的方式。你可以在[活动记录](db-active-record.md)章节了解更多信息。除此之外你还可以使用另一种更原生的被称做[数据访问对象](db-dao)的方法操作数据库数据。 创建操作 @@ -148,7 +148,7 @@ class CountryController extends Controller } ``` -把上面的代码保存在 `controllers/CountryController.php`。 +把上面的代码保存在 `controllers/CountryController.php` 文件中。 `index` 操作调用了活动记录 `Country::find()` 方法,去生成查询语句并从 `country` 表中取回所有数据。为了限定每个请求所返回的国家数量,查询在 [[yii\data\Pagination]] 对象的帮助下进行分页。 `Pagination` 对象的使命主要有两点: @@ -184,7 +184,7 @@ use yii\widgets\LinkPager; 这个视图包含两部分用以显示国家数据。第一部分遍历国家数据并以无序 HTML 列表渲染出来。第二部分使用 [[yii\widgets\LinkPager]] 去渲染从操作中传来的分页信息。小部件 `LinkPager` 显示一个分页按钮的列表。点击任何一个按钮都会跳转到对应的分页。 -尝试下 +试运行 ------------- 浏览器访问下面的 URL 看看能否工作: diff --git a/docs/guide-zh-CN/start-forms.md b/docs/guide-zh-CN/start-forms.md index f633e51..f87666c 100644 --- a/docs/guide-zh-CN/start-forms.md +++ b/docs/guide-zh-CN/start-forms.md @@ -1,7 +1,7 @@ 使用表单 ================== -本章节将介绍如何创建一个从用户那搜集数据的表单页。该页将显示一个包含 name 输入框和 email 输入框的表单。当搜集完这两部分信息后,页面将会显示用户输入的信息。 +本章将介绍如何创建一个从用户那搜集数据的表单页。该页将显示一个包含 name 输入框和 email 输入框的表单。当搜集完这两部分信息后,页面将会显示用户输入的信息。 为了实现这个目标,除了创建一个[操作](structure-controllers.md)和两个[视图](structure-views)外,还需要创建一个[模型](structure-models.md)。 @@ -39,11 +39,11 @@ class EntryForm extends Model } ``` -该类继承自 [[yii\base\Model]],Yii 提供的一个基类,通常用来表示数据。 +该类继承自 [[yii\base\Model]],Yii 提供的一个基类,通常用来代表表单数据。 -> 补充:[[yii\base\Model]] 被用于普通模型类的父类并与数据表**无关**。[[yii\db\ActiveRecord]] 通常是普通模型类的父类但与数据表有关联(译者注:[[yii\db\ActiveRecord]] 类其实也是继承自 [[yii\base\Model]],增加了数据库处理)。 +> 补充:[[yii\base\Model]] 被用于普通模型类的父类并与数据表**无关**。[[yii\db\ActiveRecord]] 通常是普通模型类的父类但与数据表有关联。 -`EntryForm` 类包含 `name` 和 `email` 两个公共成员,用来储存用户输入的数据。它还包含一个名为 `rules()` 的方法,用来返回数据验证规则的集合。上面声明的验证规则表示: +`EntryForm` 类包含 `name` 和 `email` 两个公共变量,用来储存用户输入的数据。它还包含一个名为 `rules()` 的方法,用来返回数据验证规则的集合。上面声明的验证规则表示: * `name` 和 `email` 值都是必须的 * `mail` 的值必须满足 email 地址验证 @@ -67,7 +67,7 @@ if ($model->validate()) { 创建操作 ------------------ -下面你得在 `site` 控制器中创建一个 `entry` 操作用于新建的模型。操作的创建和使用已经在[说一声你好](start-hello.md)小节中解释了。 +接下来你需要在 `site` 控制器中创建一个 `entry` 操作用于新建的模型。操作的创建和使用已经在[说一声你好](start-hello.md)小节中解释了。 ```php 补充:表达式 `Yii::$app` 代表[应用](structure-applications.md)实例,它是一个全局可访问的单例。同时它也是一个[服务定位器](concept-service-locator.md),能提供 `request`,`response`,`db` 等等特定功能的组件。在上面的代码里就是使用 `request` 组件来访问应用实例收到的 `$_POST` 数据。 -用户提交表单后,操作将会渲染一个名为 `entry-confirm` 的视图去确认用户输入的数据。如果没填表单就提交,或数据包含错误(译者:如 email 格式不对),`entry` 视图将会渲染输出,连同表单一起输出的还有验证错误的详细信息。 +用户提交表单后,操作将会渲染一个名为 `entry-confirm` 的视图去确认用户输入的数据。如果没填表单就提交,或数据包含错误,`entry` 视图将会渲染输出,连同表单一起输出的还有验证错误的详细信息。 > 注意:在这个简单例子里我们只是呈现了有效数据的确认页面。实践中你应该考虑使用 [[yii\web\Controller::refresh()|refresh()]] 或 [[yii\web\Controller::redirect()|redirect()]] 去避免[表单重复提交问题](http://en.wikipedia.org/wiki/Post/Redirect/Get)。 @@ -148,10 +148,10 @@ use yii\widgets\ActiveForm; ``` -视图使用了一个功能强大的[小部件](structure-widgets.md) [[yii\widgets\ActiveForm|ActiveForm]] 去生成 HTML 表单。其中的 `begin()` 和 `end()` 分别用来渲染表单的开始和关闭标签。在这两个方法之间使用了 [[yii\widgets\ActiveForm::field()|field()]] 方法去创建输入框。第一个输入框用于 “name”,第二个输入框用于 “email”。之后使用 [[yii\helpers\Html::submitButton()]] 方法生成提交按钮。 +视图使用了一个功能强大的[小部件](structure-widgets.md) [[yii\widgets\ActiveForm|ActiveForm]] 去生成 HTML 表单。其中的 `begin()` 和 `end()` 分别用来渲染表单的开始和关闭标签。在这两个方法之间使用了 [[yii\widgets\ActiveForm::field()|field()]] 方法去创建表单栏。第一个表单栏用于 “name”,第二个表单栏用于 “email”。之后使用 [[yii\helpers\Html::submitButton()]] 方法生成提交按钮。 -尝试下 +试运行 ------------- 用浏览器访问下面的 URL 看它能否工作: @@ -160,7 +160,7 @@ use yii\widgets\ActiveForm; http://hostname/index.php?r=site/entry ``` -你会看到一个包含两个输入框的表单的页面。每个输入框的前面都有一个标签指明应该输入的数据类型。如果什么都不填就点击提交按钮,或填入格式不正确的 email 地址,将会看到在对应的输入框下显示错误信息。 +你会看到一个包含两个表单栏的页面。每个表单栏的前面都有一个标签指明应该输入的数据类型。如果什么都不填就点击提交按钮,或填入格式不正确的 email 地址,将会看到在对应的表单栏下显示错误信息。 ![验证错误的表单](images/start-form-validation.png) @@ -172,13 +172,13 @@ http://hostname/index.php?r=site/entry ### 效果说明 -你可能会好奇 HTML 表单暗地里是如何工作的呢,看起来它可以为每个输入框显示文字标签,而当你没输入正确的信息时又不需要刷新页面就能给出错误提示,似乎有些神奇。 +你可能会好奇 HTML 表单暗地里是如何工作的,看起来它可以为每个表单栏显示文字标签,而当你没输入正确的信息时又不需要刷新页面就能给出错误提示,似乎有些神奇。 是的,其实数据首先由客户端 JavaScript 脚本验证,然后才会提交给服务器通过 PHP 验证。[[yii\widgets\ActiveForm]] 足够智能到把你在 `EntryForm` 模型中声明的验证规则转化成客户端 JavaScript 脚本去执行验证。如果用户浏览器禁用了 JavaScript, 服务器端仍然会像 `actionEntry()` 方法里这样验证一遍数据。这保证了任何情况下用户提交的数据都是有效的。 -> 警告:客户端验证是提高用户体验的手段。无论它是否正常启用,服务端验证则都是必须的,请不要忽略它。 +> 警告:客户端验证只是提高用户体验的手段。无论它是否正常启用,服务端验证则都是必须的,请不要忽略它。 -输入框的文字标签是 `field()` 方法生成的,内容就是模型中该数据的属性名。例如模型中的 `name` 属性生成的标签就是 `Name`。 +表单栏的文字标签是 `field()` 方法生成的,内容就是模型中该数据的属性名。例如模型中的 `name` 属性生成的标签就是 `Name`。 你可以在视图中自定义标签: @@ -193,7 +193,7 @@ http://hostname/index.php?r=site/entry 总结 ------- -本章节指南中你接触了 MVC 设计模式的每个部分。学到了如何创建一个模型代表用户数据并验证它的有效性。 +本章指南中你接触了 MVC 设计模式的每个部分。学到了如何创建一个模型代表用户数据并验证它的有效性。 你还学到了如何从用户那获取数据并在浏览器上回显给用户。这本来是开发应用的过程中比较耗时的任务,好在 Yii 提供了强大的小部件让它变得如此简单。 diff --git a/docs/guide-zh-CN/start-gii.md b/docs/guide-zh-CN/start-gii.md index 9be3c7d..fb2397e 100644 --- a/docs/guide-zh-CN/start-gii.md +++ b/docs/guide-zh-CN/start-gii.md @@ -1,7 +1,7 @@ 使用 Gii 生成代码 ======================== -本章节将介绍如何使用 [Gii](tool-gii.md) 去自动生成 Web 站点常用功能的代码。使用 Gii 生成代码非常简单,只要按照 Gii 页面上的介绍输入正确的信息即可。 +本章将介绍如何使用 [Gii](tool-gii.md) 去自动生成 Web 站点常用功能的代码。使用 Gii 生成代码非常简单,只要按照 Gii 页面上的介绍输入正确的信息即可。 贯穿本章节,你将会学到: @@ -25,7 +25,7 @@ if (YII_ENV_DEV) { } ``` -这段配置的意思是如果当前是[开发环境](concept-configurations.md#environment-constants),应用会包含 `gii` 模块,模块类是 [[yii\gii\Module]]。 +这段配置表明,如果当前是[开发环境](concept-configurations.md#environment-constants),应用会包含 `gii` 模块,模块类是 [[yii\gii\Module]]。 如果你检查应用的[入口脚本](structure-entry-scripts.md) `web/index.php`,将看到这行代码将 `YII_ENV_DEV` 设为 true: @@ -33,16 +33,18 @@ if (YII_ENV_DEV) { defined('YII_ENV') or define('YII_ENV', 'dev'); ``` -代码设置应用处于开发模式下,按照上面的配置会打开 Gii 模块。你可以直接通过 URL 访问 Gii: +鉴于这行代码的定义,应用处于开发模式下,按照上面的配置会打开 Gii 模块。你可以直接通过 URL 访问 Gii: ``` http://hostname/index.php?r=gii ``` -> 提示:如果不是通过localhost而是通过IP地址访问Gii,出于安全考虑系统默认禁止,按照如下在配置文件的gii部分添加允许IP地址访问。 + +> 补充: 如果你通过本机以外的机器访问 Gii,请求会被出于安全原因拒绝。你可以配置 Gii 为其添加允许访问的 IP 地址: +> ```php 'gii' => [ 'class' => 'yii\gii\Module', - 'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // 根据你自己的需要调整 + 'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'] // 按需调整这里 ], ``` @@ -61,7 +63,7 @@ http://hostname/index.php?r=gii 然后点击 “Preview” 按钮。你会看到 `models/Country.php` 被列在将要生成的文件列表中。可以点击文件名预览内容。 -如果你已经创建过同样的文件,使用 Gii 可以覆写它,点击文件名旁边的 `diff` 能查看现有文件与将要生成的文件的内容区别。 +如果你已经创建过同样的文件,使用 Gii 会覆写它,点击文件名旁边的 `diff` 能查看现有文件与将要生成的文件的内容区别。 ![模型生成器预览](images/start-gii-model-preview.png) @@ -73,7 +75,7 @@ http://hostname/index.php?r=gii 生成 CRUD 代码 -------------------- -CRUD 代表增,查,改,删操作,这是绝大多数 Web 站点常用的数据处理方式。选择 Gii 中的 “CRUD Generator” (点击 Gii 首页的链接)去创建 CRUD 功能。之前的 “country” 例子需要像这样填写表单: +CRUD 代表增,查,改,删操作,这是绝大多数 Web 站点常用的数据处理方式。选择 Gii 中的 “CRUD Generator” (点击 Gii 首页的链接)去创建 CRUD 功能。本例 “country” 中需要这样填写表单: * Model Class: `app\models\Country` * Search Model Class: `app\models\CountrySearch` @@ -83,12 +85,12 @@ CRUD 代表增,查,改,删操作,这是绝大多数 Web 站点常用的 然后点击 “Preview” 按钮。你会看到下述将要生成的文件列表。 -![CRUD 生成器预览](images/start-gii-crud-preview.png) +[[NEED THE IMAGE HERE / 等待官方补充图片]] -如果你之前创建过 `controllers/CountryController.php` 和 `views/country/index.php` 文件(在指南的使用数据库小节),选中 “overwrite” 下的复选框覆写它们(之前的文件没能全部支持 CRUD)。 +如果你之前创建过 `controllers/CountryController.php` 和 `views/country/index.php` 文件(在指南的使用数据库章节),选中 “overwrite” 下的复选框覆写它们(之前的文件没能全部支持 CRUD)。 -尝试下 +试运行 ------------- 用浏览器访问下面的 URL 查看生成代码的运行: @@ -97,7 +99,7 @@ CRUD 代表增,查,改,删操作,这是绝大多数 Web 站点常用的 http://hostname/index.php?r=country/index ``` -可以看到一个栅格显示着从数据表中获取的国家数据。支持在列头对数据进行排序,输入筛选条件进行筛选。 +可以看到一个栅格显示着从数据表中读取的国家数据。支持在列头对数据进行排序,输入筛选条件进行筛选。 可以浏览详情,编辑,或删除栅格中的每个国家。还可以点击栅格上方的 “Create Country” 按钮通过表单创建新国家。 @@ -111,7 +113,7 @@ http://hostname/index.php?r=country/index * 模型:`models/Country.php` 和 `models/CountrySearch.php` * 视图:`views/country/*.php` -> 补充:Gii 被设计成高度可定制和可扩展的代码生成工具。使用它可以大幅提高应用开发速度。请参考 [Gii](tool-gii.md) 小节了解更多内容。 +> 补充:Gii 被设计成高度可定制和可扩展的代码生成工具。使用它可以大幅提高应用开发速度。请参考 [Gii](tool-gii.md) 章节了解更多内容。 总结 diff --git a/docs/guide-zh-CN/start-hello.md b/docs/guide-zh-CN/start-hello.md index 6fc664a..59c2519 100644 --- a/docs/guide-zh-CN/start-hello.md +++ b/docs/guide-zh-CN/start-hello.md @@ -1,7 +1,7 @@ 说声 Hello ============ -本章节描述了如何在你的应用中创建一个新的 “Hello” 页面。为了做到这点,将会创建一个[操作](structure-controllers.md#creating-actions)和一个[视图](structure-views.md): +本章描述了如何在你的应用中创建一个新的 “Hello” 页面。为了实现这一目标,将会创建一个[操作](structure-controllers.md#creating-actions)和一个[视图](structure-views.md): * 应用将会分派页面请求给操作 * 操作将会依次渲染视图呈现 “Hello” 给最终用户 @@ -16,7 +16,7 @@ 创建操作 ------------------ -为了说 “Hello”,需要创建一个 `say` [操作](structure-controllers.md#creating-actions),从请求中接收 `message` 参数并显示给最终用户。如果请求没有提供 `message` 参数,操作将显示默认参数 “Hello”。 +为了 “Hello”,需要创建一个 `say` [操作](structure-controllers.md#creating-actions),从请求中接收 `message` 参数并显示给最终用户。如果请求没有提供 `message` 参数,操作将显示默认参数 “Hello”。 > 补充:[操作](structure-controllers.md#creating-actions)是最终用户可以直接访问并执行的对象。操作被组织在[控制器](structure-controllers.md)中。一个操作的执行结果就是最终用户收到的响应内容。 @@ -33,7 +33,7 @@ class SiteController extends Controller { // ...其它代码... - public function actionSay($message = '你好') + public function actionSay($message = 'Hello') { return $this->render('say', ['message' => $message]); } @@ -68,7 +68,7 @@ use yii\helpers\Html; 当然了,你大概会在 `say` 视图里放入更多内容。内容可以由 HTML 标签,纯文本,甚至 PHP 语句组成。实际上 `say` 视图就是一个由 [[yii\web\Controller::render()|render()]] 执行的 PHP 脚本。视图脚本输出的内容将会作为响应结果返回给应用。应用将依次输出结果给最终用户。 -尝试下 +试运行 ------------- 创建完操作和视图后,你就可以通过下面的 URL 访问新页面了: diff --git a/docs/guide-zh-CN/start-installation.md b/docs/guide-zh-CN/start-installation.md index b007e43..395e22a 100644 --- a/docs/guide-zh-CN/start-installation.md +++ b/docs/guide-zh-CN/start-installation.md @@ -90,9 +90,9 @@ http://localhost/basic/web/index.php >补充:如果你现在只是要试用 Yii 而不是将其部署到生产环境中,本小节可以跳过。 -通过上述方法安装的应用程序在 Windows,Max OS X,Linux 中的 [Apache HTTP 服务器](http://httpd.apache.org/)或 [Nginx HTTP 服务器](http://nginx.org/) 且PHP版本为5.4或更高都可以直接运行。Yii 2.0 也兼容 Facebook 公司的 [HHVM](http://hhvm.com/),由于 HHVM 和标准 PHP 在边界案例上有些地方略有不同,在使用 HHVM 时需稍作处理。 +通过上述方法安装的应用程序在 Windows,Max OS X,Linux 中的 [Apache HTTP 服务器](http://httpd.apache.org/)或 [Nginx HTTP 服务器](http://nginx.org/)且PHP版本为5.4或更高都可以直接运行。Yii 2.0 也兼容 Facebook 公司的 [HHVM](http://hhvm.com/),由于 HHVM 和标准 PHP 在边界案例上有些地方略有不同,在使用 HHVM 时需稍作处理。 -在生产环境的服务器上,你可能会想配置服务器让应用程序可以通过 URL `http://www.example.com/index.php` 访问而不是 `http://www.example.com/basic/web/index.php`。这种配置需要将 Web 服务器的文档根目录指向 `basic/web` 目录。可能你还会想隐藏掉 URL 中的 `index.php`,具体细节在 [URL 解析和生成](runtime-url-handling.md) 一章中有介绍,你将学到如何配置 Apache 或 Nginx 服务器实现这些目标。 +在生产环境的服务器上,你可能会想配置服务器让应用程序可以通过 URL `http://www.example.com/index.php` 访问而不是 `http://www.example.com/basic/web/index.php`。这种配置需要将 Web 服务器的文档根目录指向 `basic/web` 目录。可能你还会想隐藏掉 URL 中的 `index.php`,具体细节在 [URL 解析和生成](runtime-url-handling.md)一章中有介绍,你将学到如何配置 Apache 或 Nginx 服务器实现这些目标。 >补充:将 `basic/web` 设置为文档根目录,可以防止终端用户访问 `basic/web` 相邻目录中的私有应用代码和敏感数据文件。禁止对其他目录的访问是一个不错的安全改进。 @@ -141,7 +141,7 @@ server { error_log /path/to/basic/log/error.log; location / { - # 如果找不到真实存在的文件,把请求重定向给 index.php + # 如果找不到真实存在的文件,把请求分发至 index.php try_files $uri $uri/ /index.php?$args; } diff --git a/docs/guide-zh-CN/start-looking-ahead.md b/docs/guide-zh-CN/start-looking-ahead.md index 6109fb4..03bad82 100644 --- a/docs/guide-zh-CN/start-looking-ahead.md +++ b/docs/guide-zh-CN/start-looking-ahead.md @@ -1,20 +1,16 @@ 更上一层楼 ============= -通篇阅读完整个“入门”板块,你就完成了一个完整 Yii 应用的创建。在此过程中你学到了如何实现一些常用功能,例如通过 HTML 表单从用户那获取数据,从数据库中获取数据并以分页形式显示。你还学到了如何通过 [Gii](tool-gii.md) 去自动生成代码。使用 Gii 生成代码把 Web 开发中多数繁杂的过程转化为仅仅填写几个表单就行。 +通篇阅读完整个“入门”部分,你就完成了一个完整 Yii 应用的创建。在此过程中你学到了如何实现一些常用功能,例如通过 HTML 表单从用户那获取数据,从数据库中获取数据并以分页形式显示。你还学到了如何通过 [Gii](tool-gii.md) 去自动生成代码。使用 Gii 生成代码把 Web 开发中多数繁杂的过程转化为仅仅填写几个表单就行。 -本章节将介绍一些有助于更好使用 Yii 的资源: +本章将介绍一些有助于更好使用 Yii 的资源: * 文档 - - 权威指南: - 顾名思义,指南详细描述了 Yii 的工作原理并提供了如何使用它的常规引导。这是最重要的 Yii 辅助资料,强烈建议在开始写 Yii 代码之前阅读。 - - 类参考手册: - 描述了 Yii 中每个类的用法。在编码过程中这极为有用,能够帮你理清某个特定类,方法,和属性的用法。类参考手册最好在整个框架的语境下去理解。 - - Wiki 文章: - Wiki 文章是 Yii 用户在其自身经验基础上分享出来的。大多数是使用教程或如何使用 Yii 解决特定问题。虽然这些文章质量可能并不如权威指南,但它们往往覆盖了更广泛的话题,并常常提供解决方案,所以它们也很有用。 + - 权威指南:顾名思义,指南详细描述了 Yii 的工作原理并提供了如何使用它的常规引导。这是最重要的 Yii 辅助资料,强烈建议在开始写 Yii 代码之前阅读。 + - 类参考手册:描述了 Yii 中每个类的用法。在编码过程中这极为有用,能够帮你理清某个特定类,方法,和属性的用法。类参考手册最好在整个框架的语境下去理解。 + - Wiki 文章:Wiki 文章是 Yii 用户在其自身经验基础上分享出来的。大多数是使用教程或如何使用 Yii 解决特定问题。虽然这些文章质量可能并不如权威指南,但它们往往覆盖了更广泛的话题,并常常提供解决方案,所以它们也很有用。 - 书籍 -* [扩展](http://www.yiiframework.com/extensions/): - Yii 拥有数以千计用户提供的扩展,这些扩展能非常方便的插入到应用中,使你的应用开发过程更加方便快捷。 +* [扩展](http://www.yiiframework.com/extensions/):Yii 拥有数以千计用户提供的扩展,这些扩展能非常方便的插入到应用中,使你的应用开发过程更加方便快捷。 * 社区 - [官方论坛](http://www.yiiframework.com/forum/) - [GitHub](https://github.com/yiisoft/yii2) diff --git a/docs/guide-zh-CN/structure-application-components.md b/docs/guide-zh-CN/structure-application-components.md new file mode 100644 index 0000000..e5142fa --- /dev/null +++ b/docs/guide-zh-CN/structure-application-components.md @@ -0,0 +1,101 @@ +应用组件 +====================== + +应用主体是[服务定位器](concept-service-locator.md),它部署一组提供各种不同功能的 *应用组件* 来处理请求。 +例如,`urlManager`组件负责处理网页请求路由到对应的控制器。`db`组件提供数据库相关服务等等。 + +在同一个应用中,每个应用组件都有一个独一无二的 ID 用来区分其他应用组件,你可以通过如下表达式访问应用组件。 + +```php +\Yii::$app->componentID +``` + +例如,可以使用 `\Yii::$app->db` 来获取到已注册到应用的 [[yii\db\Connection|DB connection]], +使用 `\Yii::$app->cache` 来获取到已注册到应用的 [[yii\caching\Cache|primary cache]]。 + +第一次使用以上表达式时候会创建应用组件实例,后续再访问会返回此实例,无需再次创建。 + +应用组件可以是任意对象,可以在 [应用主体配置](structure-applications.md#application-configurations)配置 [[yii\base\Application::components]] 属性 . +例如: + +```php +[ + 'components' => [ + // 使用类名注册 "cache" 组件 + 'cache' => 'yii\caching\ApcCache', + + // 使用配置数组注册 "db" 组件 + 'db' => [ + 'class' => 'yii\db\Connection', + 'dsn' => 'mysql:host=localhost;dbname=demo', + 'username' => 'root', + 'password' => '', + ], + + // 使用函数注册"search" 组件 + 'search' => function () { + return new app\components\SolrService; + }, + ], +] +``` + +> 补充:请谨慎注册太多应用组件,应用组件就像全局变量,使用太多可能加大测试和维护的难度。 + 一般情况下可以在需要时再创建本地组件。 + + +## 引导启动组件 + +上面提到一个应用组件只会在第一次访问时实例化,如果处理请求过程没有访问的话就不实例化。 +有时你想在每个请求处理过程都实例化某个组件即便它不会被访问, +可以将该组件ID加入到应用主体的 [[yii\base\Application::bootstrap|bootstrap]] 属性中。 + +例如, 如下的应用主体配置保证了 `log` 组件一直被加载。 + +```php +[ + 'bootstrap' => [ + // 将 log 组件 ID 加入引导让它始终载入 + 'log', + ], + 'components' => [ + 'log' => [ + // "log" 组件的配置 + ], + ], +] +``` + + +## 核心应用组件 + +Yii 定义了一组固定ID和默认配置的 *核心* 组件,例如 [[yii\web\Application::request|request]] 组件 +用来收集用户请求并解析 [路由](runtime-routing.md); +[[yii\base\Application::db|db]] 代表一个可以执行数据库操作的数据库连接。 +通过这些组件,Yii应用主体能处理用户请求。 + +下面是预定义的核心应用组件列表,可以和普通应用组件一样配置和自定义它们。 +当你配置一个核心组件,不指定它的类名的话就会使用Yii默认指定的类。 + +* [[yii\web\AssetManager|assetManager]]: 管理资源包和资源发布,详情请参考 [管理资源](output-assets.md) 一节。 +* [[yii\db\Connection|db]]: 代表一个可以执行数据库操作的数据库连接, + 注意配置该组件时必须指定组件类名和其他相关组件属性,如[[yii\db\Connection::dsn]]。 + 详情请参考 [数据访问对象](db-dao.md) 一节。 +* [[yii\base\Application::errorHandler|errorHandler]]: 处理 PHP 错误和异常, + 详情请参考 [错误处理](tutorial-handling-errors.md) 一节。 +* [[yii\i18n\Formatter|formatter]]: 格式化输出显示给终端用户的数据,例如数字可能要带分隔符, + 日期使用长格式。详情请参考 [格式化输出数据](output-formatting.md) 一节。 +* [[yii\i18n\I18N|i18n]]: 支持信息翻译和格式化。详情请参考 [国际化](tutorial-i18n.md) 一节。 +* [[yii\log\Dispatcher|log]]: 管理日志对象。详情请参考 [日志](tutorial-logging.md) 一节。 +* [[yii\swiftmailer\Mailer|mail]]: 支持生成邮件结构并发送,详情请参考 [邮件](tutorial-mailing.md) 一节。 +* [[yii\base\Application::response|response]]: 代表发送给用户的响应, + 详情请参考 [响应](runtime-responses.md) 一节。 +* [[yii\base\Application::request|request]]: 代表从终端用户处接收到的请求, + 详情请参考 [请求](runtime-requests.md) 一节。 +* [[yii\web\Session|session]]: 代表会话信息,仅在[[yii\web\Application|Web applications]] 网页应用中可用, + 详情请参考 [Sessions (会话) and Cookies](runtime-sessions-cookies.md) 一节。 +* [[yii\web\UrlManager|urlManager]]: 支持URL地址解析和创建, + 详情请参考 [URL 解析和生成](runtime-url-handling.md) 一节。 +* [[yii\web\User|user]]: 代表认证登录用户信息,仅在[[yii\web\Application|Web applications]] 网页应用中可用, + 详情请参考 [认证](security-authentication.md) 一节。 +* [[yii\web\View|view]]: 支持渲染视图,详情请参考 [Views](structure-views.md) 一节。 diff --git a/docs/guide-zh-CN/structure-applications.md b/docs/guide-zh-CN/structure-applications.md new file mode 100644 index 0000000..452467d --- /dev/null +++ b/docs/guide-zh-CN/structure-applications.md @@ -0,0 +1,524 @@ +应用主体 +============ + +应用主体是管理 Yii 应用系统整体结构和生命周期的对象。 +每个Yii应用系统只能包含一个应用主体,应用主体在 [入口脚本](structure-entry-scripts.md) 中创建并能通过表达式 `\Yii::$app` 全局范围内访问。 + +> 补充: 当我们说"一个应用",它可能是一个应用主体对象,也可能是一个应用系统,是根据上下文来决定[译:中文为避免歧义,Application翻译为应用主体]。 + +Yii有两种应用主体: [[yii\web\Application|网页应用主体]] and +[[yii\console\Application|控制台应用主体]], 如名称所示,前者主要处理网页请求,后者处理控制台请求。 + + +## 应用主体配置 + +如下所示,当 [入口脚本](structure-entry-scripts.md) 创建了一个应用主体,它会加载一个 [配置](concept-configurations.md) 文件并传给应用主体。 + +```php +require(__DIR__ . '/../vendor/autoload.php'); +require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); + +// 加载应用主体配置 +$config = require(__DIR__ . '/../config/web.php'); + +// 实例化应用主体、配置应用主体 +(new yii\web\Application($config))->run(); +``` + +类似其他 [配置](concept-configurations.md) 文件, 应用主体配置文件标明如何设置应用对象初始属性。 +由于应用主体配置比较复杂,一般保存在多个类似如上web.php的 [配置文件](concept-configurations.md#configuration-files) 当中。 + +## 应用主体属性 + +应用主体配置文件中有许多重要的属性要配置,这些属性指定应用主体的运行环境。 +比如,应用主体需要知道如何加载 [控制器](structure-controllers.md) ,临时文件保存到哪儿等等。 +以下我们简述这些属性。 + +### 必要属性 + +在一个应用中,至少要配置2个属性: [[yii\base\Application::id|id]] 和 [[yii\base\Application::basePath|basePath]]。 + + +#### [[yii\base\Application::id|id]] + +[[yii\base\Application::id|id]] 属性用来区分其他应用的唯一标识ID。主要给程序使用。 +为了方便协作,最好使用数字作为应用主体ID,但不强制要求为数字。 + + +#### [[yii\base\Application::basePath|basePath]] + + +[[yii\base\Application::basePath|basePath]] 指定该应用的根目录。根目录包含应用系统所有受保护的源代码。 +在根目录下可以看到对应MVC设计模式的`models`, `views`, `controllers`等子目录。 + +可以使用路径或 [路径别名](concept-aliases.md) 来在配置 [[yii\base\Application::basePath|basePath]] 属性。 +两种格式所对应的目录都必须存在,否则系统会抛出一个异常。 系统会使用 `realpath()` 函数规范化配置的路径. + +[[yii\base\Application::basePath|basePath]] 属性经常用于派生一些其他重要路径(如runtime路径),因此,系统预定义 `@app` 代表这个路径。 +派生路径可以通过这个别名组成(如`@app/runtime`代表runtime的路径)。 + + +### 重要属性 + +本小节所描述的属性通常需要设置,因为不用的应用属性不同。 + + +#### [[yii\base\Application::aliases|aliases]] + +该属性允许你用一个数组定义多个 [别名](concept-aliases.md)。数组的key为别名名称,值为对应的路径。例如: + +```php +[ + 'aliases' => [ + '@name1' => 'path/to/path1', + '@name2' => 'path/to/path2', + ], +] +``` + +使用这个属性来定义别名,代替 [[Yii::setAlias()]] 方法来设置。 + + +#### [[yii\base\Application::bootstrap|bootstrap]] + +这个属性很实用,它允许你用数组指定启动阶段[[yii\base\Application::bootstrap()|bootstrapping process]]需要运行的组件。 +比如,如果你希望一个 [模块](structure-modules.md) 自定义 [URL 规则](runtime-url-handling.md),你可以将模块ID加入到bootstrap数组中。 + +属性中的每个组件需要指定以下一项: + +- 应用 [组件](#components) ID. +- [模块](#modules) ID. +- 类名. +- 配置数组. +- 创建并返回一个组件的无名称函数. + +例如: + +```php +[ + 'bootstrap' => [ + // 应用组件ID或模块ID + 'demo', + + // 类名 + 'app\components\Profiler', + + // 配置数组 + [ + 'class' => 'app\components\Profiler', + 'level' => 3, + ], + + // 无名称函数 + function () { + return new app\components\Profiler(); + } + ], +] +``` + +> 补充: 如果模块ID和应用组件ID同名,优先使用应用组件ID,如果你想用模块ID,可以使用如下无名称函数返回模块ID。 +>```php +[ + function () { + return Yii::$app->getModule('user'); + }, +] +``` + + +在启动阶段,每个组件都会实例化。如果组件类实现接口 [[yii\base\BootstrapInterface]],也会调用 [[yii\base\BootstrapInterface::bootstrap()|bootstrap()]] 方法。 + +举一个实际的例子,[Basic Application Template](start-installation.md) 应用主体配置中, +开发环境下会在启动阶段运行 `debug` 和 `gii` 模块。 + +```php +if (YII_ENV_DEV) { + // configuration adjustments for 'dev' environment + $config['bootstrap'][] = 'debug'; + $config['modules']['debug'] = 'yii\debug\Module'; + + $config['bootstrap'][] = 'gii'; + $config['modules']['gii'] = 'yii\gii\Module'; +} +``` + +> 注: 启动太多的组件会降低系统性能,因为每次请求都需要重新运行启动组件,因此谨慎配置启动组件。 + + +#### [[yii\web\Application::catchAll|catchAll]] + +该属性仅 [[yii\web\Application|Web applications]] 网页应用支持。 +它指定一个要处理所有用户请求的 [控制器方法](structure-controllers.md),通常在维护模式下使用,同一个方法处理所有用户请求。 + +该配置为一个数组,第一项指定动作的路由,剩下的数组项(key-value 成对)指定传递给动作的参数,例如: + +```php +[ + 'catchAll' => [ + 'offline/notice', + 'param1' => 'value1', + 'param2' => 'value2', + ], +] +``` + + +#### [[yii\base\Application::components|components]] + +这是最重要的属性,它允许你注册多个在其他地方使用的[应用组件](#structure-application-components.md). 例如 + +```php +[ + 'components' => [ + 'cache' => [ + 'class' => 'yii\caching\FileCache', + ], + 'user' => [ + 'identityClass' => 'app\models\User', + 'enableAutoLogin' => true, + ], + ], +] +``` + +每一个应用组件指定一个key-value对的数组,key代表组件ID,value代表组件类名或 [配置](concept-configurations.md)。 + +在应用中可以任意注册组件,并可以通过表达式 `\Yii::$app->ComponentID` 全局访问。 + +详情请阅读 [应用组件](structure-application-components.md) 一节. + + +#### [[yii\base\Application::controllerMap|controllerMap]] + +该属性允许你指定一个控制器ID到任意控制器类。Yii遵循一个默认的 [规则](#controllerNamespace)指定控制器ID到任意控制器类(如`post`对应`app\controllers\PostController`)。 +通过配置这个属性,可以打破这个默认规则,在下面的例子中,`account`对应到`app\controllers\UserController`, +`article` 对应到 `app\controllers\PostController`。 + +```php +[ + 'controllerMap' => [ + [ + 'account' => 'app\controllers\UserController', + 'article' => [ + 'class' => 'app\controllers\PostController', + 'enableCsrfValidation' => false, + ], + ], + ], +] +``` + +数组的键代表控制器ID,数组的值代表对应的类名。 + + +#### [[yii\base\Application::controllerNamespace|controllerNamespace]] + +该属性指定控制器类默认的命名空间,默认为`app\controllers`。比如控制器ID为 `post` 默认对应 `PostController` (不带命名空间), +类全名为 `app\controllers\PostController`。 + +控制器类文件可能放在这个命名空间对应目录的子目录下, +例如,控制器ID `admin/post` 对应的控制器类全名为 `app\controllers\admin\PostController`。 + +控制器类全面能被 [自动加载](concept-autoloading.md),这点是非常重要的,控制器类的实际命名空间对应这个属性, +否则,访问时你会收到"Page Not Found"[译:页面找不到]。 + +如果你想打破上述的规则,可以配置 [controllerMap](#controllerMap) 属性。 + + +#### [[yii\base\Application::language|language]] + +该属性指定应用展示给终端用户的语言,默认为 `en` 标识英文。如果需要之前其他语言可以配置该属性。 + +该属性影响各种 [国际化](tutorial-i18n.md) ,包括信息翻译、日期格式、数字格式等。 +例如 [[yii\jui\DatePicker]] 小部件会根据该属性展示对应语言的日历以及日期格式。 + +推荐遵循 [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag) 来设置语言,例如 `en` 代表英文, `en-US` 代表英文(美国). + +该属性的更多信息可参考 [国际化](tutorial-i18n.md) 一节. + + +#### [[yii\base\Application::modules|modules]] + +该属性指定应用所包含的 [模块](structure-modules.md)。 + +该属性使用数组包含多个模块类 [配置](concept-configurations.md),数组的键为模块ID,例: + +```php +[ + 'modules' => [ + // "booking" 模块以及对应的类 + 'booking' => 'app\modules\booking\BookingModule', + + // "comment" 模块以及对应的配置数组 + 'comment' => [ + 'class' => 'app\modules\comment\CommentModule', + 'db' => 'db', + ], + ], +] +``` + +更多详情请参考 [模块](structure-modules.md) 一节。 + + +#### [[yii\base\Application::name|name]] + +该属性指定你可能想展示给终端用户的应用名称,不同于需要唯一性的 [[yii\base\Application::id|id]] 属性, +该属性可以不唯一,该属性用于显示应用的用途。 + +如果其他地方的代码没有用到,可以不配置该属性。 + + +#### [[yii\base\Application::params|params]] + +该属性为一个数组,指定可以全局访问的参数,代替程序中硬编码的数字和字符,应用中的参数定义到一个单独的文件并随时可以访问是一个好习惯。 +例如用参数定义缩略图的长宽如下: + +```php +[ + 'params' => [ + 'thumbnail.size' => [128, 128], + ], +] +``` + +然后简单的使用如下代码即可获取到你需要的长宽参数: + +```php +$size = \Yii::$app->params['thumbnail.size']; +$width = \Yii::$app->params['thumbnail.size'][0]; +``` + +以后想修改缩略图长宽,只需要修改该参数而不需要相关的代码。 + + +#### [[yii\base\Application::sourceLanguage|sourceLanguage]] + +该属性指定应用代码的语言,默认为 `'en-US'` 标识英文(美国),如果应用不是英文请修改该属性。 + +和 [语言](#language) 属性类似,配置该属性需遵循 [IETF language tag](http://en.wikipedia.org/wiki/IETF_language_tag). +例如 `en` 代表英文, `en-US` 代表英文(美国)。 + +该属性的更多信息可参考 [国际化](tutorial-i18n.md) 一节. + + +#### [[yii\base\Application::timeZone|timeZone]] + +该属性提供一种方式修改PHP运行环境中的默认时区,配置该属性本质上就是调用PHP函数 +[date_default_timezone_set()](http://php.net/manual/en/function.date-default-timezone-set.php),例如: + +```php +[ + 'timeZone' => 'America/Los_Angeles', +] +``` + + +#### [[yii\base\Application::version|version]] + +该属性指定应用的版本,默认为`'1.0'`,其他代码不使用的话可以不配置。 + + +### 实用属性 + +本小节描述的属性不经常设置,通常使用系统默认值。如果你想改变默认值,可以配置这些属性。 + + +#### [[yii\base\Application::charset|charset]] + +该属性指定应用使用的字符集,默认值为 `'UTF-8'`,绝大部分应用都在使用,除非已有的系统大量使用非unicode数据才需要更改该属性。 + + +#### [[yii\base\Application::defaultRoute|defaultRoute]] + +该属性指定未配置的请求的响应 [路由](runtime-routing.md) 规则,路由规则可能包含模块ID,控制器ID,动作ID。 +例如`help`, `post/create`, `admin/post/create`,如果动作ID没有指定,会使用[[yii\base\Controller::defaultAction]]中指定的默认值。 + +对于 [[yii\web\Application|Web applications]] 网页应用,默认值为 `'site'` 对应 `SiteController` 控制器,并使用默认的动作。 +因此你不带路由的访问应用,默认会显示 `app\controllers\SiteController::actionIndex()` 的结果。 + +对于 [[yii\console\Application|console applications]] 控制台应用, +默认值为 `'help'` 对应 [[yii\console\controllers\HelpController::actionIndex()]]。 +因此,如果执行的命令不带参数,默认会显示帮助信息。 + + +#### [[yii\base\Application::extensions|extensions]] + +该属性用数组列表指定应用安装和使用的 [扩展](structure-extensions.md),默认使用`@vendor/yiisoft/extensions.php`文件返回的数组。 +当你使用 [Composer](http://getcomposer.org) 安装扩展,`extensions.php` 会被自动生成和维护更新。 +所以大多数情况下,不需要配置该属性。 + +特殊情况下你想自己手动维护扩展,可以参照如下配置该属性: + +```php +[ + 'extensions' => [ + [ + 'name' => 'extension name', + 'version' => 'version number', + 'bootstrap' => 'BootstrapClassName', // 可选配,可为配置数组 + 'alias' => [ // 可选配 + '@alias1' => 'to/path1', + '@alias2' => 'to/path2', + ], + ], + + // ... 更多像上面的扩展 ... + + ], +] +``` + +如上所示,该属性包含一个扩展定义数组,每个扩展为一个包含 `name` 和 `version` 项的数组。 +如果扩展要在 [引导启动](runtime-bootstrapping.md) 阶段运行,需要配置 `bootstrap`以及对应的引导启动类名或 [configuration](concept-configurations.md) 数组。 +扩展也可以定义 [别名](concept-aliases.md) + + +#### [[yii\base\Application::layout|layout]] + +该属性指定渲染 [视图](structure-views.md) 默认使用的布局名字,默认值为 `'main'` 对应[布局路径](#layoutPath)下的 `main.php` 文件, +如果 [布局路径](#layoutPath) 和 [视图路径](#viewPath) 都是默认值,默认布局文件可以使用路径别名`@app/views/layouts/main.php` + +如果不想设置默认布局文件,可以设置该属性为 `false`,这种做法比较罕见。 + + +#### [[yii\base\Application::layoutPath|layoutPath]] + +该属性指定查找布局文件的路径,默认值为 [视图路径](#viewPath) 下的 `layouts` 子目录。 +如果 [视图路径](#viewPath) 使用默认值,默认的布局路径别名为`@app/views/layouts`。 + +该属性需要配置成一个目录或 路径 [别名](concept-aliases.md)。 +You may configure it as a directory or a path [alias](concept-aliases.md). + + +#### [[yii\base\Application::runtimePath|runtimePath]] + +该属性指定临时文件如日志文件、缓存文件等保存路径,默认值为带别名的 `@app/runtime`。 + +可以配置该属性为一个目录或者路径 [别名](concept-aliases.md),注意应用运行时有对该路径的写入权限, +以及终端用户不能访问改路径因为临时文件可能包含一些敏感信息。 + +为了简化访问该路径,Yii预定义别名 `@runtime` 代表该路径。 + + +#### [[yii\base\Application::viewPath|viewPath]] + +该路径指定视图文件的根目录,默认值为带别名的 `@app/views`,可以配置它为一个目录或者路径 [别名](concept-aliases.md). + + +#### [[yii\base\Application::vendorPath|vendorPath]] + +该属性指定 [Composer](http://getcomposer.org) 管理的供应商路径,该路径包含应用使用的包括Yii框架在内的所有第三方库。 +默认值为带别名的 `@app/vendor` 。 + +可以配置它为一个目录或者路径 [别名](concept-aliases.md),当你修改时,务必修改对应的 Composer 配置。 + +为了简化访问该路径,Yii预定义别名 `@vendor` 代表该路径。 + + +#### [[yii\console\Application::enableCoreCommands|enableCoreCommands]] + +该属性仅 [[yii\console\Application|console applications]] 控制台应用支持, 用来指定是否启用Yii中的核心命令,默认值为 `true`。 + + +## 应用事件 + +应用在处理请求过程中会触发事件,可以在配置文件配置事件处理代码,如下所示: + +```php +[ + 'on beforeRequest' => function ($event) { + // ... + }, +] +``` + +`on eventName` 语法的用法在 [Configurations](concept-configurations.md#configuration-format) 一节有详细描述. + +另外,在应用主体实例化后,你可以在[引导启动](runtime-bootstrapping.md) 阶段附加事件处理代码,例如: + +```php +\Yii::$app->on(\yii\base\Application::EVENT_BEFORE_REQUEST, function ($event) { + // ... +}); +``` + +### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] + +该事件在应用处理请求*before*之前,实际的事件名为 `beforeRequest`。 + +在事件触发前,应用主体已经实例化并配置好了,所以通过事件机制将你的代码嵌入到请求处理过程中非常不错。 +例如在事件处理中根据某些参数动态设置[[yii\base\Application::language]]语言属性。 + + +### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_AFTER_REQUEST]] + +该事件在应用处理请求*after*之后但在返回响应*before*之前触发,实际的事件名为`afterRequest`。 + +该事件触发时,请求已经被处理完,可以做一些请求后处理或自定义响应。 + +注意 [[yii\web\Response|response]] 组件在发送响应给终端用户时也会触发一些事件,这些事件都在本事件*after*之后触发。 + + +### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_ACTION]] + +该事件在每个 [控制器动作](structure-controllers.md) 运行*before*之前会被触发,实际的事件名为 `beforeAction`. + +事件的参数为一个 [[yii\base\ActionEvent]] 实例, +事件处理中可以设置[[yii\base\ActionEvent::isValid]] 为 `false` 停止运行后续动作,例如: + +```php +[ + 'on beforeAction' => function ($event) { + if (some condition) { + $event->isValid = false; + } else { + } + }, +] +``` + +注意 [模块](structure-modules.md) 和 [控制器](structure-controllers.md) 都会触发 `beforeAction` 事件。 +应用主体对象首先触发该事件,然后模块触发(如果存在模块),最后控制器触发。 +任何一个事件处理中设置 [[yii\base\ActionEvent::isValid]] 设置为 `false` 会停止触发后面的事件。 + + +### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_AFTER_ACTION]] + +该事件在每个 [控制器动作](structure-controllers.md) 运行*after*之后会被触发,实际的事件名为 `afterAction`. + +该事件的参数为 [[yii\base\ActionEvent]] 实例,通过[[yii\base\ActionEvent::result]]属性, +事件处理可以访问和修改动作的结果。例如: + +```php +[ + 'on afterAction' => function ($event) { + if (some condition) { + // 修改 $event->result + } else { + } + }, +] +``` + +注意 [模块](structure-modules.md) 和 [控制器](structure-controllers.md) 都会触发 `afterAction` 事件。 +这些对象的触发顺序和 `beforeAction` 相反,也就是说,控制器最先触发,然后是模块(如果有模块),最后为应用主体。 + + +## 应用主体生命周期 + +当运行 [入口脚本](structure-entry-scripts.md) 处理请求时,应用主体会经历以下生命周期: + +1. 入口脚本加载应用主体配置数组。 +2. 入口脚本创建一个应用主体实例: + * 调用 [[yii\base\Application::preInit()|preInit()]] 配置几个高级别应用主体属性,比如[[yii\base\Application::basePath|basePath]]。 + * 注册 [[yii\base\Application::errorHandler|error handler]] 错误处理方法. + * 配置应用主体属性. + * 调用 [[yii\base\Application::init()|init()]] 初始化,该函数会调用 [[yii\base\Application::bootstrap()|bootstrap()]] 运行引导启动组件. +3. 入口脚本调用 [[yii\base\Application::run()]] 运行应用主体: + * 触发 [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_REQUEST]] 事件。 + * 处理请求:解析请求 [路由](runtime-routing.md) 和相关参数;创建路由指定的模块、控制器和动作对应的类,并运行动作。 + * 触发 [[yii\base\Application::EVENT_AFTER_REQUEST|EVENT_AFTER_REQUEST]] 事件。 + * 发送响应到终端用户. +4. 入口脚本接收应用主体传来的退出状态并完成请求的处理。 diff --git a/docs/guide-zh-CN/structure-controllers.md b/docs/guide-zh-CN/structure-controllers.md new file mode 100644 index 0000000..79737be --- /dev/null +++ b/docs/guide-zh-CN/structure-controllers.md @@ -0,0 +1,449 @@ +Controllers +=========== + +Controllers are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture. +They are objects of classes extending from [[yii\base\Controller]] and are responsible for processing requests and +generating responses. In particular, after taking over the control from [applications](structure-applications.md), +controllers will analyze incoming request data, pass them to [models](structure-models.md), inject model results +into [views](structure-views.md), and finally generate outgoing responses. + + +## Actions + +Controllers are composed by *actions* which are the most basic units that end users can address and request for +execution. A controller can have one or multiple actions. + +The following example shows a `post` controller with two actions: `view` and `create`: + +```php +namespace app\controllers; + +use Yii; +use app\models\Post; +use yii\web\Controller; +use yii\web\NotFoundHttpException; + +class PostController extends Controller +{ + public function actionView($id) + { + $model = Post::findOne($id); + if ($model === null) { + throw new NotFoundHttpException; + } + + return $this->render('view', [ + 'model' => $model, + ]); + } + + public function actionCreate() + { + $model = new Post; + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); + } else { + return $this->render('create', [ + 'model' => $model, + ]); + } + } +} +``` + +In the `view` action (defined by the `actionView()` method), the code first loads the [model](structure-models.md) +according to the requested model ID; If the model is loaded successfully, it will display it using +a [view](structure-views.md) named `view`. Otherwise, it will throw an exception. + +In the `create` action (defined by the `actionCreate()` method), the code is similar. It first tries to populate +the [model](structure-models.md) using the request data and save the model. If both succeed it will redirect +the browser to the `view` action with the ID of the newly created model. Otherwise it will display +the `create` view through which users can provide the needed input. + + +## Routes + +End users address actions through the so-called *routes*. A route is a string that consists of the following parts: + +* a module ID: this exists only if the controller belongs to a non-application [module](structure-modules.md); +* a controller ID: a string that uniquely identifies the controller among all controllers within the same application + (or the same module if the controller belongs to a module); +* an action ID: a string that uniquely identifies the action among all actions within the same controller. + +Routes take the following format: + +``` +ControllerID/ActionID +``` + +or the following format if the controller belongs to a module: + +```php +ModuleID/ControllerID/ActionID +``` + +So if a user requests with the URL `http://hostname/index.php?r=site/index`, the `index` action in the `site` controller +will be executed. For more details how routes are resolved into actions, please refer to +the [Routing](runtime-routing.md) section. + + +## Creating Controllers + +In [[yii\web\Application|Web applications]], controllers should extend from [[yii\web\Controller]] or its +child classes. Similarly in [[yii\console\Application|console applications]], controllers should extend from +[[yii\console\Controller]] or its child classes. The following code defines a `site` controller: + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ +} +``` + + +### Controller IDs + +Usually, a controller is designed to handle the requests regarding a particular type of resource. +For this reason, controller IDs are often nouns referring to the types of the resources that they are handling. +For example, you may use `article` as the ID of a controller that handles article data. + +By default, controller IDs should contain these characters only: English letters in lower case, digits, +underscores, dashes and forward slashes. For example, `article` and `post-comment` are both valid controller IDs, +while `article?`, `PostComment`, `admin\post` are not. + +A controller ID may also contain a subdirectory prefix. For example, `admin/article` stands for an `article` controller +in the `admin` subdirectory under the [[yii\base\Application::controllerNamespace|controller namespace]]. +Valid characters for subdirectory prefixes include: English letters in lower and upper cases, digits, underscores and +forward slashes, where forward slashes are used as separators for multi-level subdirectories (e.g. `panels/admin`). + + +### Controller Class Naming + +Controller class names can be derived from controller IDs according to the following rules: + +* Turn the first letter in each word separated by dashes into upper case. Note that if the controller ID + contains slashes, this rule only applies to the part after the last slash in the ID. +* Remove dashes and replace any forward slashes with backward slashes. +* Append the suffix `Controller`. +* And prepend the [[yii\base\Application::controllerNamespace|controller namespace]]. + +The followings are some examples, assuming the [[yii\base\Application::controllerNamespace|controller namespace]] +takes the default value `app\controllers`: + +* `article` derives `app\controllers\ArticleController`; +* `post-comment` derives `app\controllers\PostCommentController`; +* `admin/post-comment` derives `app\controllers\admin\PostCommentController`; +* `adminPanels/post-comment` derives `app\controllers\adminPanels\PostCommentController`. + +Controller classes must be [autoloadable](concept-autoloading.md). For this reason, in the above examples, +the `article` controller class should be saved in the file whose [alias](concept-aliases.md) +is `@app/controllers/ArticleController.php`; while the `admin/post2-comment` controller should be +in `@app/controllers/admin/Post2CommentController.php`. + +> Info: The last example `admin/post2-comment` shows how you can put a controller under a sub-directory + of the [[yii\base\Application::controllerNamespace|controller namespace]]. This is useful when you want + to organize your controllers into several categories and you do not want to use [modules](structure-modules.md). + + +### Controller Map + +You can configure [[yii\base\Application::controllerMap|controller map]] to overcome the constraints +of the controller IDs and class names described above. This is mainly useful when you are using some +third-party controllers which you do not control over their class names. + +You may configure [[yii\base\Application::controllerMap|controller map]] in the +[application configuration](structure-applications.md#application-configurations) like the following: + +```php +[ + 'controllerMap' => [ + // declares "account" controller using a class name + 'account' => 'app\controllers\UserController', + + // declares "article" controller using a configuration array + 'article' => [ + 'class' => 'app\controllers\PostController', + 'enableCsrfValidation' => false, + ], + ], +] +``` + + +### Default Controller + +Each application has a default controller specified via the [[yii\base\Application::defaultRoute]] property. +When a request does not specify a [route](#ids-routes), the route specified by this property will be used. +For [[yii\web\Application|Web applications]], its value is `'site'`, while for [[yii\console\Application|console applications]], +it is `help`. Therefore, if a URL is `http://hostname/index.php`, it means the `site` controller will handle the request. + +You may change the default controller with the following [application configuration](structure-applications.md#application-configurations): + +```php +[ + 'defaultRoute' => 'main', +] +``` + + +## Creating Actions + +Creating actions can be as simple as defining the so-called *action methods* in a controller class. An action method is +a *public* method whose name starts with the word `action`. The return value of an action method represents +the response data to be sent to end users. The following code defines two actions `index` and `hello-world`: + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ + public function actionIndex() + { + return $this->render('index'); + } + + public function actionHelloWorld() + { + return 'Hello World'; + } +} +``` + + +### Action IDs + +An action is often designed to perform a particular manipulation about a resource. For this reason, +action IDs are usually verbs, such as `view`, `update`, etc. + +By default, action IDs should contain these characters only: English letters in lower case, digits, +underscores and dashes. The dashes in an actionID are used to separate words. For example, +`view`, `update2`, `comment-post` are all valid action IDs, while `view?`, `Update` are not. + +You can create actions in two ways: inline actions and standalone actions. An inline action is +defined as a method in the controller class, while a standalone action is a class extending +[[yii\base\Action]] or its child class. Inline actions take less effort to create and are often preferred +if you have no intention to reuse these actions. Standalone actions, on the other hand, are mainly +created to be used in different controllers or be redistributed as [extensions](structure-extensions.md). + + +### Inline Actions + +Inline actions refer to the actions that are defined in terms of action methods as we just described. + +The names of the action methods are derived from action IDs according to the following criteria: + +* Turn the first letter in each word of the action ID into upper case; +* Remove dashes; +* Prepend the prefix `action`. + +For example, `index` becomes `actionIndex`, and `hello-world` becomes `actionHelloWorld`. + +> Note: The names of the action methods are *case-sensitive*. If you have a method named `ActionIndex`, + it will not be considered as an action method, and as a result, the request for the `index` action + will result in an exception. Also note that action methods must be public. A private or protected + method does NOT define an inline action. + + +Inline actions are the most commonly defined actions because they take little effort to create. However, +if you plan to reuse the same action in different places, or if you want to redistribute an action, +you should consider defining it as a *standalone action*. + + +### Standalone Actions + +Standalone actions are defined in terms of action classes extending [[yii\base\Action]] or its child classes. +For example, in the Yii releases, there are [[yii\web\ViewAction]] and [[yii\web\ErrorAction]], both of which +are standalone actions. + +To use a standalone action, you should declare it in the *action map* by overriding the +[[yii\base\Controller::actions()]] method in your controller classes like the following: + +```php +public function actions() +{ + return [ + // declares "error" action using a class name + 'error' => 'yii\web\ErrorAction', + + // declares "view" action using a configuration array + 'view' => [ + 'class' => 'yii\web\ViewAction', + 'viewPrefix' => '', + ], + ]; +} +``` + +As you can see, the `actions()` method should return an array whose keys are action IDs and values the corresponding +action class names or [configurations](concept-configurations.md). Unlike inline actions, action IDs for standalone +actions can contain arbitrary characters, as long as they are declared in the `actions()` method. + + +To create a standalone action class, you should extend [[yii\base\Action]] or its child class, and implement +a public method named `run()`. The role of the `run()` method is similar to that of an action method. For example, + +```php + + +The return value of an action method or the `run()` method of a standalone action is significant. It stands +for the result of the corresponding action. + +The return value can be a [response](runtime-responses.md) object which will be sent to as the response +to end users. + +* For [[yii\web\Application|Web applications]], the return value can also be some arbitrary data which will + be assigned to [[yii\web\Response::data]] and be further converted into a string representing the response body. +* For [[yii\console\Application|console applications]], the return value can also be an integer representing + the [[yii\console\Response::exitStatus|exit status]] of the command execution. + +In the examples shown above, the action results are all strings which will be treated as the response body +to be sent to end users. The following example shows how an action can redirect the user browser to a new URL +by returning a response object (because the [[yii\web\Controller::redirect()|redirect()]] method returns +a response object): + +```php +public function actionForward() +{ + // redirect the user browser to http://example.com + return $this->redirect('http://example.com'); +} +``` + + +### Action Parameters + +The action methods for inline actions and the `run()` methods for standalone actions can take parameters, +called *action parameters*. Their values are obtained from requests. For [[yii\web\Application|Web applications]], +the value of each action parameter is retrieved from `$_GET` using the parameter name as the key; +for [[yii\console\Application|console applications]], they correspond to the command line arguments. + +In the following example, the `view` action (an inline action) has declared two parameters: `$id` and `$version`. + +```php +namespace app\controllers; + +use yii\web\Controller; + +class PostController extends Controller +{ + public function actionView($id, $version = null) + { + // ... + } +} +``` + +The action parameters will be populated as follows for different requests: + +* `http://hostname/index.php?r=post/view&id=123`: the `$id` parameter will be filled with the value + `'123'`, while `$version` is still null because there is no `version` query parameter. +* `http://hostname/index.php?r=post/view&id=123&version=2`: the `$id` and `$version` parameters will + be filled with `'123'` and `'2'`, respectively. +* `http://hostname/index.php?r=post/view`: a [[yii\web\BadRequestHttpException]] exception will be thrown + because the required `$id` parameter is not provided in the request. +* `http://hostname/index.php?r=post/view&id[]=123`: a [[yii\web\BadRequestHttpException]] exception will be thrown + because `$id` parameter is receiving an unexpected array value `['123']`. + +If you want an action parameter to accept array values, you should type-hint it with `array`, like the following: + +```php +public function actionView(array $id, $version = null) +{ + // ... +} +``` + +Now if the request is `http://hostname/index.php?r=post/view&id[]=123`, the `$id` parameter will take the value +of `['123']`. If the request is `http://hostname/index.php?r=post/view&id=123`, the `$id` parameter will still +receive the same array value because the scalar value `'123'` will be automatically turned into an array. + +The above examples mainly show how action parameters work for Web applications. For console applications, +please refer to the [Console Commands](tutorial-console.md) section for more details. + + +### Default Action + +Each controller has a default action specified via the [[yii\base\Controller::defaultAction]] property. +When a [route](#ids-routes) contains the controller ID only, it implies that the default action of +the specified controller is requested. + +By default, the default action is set as `index`. If you want to change the default value, simply override +this property in the controller class, like the following: + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ + public $defaultAction = 'home'; + + public function actionHome() + { + return $this->render('home'); + } +} +``` + + +## Controller Lifecycle + +When processing a request, an [application](structure-applications.md) will create a controller +based on the requested [route](#routes). The controller will then undergo the following lifecycle +to fulfill the request: + +1. The [[yii\base\Controller::init()]] method is called after the controller is created and configured. +2. The controller creates an action object based on the requested action ID: + * If the action ID is not specified, the [[yii\base\Controller::defaultAction|default action ID]] will be used. + * If the action ID is found in the [[yii\base\Controller::actions()|action map]], a standalone action + will be created; + * If the action ID is found to match an action method, an inline action will be created; + * Otherwise an [[yii\base\InvalidRouteException]] exception will be thrown. +3. The controller sequentially calls the `beforeAction()` method of the application, the module (if the controller + belongs to a module) and the controller. + * If one of the calls returns false, the rest of the uncalled `beforeAction()` will be skipped and the + action execution will be cancelled. + * By default, each `beforeAction()` method call will trigger a `beforeAction` event to which you can attach a handler. +4. The controller runs the action: + * The action parameters will be analyzed and populated from the request data; +5. The controller sequentially calls the `afterAction()` method of the controller, the module (if the controller + belongs to a module) and the application. + * By default, each `afterAction()` method call will trigger an `afterAction` event to which you can attach a handler. +6. The application will take the action result and assign it to the [response](runtime-responses.md). + + +## Best Practices + +In a well-designed application, controllers are often very thin with each action containing only a few lines of code. +If your controller is rather complicated, it usually indicates that you should refactor it and move some code +to other classes. + +In summary, controllers + +* may access the [request](runtime-requests.md) data; +* may call methods of [models](structure-models.md) and other service components with request data; +* may use [views](structure-views.md) to compose responses; +* should NOT process the request data - this should be done in [models](structure-models.md); +* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md). diff --git a/docs/guide-zh-CN/structure-entry-scripts.md b/docs/guide-zh-CN/structure-entry-scripts.md new file mode 100644 index 0000000..2992ccd --- /dev/null +++ b/docs/guide-zh-CN/structure-entry-scripts.md @@ -0,0 +1,104 @@ +入口脚本 +============= + +入口脚本是应用启动流程中的第一环,一个应用(不管是网页应用还是控制台应用)只有一个入口脚本。终端用户的请求通过入口脚本实例化应用并将将请求转发到应用。 + +Web 应用的入口脚本必须放在终端用户能够访问的目录下,通常命名为 `index.php`,也可以使用 Web 服务器能定位到的其他名称。 + +控制台应用的入口脚本一般在应用根目录下命名为 `yii`(后缀为.php),该文件需要有执行权限,这样用户就能通过命令 `./yii [arguments] [options]` 来运行控制台应用。 + +入口脚本主要完成以下工作: + +* 定义全局常量; +* 注册 [Composer 自动加载器](http://getcomposer.org/doc/01-basic-usage.md#autoloading); +* 包含 [[Yii]] 类文件; +* 加载应用配置; +* 创建一个[应用](structure-applications.md)实例并配置; +* 调用 [[yii\base\Application::run()]] 来处理请求。 + + +## Web 应用 + +以下是[基础应用模版](start-installation.md)入口脚本的代码: + +```php +run(); +``` + + +## 控制台应用 + +以下是一个控制台应用的入口脚本: + +```php +#!/usr/bin/env php +run(); +exit($exitCode); +``` + + +## 定义常量 + +入口脚本是定义全局常量的最好地方,Yii 支持以下三个常量: + +* `YII_DEBUG`:标识应用是否运行在调试模式。当在调试模式下,应用会保留更多日志信息,如果抛出异常,会显示详细的错误调用堆栈。因此,调试模式主要适合在开发阶段使用,`YII_DEBUG` 默认值为 false。 +* `YII_ENV`:标识应用运行的环境,详情请查阅[配置](concept-configurations.md#environment-constants)章节。`YII_ENV` 默认值为 `'prod'`,表示应用运行在线上产品环境。 +* `YII_ENABLE_ERROR_HANDLER`:标识是否启用 Yii 提供的错误处理,默认为 true。 + +当定义一个常量时,通常使用类似如下代码来定义: + +```php +defined('YII_DEBUG') or define('YII_DEBUG', true); +``` + +上面的代码等同于: + +```php +if (!defined('YII_DEBUG')) { + define('YII_DEBUG', true); +} +``` + +显然第一段代码更加简洁易懂。 + +常量定义应该在入口脚本的开头,这样包含其他 PHP 文件时,常量就能生效。 \ No newline at end of file diff --git a/docs/guide-zh-CN/structure-models.md b/docs/guide-zh-CN/structure-models.md new file mode 100644 index 0000000..e483d73 --- /dev/null +++ b/docs/guide-zh-CN/structure-models.md @@ -0,0 +1,511 @@ +Models +====== + +Models are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture. +They are objects representing business data, rules and logic. + +You can create model classes by extending [[yii\base\Model]] or its child classes. The base class +[[yii\base\Model]] supports many useful features: + +* [Attributes](#attributes): represent the business data and can be accessed like normal object properties + or array elements; +* [Attribute labels](#attribute-labels): specify the display labels for attributes; +* [Massive assignment](#massive-assignment): supports populating multiple attributes in a single step; +* [Validation rules](#validation-rules): ensures input data based on the declared validation rules; +* [Data Exporting](#data-exporting): allows model data to be exported in terms of arrays with customizable formats. + +The `Model` class is also the base class for more advanced models, such as [Active Record](db-active-record.md). +Please refer to the relevant documentation for more details about these advanced models. + +> Info: You are not required to base your model classes on [[yii\base\Model]]. However, because there are many Yii + components built to support [[yii\base\Model]], it is usually the preferable base class for a model. + + +## Attributes + +Models represent business data in terms of *attributes*. Each attribute is like a publicly accessible property +of a model. The method [[yii\base\Model::attributes()]] specifies what attributes a model class has. + +You can access an attribute like accessing a normal object property: + +```php +$model = new \app\models\ContactForm; + +// "name" is an attribute of ContactForm +$model->name = 'example'; +echo $model->name; +``` + +You can also access attributes like accessing array elements, thanks to the support for +[ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) and [ArrayIterator](http://php.net/manual/en/class.arrayiterator.php) +by [[yii\base\Model]]: + +```php +$model = new \app\models\ContactForm; + +// accessing attributes like array elements +$model['name'] = 'example'; +echo $model['name']; + +// iterate attributes +foreach ($model as $name => $value) { + echo "$name: $value\n"; +} +``` + + +### Defining Attributes + +By default, if your model class extends directly from [[yii\base\Model]], all its *non-static public* member +variables are attributes. For example, the `ContactForm` model class below has four attributes: `name`, `email`, +`subject` and `body`. The `ContactForm` model is used to represent the input data received from an HTML form. + +```php +namespace app\models; + +use yii\base\Model; + +class ContactForm extends Model +{ + public $name; + public $email; + public $subject; + public $body; +} +``` + + +You may override [[yii\base\Model::attributes()]] to define attributes in a different way. The method should +return the names of the attributes in a model. For example, [[yii\db\ActiveRecord]] does so by returning +the column names of the associated database table as its attribute names. Note that you may also need to +override the magic methods such as `__get()`, `__set()` so that the attributes can be accessed like +normal object properties. + + +### Attribute Labels + +When displaying values or getting input for attributes, you often need to display some labels associated +with attributes. For example, given an attribute named `firstName`, you may want to display a label `First Name` +which is more user-friendly when displayed to end users in places such as form inputs and error messages. + +You can get the label of an attribute by calling [[yii\base\Model::getAttributeLabel()]]. For example, + +```php +$model = new \app\models\ContactForm; + +// displays "Name" +echo $model->getAttributeLabel('name'); +``` + +By default, attribute labels are automatically generated from attribute names. The generation is done by +the method [[yii\base\Model::generateAttributeLabel()]]. It will turn camel-case variable names into +multiple words with the first letter in each word in upper case. For example, `username` becomes `Username`, +and `firstName` becomes `First Name`. + +If you do not want to use automatically generated labels, you may override [[yii\base\Model::attributeLabels()]] +to explicitly declare attribute labels. For example, + +```php +namespace app\models; + +use yii\base\Model; + +class ContactForm extends Model +{ + public $name; + public $email; + public $subject; + public $body; + + public function attributeLabels() + { + return [ + 'name' => 'Your name', + 'email' => 'Your email address', + 'subject' => 'Subject', + 'body' => 'Content', + ]; + } +} +``` + +For applications supporting multiple languages, you may want to translate attribute labels. This can be done +in the [[yii\base\Model::attributeLabels()|attributeLabels()]] method as well, like the following: + +```php +public function attributeLabels() +{ + return [ + 'name' => \Yii::t('app', 'Your name'), + 'email' => \Yii::t('app', 'Your email address'), + 'subject' => \Yii::t('app', 'Subject'), + 'body' => \Yii::t('app', 'Content'), + ]; +} +``` + +You may even conditionally define attribute labels. For example, based on the [scenario](#scenarios) the model +is being used in, you may return different labels for the same attribute. + +> Info: Strictly speaking, attribute labels are part of [views](structure-views.md). But declaring labels + in models is often very convenient and can result in very clean and reusable code. + + +## Scenarios + +A model may be used in different *scenarios*. For example, a `User` model may be used to collect user login inputs, +but it may also be used for the user registration purpose. In different scenarios, a model may use different +business rules and logic. For example, the `email` attribute may be required during user registration, +but not so during user login. + +A model uses the [[yii\base\Model::scenario]] property to keep track of the scenario it is being used in. +By default, a model supports only a single scenario named `default`. The following code shows two ways of +setting the scenario of a model: + +```php +// scenario is set as a property +$model = new User; +$model->scenario = 'login'; + +// scenario is set through configuration +$model = new User(['scenario' => 'login']); +``` + +By default, the scenarios supported by a model are determined by the [validation rules](#validation-rules) declared +in the model. However, you can customize this behavior by overriding the [[yii\base\Model::scenarios()]] method, +like the following: + +```php +namespace app\models; + +use yii\db\ActiveRecord; + +class User extends ActiveRecord +{ + public function scenarios() + { + return [ + 'login' => ['username', 'password'], + 'register' => ['username', 'email', 'password'], + ]; + } +} +``` + +> Info: In the above and following examples, the model classes are extending from [[yii\db\ActiveRecord]] + because the usage of multiple scenarios usually happens to [Active Record](db-active-record.md) classes. + +The `scenarios()` method returns an array whose keys are the scenario names and values the corresponding +*active attributes*. An active attribute can be [massively assigned](#massive-assignment) and is subject +to [validation](#validation-rules). In the above example, the `username` and `password` attributes are active +in the `login` scenario; while in the `register` scenario, `email` is also active besides `username` and `password`. + +The default implementation of `scenarios()` will return all scenarios found in the validation rule declaration +method [[yii\base\Model::rules()]]. When overriding `scenarios()`, if you want to introduce new scenarios +in addition to the default ones, you may write code like the following: + +```php +namespace app\models; + +use yii\db\ActiveRecord; + +class User extends ActiveRecord +{ + public function scenarios() + { + $scenarios = parent::scenarios(); + $scenarios['login'] = ['username', 'password']; + $scenarios['register'] = ['username', 'email', 'password']; + return $scenarios; + } +} +``` + +The scenario feature is primarily used by [validation](#validation-rules) and [massive attribute assignment](#massive-assignment). +You can, however, use it for other purposes. For example, you may declare [attribute labels](#attribute-labels) +differently based on the current scenario. + + +## Validation Rules + +When the data for a model is received from end users, it should be validated to make sure it satisfies +certain rules (called *validation rules*, also known as *business rules*). For example, given a `ContactForm` model, +you may want to make sure all attributes are not empty and the `email` attribute contains a valid email address. +If the values for some attributes do not satisfy the corresponding business rules, appropriate error messages +should be displayed to help the user to fix the errors. + +You may call [[yii\base\Model::validate()]] to validate the received data. The method will use +the validation rules declared in [[yii\base\Model::rules()]] to validate every relevant attribute. If no error +is found, it will return true. Otherwise, it will keep the errors in the [[yii\base\Model::errors]] property +and return false. For example, + +```php +$model = new \app\models\ContactForm; + +// populate model attributes with user inputs +$model->attributes = \Yii::$app->request->post('ContactForm'); + +if ($model->validate()) { + // all inputs are valid +} else { + // validation failed: $errors is an array containing error messages + $errors = $model->errors; +} +``` + + +To declare validation rules associated with a model, override the [[yii\base\Model::rules()]] method by returning +the rules that the model attributes should satisfy. The following example shows the validation rules declared +for the `ContactForm` model: + +```php +public function rules() +{ + return [ + // the name, email, subject and body attributes are required + [['name', 'email', 'subject', 'body'], 'required'], + + // the email attribute should be a valid email address + ['email', 'email'], + ]; +} +``` + +A rule can be used to validate one or multiple attributes, and an attribute may be validated by one or multiple rules. +Please refer to the [Validating Input](input-validation.md) section for more details on how to declare +validation rules. + +Sometimes, you may want a rule to be applied only in certain [scenarios](#scenarios). To do so, you can +specify the `on` property of a rule, like the following: + +```php +public function rules() +{ + return [ + // username, email and password are all required in "register" scenario + [['username', 'email', 'password'], 'required', 'on' => 'register'], + + // username and password are required in "login" scenario + [['username', 'password'], 'required', 'on' => 'login'], + ]; +} +``` + +If you do not specify the `on` property, the rule would be applied in all scenarios. A rule is called +an *active rule* if it can be applied in the current [[yii\base\Model::scenario|scenario]]. + +An attribute will be validated if and only if it is an active attribute declared in `scenarios()` and +is associated with one or multiple active rules declared in `rules()`. + + +## Massive Assignment + +Massive assignment is a convenient way of populating a model with user inputs using a single line of code. +It populates the attributes of a model by assigning the input data directly to the [[yii\base\Model::$attributes]] +property. The following two pieces of code are equivalent, both trying to assign the form data submitted by end users +to the attributes of the `ContactForm` model. Clearly, the former, which uses massive assignment, is much cleaner +and less error prone than the latter: + +```php +$model = new \app\models\ContactForm; +$model->attributes = \Yii::$app->request->post('ContactForm'); +``` + +```php +$model = new \app\models\ContactForm; +$data = \Yii::$app->request->post('ContactForm', []); +$model->name = isset($data['name']) ? $data['name'] : null; +$model->email = isset($data['email']) ? $data['email'] : null; +$model->subject = isset($data['subject']) ? $data['subject'] : null; +$model->body = isset($data['body']) ? $data['body'] : null; +``` + + +### Safe Attributes + +Massive assignment only applies to the so-called *safe attributes* which are the attributes listed in +[[yii\base\Model::scenarios()]] for the current [[yii\base\Model::scenario|scenario]] of a model. +For example, if the `User` model has the following scenario declaration, then when the current scenario +is `login`, only the `username` and `password` can be massively assigned. Any other attributes will +be kept untouched. + +```php +public function scenarios() +{ + return [ + 'login' => ['username', 'password'], + 'register' => ['username', 'email', 'password'], + ]; +} +``` + +> Info: The reason that massive assignment only applies to safe attributes is because you want to + control which attributes can be modified by end user data. For example, if the `User` model + has a `permission` attribute which determines the permission assigned to the user, you would + like this attribute to be modifiable by administrators through a backend interface only. + +Because the default implementation of [[yii\base\Model::scenarios()]] will return all scenarios and attributes +found in [[yii\base\Model::rules()]], if you do not override this method, it means an attribute is safe as long +as it appears in one of the active validation rules. + +For this reason, a special validator aliased `safe` is provided so that you can declare an attribute +to be safe without actually validating it. For example, the following rules declare that both `title` +and `description` are safe attributes. + +```php +public function rules() +{ + return [ + [['title', 'description'], 'safe'], + ]; +} +``` + + +### Unsafe Attributes + +As described above, the [[yii\base\Model::scenarios()]] method serves for two purposes: determining which attributes +should be validated, and determining which attributes are safe. In some rare cases, you may want to validate +an attribute but do not want to mark it safe. You can do so by prefixing an exclamation mark `!` to the attribute +name when declaring it in `scenarios()`, like the `secret` attribute in the following: + +```php +public function scenarios() +{ + return [ + 'login' => ['username', 'password', '!secret'], + ]; +} +``` + +When the model is in the `login` scenario, all three attributes will be validated. However, only the `username` +and `password` attributes can be massively assigned. To assign an input value to the `secret` attribute, you +have to do it explicitly as follows, + +```php +$model->secret = $secret; +``` + + +## Data Exporting + +Models often need to be exported in different formats. For example, you may want to convert a collection of +models into JSON or Excel format. The exporting process can be broken down into two independent steps. +In the first step, models are converted into arrays; in the second step, the arrays are converted into +target formats. You may just focus on the first step, because the second step can be achieved by generic +data formatters, such as [[yii\web\JsonResponseFormatter]]. + +The simplest way of converting a model into an array is to use the [[yii\base\Model::$attributes]] property. +For example, + +```php +$post = \app\models\Post::findOne(100); +$array = $post->attributes; +``` + +By default, the [[yii\base\Model::$attributes]] property will return the values of *all* attributes +declared in [[yii\base\Model::attributes()]]. + +A more flexible and powerful way of converting a model into an array is to use the [[yii\base\Model::toArray()]] +method. Its default behavior is the same as that of [[yii\base\Model::$attributes]]. However, it allows you +to choose which data items, called *fields*, to be put in the resulting array and how they should be formatted. +In fact, it is the default way of exporting models in RESTful Web service development, as described in +the [Response Formatting](rest-response-formatting.md). + + +### Fields + +A field is simply a named element in the array that is obtained by calling the [[yii\base\Model::toArray()]] method +of a model. + +By default, field names are equivalent to attribute names. However, you can change this behavior by overriding +the [[yii\base\Model::fields()|fields()]] and/or [[yii\base\Model::extraFields()|extraFields()]] methods. Both methods +should return a list of field definitions. The fields defined by `fields()` are default fields, meaning that +`toArray()` will return these fields by default. The `extraFields()` method defines additionally available fields +which can also be returned by `toArray()` as long as you specify them via the `$expand` parameter. For example, +the following code will return all fields defined in `fields()` and the `prettyName` and `fullAddress` fields +if they are defined in `extraFields()`. + +```php +$array = $model->toArray([], ['prettyName', 'fullAddress']); +``` + +You can override `fields()` to add, remove, rename or redefine fields. The return value of `fields()` +should be an array. The array keys are the field names, and the array values are the corresponding +field definitions which can be either property/attribute names or anonymous functions returning the +corresponding field values. In the special case when a field name is the same as its defining attribute +name, you can omit the array key. For example, + +```php +// explicitly list every field, best used when you want to make sure the changes +// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility). +public function fields() +{ + return [ + // field name is the same as the attribute name + 'id', + + // field name is "email", the corresponding attribute name is "email_address" + 'email' => 'email_address', + + // field name is "name", its value is defined by a PHP callback + 'name' => function () { + return $this->first_name . ' ' . $this->last_name; + }, + ]; +} + +// filter out some fields, best used when you want to inherit the parent implementation +// and blacklist some sensitive fields. +public function fields() +{ + $fields = parent::fields(); + + // remove fields that contain sensitive information + unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']); + + return $fields; +} +``` + +> Warning: Because by default all attributes of a model will be included in the exported array, you should +> examine your data to make sure they do not contain sensitive information. If there is such information, +> you should override `fields()` to filter them out. In the above example, we choose +> to filter out `auth_key`, `password_hash` and `password_reset_token`. + + +## Best Practices + +Models are the central places to represent business data, rules and logic. They often need to be reused +in different places. In a well-designed application, models are usually much fatter than +[controllers](structure-controllers.md). + +In summary, models + +* may contain attributes to represent business data; +* may contain validation rules to ensure the data validity and integrity; +* may contain methods implementing business logic; +* should NOT directly access request, session, or any other environmental data. These data should be injected + by [controllers](structure-controllers.md) into models; +* should avoid embedding HTML or other presentational code - this is better done in [views](structure-views.md); +* avoid having too many [scenarios](#scenarios) in a single model. + +You may usually consider the last recommendation above when you are developing large complex systems. +In these systems, models could be very fat because they are used in many places and may thus contain many sets +of rules and business logic. This often ends up in a nightmare in maintaining the model code +because a single touch of the code could affect several different places. To make the mode code more maintainable, +you may take the following strategy: + +* Define a set of base model classes that are shared by different [applications](structure-applications.md) or + [modules](structure-modules.md). These model classes should contain minimal sets of rules and logic that + are common among all their usages. +* In each [application](structure-applications.md) or [module](structure-modules.md) that uses a model, + define a concrete model class by extending from the corresponding base model class. The concrete model classes + should contain rules and logic that are specific for that application or module. + +For example, in the [Advanced Application Template](tutorial-advanced-app.md), you may define a base model +class `common\models\Post`. Then for the front end application, you define and use a concrete model class +`frontend\models\Post` which extends from `common\models\Post`. And similarly for the back end application, +you define `backend\models\Post`. With this strategy, you will be sure that the code in `frontend\models\Post` +is only specific to the front end application, and if you make any change to it, you do not need to worry if +the change may break the back end application. diff --git a/docs/guide-zh-CN/structure-overview.md b/docs/guide-zh-CN/structure-overview.md new file mode 100644 index 0000000..93bf583 --- /dev/null +++ b/docs/guide-zh-CN/structure-overview.md @@ -0,0 +1,18 @@ +总览 +======== + +Yii 应用参照[模型-视图-控制器 (MVC)](http://wikipedia.org/wiki/Model-view-controller) + 设计模式来组织。 [模型](structure-models.md)代表数据、业务逻辑和规则;[视图](structure-views.md)展示模型的输出;[控制器](structure-controllers.md)接受出入并将其转换为[模型](structure-models.md)和[视图](structure-views.md)命令。 + +除了 MVC, Yii 应用还有以下部分: + +* [入口脚本](structure-entry-scripts.md):终端用户能直接访问的 PHP 脚本,负责启动一个请求处理周期。 +* [应用](structure-applications.md):能全局范围内访问的对象,管理协调组件来完成请求. +* [应用组件](structure-application-components.md):在应用中注册的对象,提供不同的功能来完成请求。 +* [模块](structure-modules.md):包含完整 MVC 结构的独立包,一个应用可以由多个模块组建。 +* [过滤器](structure-filters.md):控制器在处理请求之前或之后需要触发执行的代码。 +* [小部件](structure-widgets.md):可嵌入到[视图](structure-views.md)中的对象,可包含控制器逻辑,可被不同视图重复调用。 + +下面的示意图展示了 Yii 应用的静态结构: + +![Yii应用静态结构](images/application-structure.png) diff --git a/docs/guide-zh-CN/structure-views.md b/docs/guide-zh-CN/structure-views.md new file mode 100644 index 0000000..1b13333 --- /dev/null +++ b/docs/guide-zh-CN/structure-views.md @@ -0,0 +1,722 @@ +Views +===== + +Views are part of the [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) architecture. +They are code responsible for presenting data to end users. In a Web application, views are usually created +in terms of *view templates* which are PHP script files containing mainly HTML code and presentational PHP code. +They are managed by the [[yii\web\View|view]] application component which provides commonly used methods +to facilitate view composition and rendering. For simplicity, we often call view templates or view template files +as views. + + +## Creating Views + +As aforementioned, a view is simply a PHP script mixed with HTML and PHP code. The following is the view +that presents a login form. As you can see, PHP code is used to generate the dynamic content, such as the +page title and the form, while HTML code organizes them into a presentable HTML page. + +```php +title = 'Login'; +?> +

title) ?>

+ +

Please fill out the following fields to login:

+ + + field($model, 'username') ?> + field($model, 'password')->passwordInput() ?> + + +``` + +Within a view, you can access `$this` which refers to the [[yii\web\View|view component]] managing +and rendering this view template. + +Besides `$this`, there may be other predefined variables in a view, such as `$model` in the above +example. These variables represent the data that are *pushed* into the view by [controllers](structure-controllers.md) +or other objects whose trigger the [view rendering](#rendering-views). + +> Tip: The predefined variables are listed in a comment block at beginning of a view so that they can + be recognized by IDEs. It is also a good way of documenting your views. + + +### Security + +When creating views that generate HTML pages, it is important that you encode and/or filter the data coming +from end users before presenting them. Otherwise, your application may be subject to +[cross-site scripting](http://en.wikipedia.org/wiki/Cross-site_scripting) attacks. + +To display a plain text, encode it first by calling [[yii\helpers\Html::encode()]]. For example, the following code +encodes the user name before displaying it: + +```php + + +
+ name) ?> +
+``` + +To display HTML content, use [[yii\helpers\HtmlPurifier]] to filter the content first. For example, the following +code filters the post content before displaying it: + +```php + + +
+ text) ?> +
+``` + +> Tip: While HTMLPurifier does excellent job in making output safe, it is not fast. You should consider + [caching](caching-overview.md) the filtering result if your application requires high performance. + + +### Organizing Views + +Like [controllers](structure-controllers.md) and [models](structure-models.md), there are conventions to organize views. + +* For views rendered by a controller, they should be put under the directory `@app/views/ControllerID` by default, + where `ControllerID` refers to the [controller ID](structure-controllers.md#routes). For example, if + the controller class is `PostController`, the directory would be `@app/views/post`; If it is `PostCommentController`, + the directory would be `@app/views/post-comment`. In case the controller belongs to a module, the directory + would be `views/ControllerID` under the [[yii\base\Module::basePath|module directory]]. +* For views rendered in a [widget](structure-widgets.md), they should be put under the `WidgetPath/views` directory by + default, where `WidgetPath` stands for the directory containing the widget class file. +* For views rendered by other objects, it is recommended that you follow the similar convention as that for widgets. + +You may customize these default view directories by overriding the [[yii\base\ViewContextInterface::getViewPath()]] +method of controllers or widgets. + + +## Rendering Views + +You can render views in [controllers](structure-controllers.md), [widgets](structure-widgets.md), or any +other places by calling view rendering methods. These methods share a similar signature shown as follows, + +``` +/** + * @param string $view view name or file path, depending on the actual rendering method + * @param array $params the data to be passed to the view + * @return string rendering result + */ +methodName($view, $params = []) +``` + + +### Rendering in Controllers + +Within [controllers](structure-controllers.md), you may call the following controller methods to render views: + +* [[yii\base\Controller::render()|render()]]: renders a [named view](#named-views) and applies a [layout](#layouts) + to the rendering result. +* [[yii\base\Controller::renderPartial()|renderPartial()]]: renders a [named view](#named-views) without any layout. +* [[yii\web\Controller::renderAjax()|renderAjax()]]: renders a [named view](#named-views) without any layout, + and injects all registered JS/CSS scripts and files. It is usually used in response to AJAX Web requests. +* [[yii\base\Controller::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or + [alias](concept-aliases.md). + +For example, + +```php +namespace app\controllers; + +use Yii; +use app\models\Post; +use yii\web\Controller; +use yii\web\NotFoundHttpException; + +class PostController extends Controller +{ + public function actionView($id) + { + $model = Post::findOne($id); + if ($model === null) { + throw new NotFoundHttpException; + } + + // renders a view named "view" and applies a layout to it + return $this->render('view', [ + 'model' => $model, + ]); + } +} +``` + + +### Rendering in Widgets + +Within [widgets](structure-widgets.md), you may call the following widget methods to render views. + +* [[yii\base\Widget::render()|render()]]: renders a [named view](#named-views). +* [[yii\base\Widget::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or + [alias](concept-aliases.md). + +For example, + +```php +namespace app\components; + +use yii\base\Widget; +use yii\helpers\Html; + +class ListWidget extends Widget +{ + public $items = []; + + public function run() + { + // renders a view named "list" + return $this->render('list', [ + 'items' => $this->items, + ]); + } +} +``` + + +### Rendering in Views + +You can render a view within another view by calling one of the following methods provided by the [[yii\base\View|view component]]: + +* [[yii\base\View::render()|render()]]: renders a [named view](#named-views). +* [[yii\web\View::renderAjax()|renderAjax()]]: renders a [named view](#named-views) and injects all registered + JS/CSS scripts and files. It is usually used in response to AJAX Web requests. +* [[yii\base\View::renderFile()|renderFile()]]: renders a view specified in terms of a view file path or + [alias](concept-aliases.md). + +For example, the following code in a view renders the `_overview.php` view file which is in the same directory +as the view being currently rendered. Remember that `$this` in a view refers to the [[yii\base\View|view]] component: + +```php +render('_overview') ?> +``` + + +### Rendering in Other Places + +In any place, you can get access to the [[yii\base\View|view]] application component by the expression +`Yii::$app->view` and then call its aforementioned methods to render a view. For example, + +```php +// displays the view file "@app/views/site/license.php" +echo \Yii::$app->view->renderFile('@app/views/site/license.php'); +``` + + +### Named Views + +When you render a view, you can specify the view using either a view name or a view file path/alias. In most cases, +you would use the former because it is more concise and flexible. We call views specified using names as *named views*. + +A view name is resolved into the corresponding view file path according to the following rules: + +* A view name may omit the file extension name. In this case, `.php` will be used as the extension. For example, + the view name `about` corresponds to the file name `about.php`. +* If the view name starts with double slashes `//`, the corresponding view file path would be `@app/views/ViewName`. + That is, the view is looked for under the [[yii\base\Application::viewPath|application's view path]]. + For example, `//site/about` will be resolved into `@app/views/site/about.php`. +* If the view name starts with a single slash `/`, the view file path is formed by prefixing the view name + with the [[yii\base\Module::viewPath|view path]] of the currently active [module](structure-modules.md). + If there is no active module, `@app/views/ViewName` will be used. For example, `/user/create` will be resolved into + `@app/modules/user/views/user/create.php`, if the currently active module is `user`. If there is no active module, + the view file path would be `@app/views/user/create.php`. +* If the view is rendered with a [[yii\base\View::context|context]] and the context implements [[yii\base\ViewContextInterface]], + the view file path is formed by prefixing the [[yii\base\ViewContextInterface::getViewPath()|view path]] of the + context to the view name. This mainly applies to the views rendered within controllers and widgets. For example, + `site/about` will be resolved into `@app/views/site/about.php` if the context is the controller `SiteController`. +* If a view is rendered within another view, the directory containing the other view file will be prefixed to + the new view name to form the actual view file path. For example, `item` will be resolved into `@app/views/post/item` + if it is being rendered in the view `@app/views/post/index.php`. + +According to the above rules, calling `$this->render('view')` in a controller `app\controllers\PostController` will +actually render the view file `@app/views/post/view.php`, while calling `$this->render('_overview')` in that view +will render the view file `@app/views/post/_overview.php`. + + +### Accessing Data in Views + +There are two approaches to access data within a view: push and pull. + +By passing the data as the second parameter to the view rendering methods, you are using the push approach. +The data should be represented as an array of name-value pairs. When the view is being rendered, the PHP +`extract()` function will be called on this array so that the array is extracted into variables in the view. +For example, the following view rendering code in a controller will push two variables to the `report` view: +`$foo = 1` and `$bar = 2`. + +```php +echo $this->render('report', [ + 'foo' => 1, + 'bar' => 2, +]); +``` + +The pull approach actively retrieves data from the [[yii\base\View|view component]] or other objects accessible +in views (e.g. `Yii::$app`). Using the code below as an example, within the view you can get the controller object +by the expression `$this->context`. And as a result, it is possible for you to access any properties or methods +of the controller in the `report` view, such as the controller ID shown in the following: + +```php +The controller ID is: context->id ?> +?> +``` + +The push approach is usually the preferred way of accessing data in views, because it makes views less dependent +on context objects. Its drawback is that you need to manually build the data array all the time, which could +become tedious and error prone if a view is shared and rendered in different places. + + +### Sharing Data among Views + +The [[yii\base\View|view component]] provides the [[yii\base\View::params|params]] property that you can use +to share data among views. + +For example, in an `about` view, you can have the following code which specifies the current segment of the +breadcrumbs. + +```php +$this->params['breadcrumbs'][] = 'About Us'; +``` + +Then, in the [layout](#layouts) file, which is also a view, you can display the breadcrumbs using the data +passed along [[yii\base\View::params|params]]: + +```php + isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], +]) ?> +``` + + +## Layouts + +Layouts are a special type of views that represent the common parts of multiple views. For example, the pages +for most Web applications share the same page header and footer. While you can repeat the same page header and footer +in every view, a better way is to do this once in a layout and embed the rendering result of a content view at +an appropriate place in the layout. + + +### Creating Layouts + +Because layouts are also views, they can be created in the similar way as normal views. By default, layouts +are stored in the directory `@app/views/layouts`. For layouts used within a [module](structure-modules.md), +they should be stored in the `views/layouts` directory under the [[yii\base\Module::basePath|module directory]]. +You may customize the default layout directory by configuring the [[yii\base\Module::layoutPath]] property of +the application or modules. + +The following example shows how a layout looks like. Note that for illustrative purpose, we have greatly simplified +the code in the layout. In practice, you may want to add more content to it, such as head tags, main menu, etc. + +```php + +beginPage() ?> + + + + + + <?= Html::encode($this->title) ?> + head() ?> + + +beginBody() ?> +
My Company
+ + +endBody() ?> + + +endPage() ?> +``` + +As you can see, the layout generates the HTML tags that are common to all pages. Within the `` section, +the layout echoes the `$content` variable which represents the rendering result of content views and is pushed +into the layout when [[yii\base\Controller::render()]] is called. + +Most layouts should call the following methods like shown in the above code. These methods mainly trigger events +about the rendering process so that scripts and tags registered in other places can be properly injected into +the places where these methods are called. + +- [[yii\base\View::beginPage()|beginPage()]]: This method should be called at the very beginning of the layout. + It triggers the [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]] event which indicates the beginning of a page. +- [[yii\base\View::endPage()|endPage()]]: This method should be called at the end of the layout. + It triggers the [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]] event which indicates the end of a page. +- [[yii\web\View::head()|head()]]: This method should be called within the `` section of an HTML page. + It generates a placeholder which will be replaced with the registered head HTML code (e.g. link tags, meta tags) + when a page finishes rendering. +- [[yii\web\View::beginBody()|beginBody()]]: This method should be called at the beginning of the `` section. + It triggers the [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]] event and generates a placeholder which will + be replaced by the registered HTML code (e.g. JavaScript) targeted at the body begin position. +- [[yii\web\View::endBody()|endBody()]]: This method should be called at the end of the `` section. + It triggers the [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]] event and generates a placeholder which will + be replaced by the registered HTML code (e.g. JavaScript) targeted at the body end position. + + +### Accessing Data in Layouts + +Within a layout, you have access to two predefined variables: `$this` and `$content`. The former refers to +the [[yii\base\View|view]] component, like in normal views, while the latter contains the rendering result of a content +view which is rendered by calling the [[yii\base\Controller::render()|render()]] method in controllers. + +If you want to access other data in layouts, you have to use the pull method as described in +the [Accessing Data in Views](#accessing-data-in-views) subsection. If you want to pass data from a content view +to a layout, you may use the method described in the [Sharing Data among Views](#sharing-data-among-views) subsection. + + +### Using Layouts + +As described in the [Rendering in Controllers](#rendering-in-controllers) subsection, when you render a view +by calling the [[yii\base\Controller::render()|render()]] method in a controller, a layout will be applied +to the rendering result. By default, the layout `@app/views/layouts/main.php` will be used. + +You may use a different layout by configuring either [[yii\base\Application::layout]] or [[yii\base\Controller::layout]]. +The former governs the layout used by all controllers, while the latter overrides the former for individual controllers. +For example, the following code makes the `post` controller to use `@app/views/layouts/post.php` as the layout +when rendering its views. Other controllers, assuming their `layout` property is untouched, will still use the default +`@app/views/layouts/main.php` as the layout. + +```php +namespace app\controllers; + +use yii\web\Controller; + +class PostController extends Controller +{ + public $layout = 'post'; + + // ... +} +``` + +For controllers belonging to a module, you may also configure the module's [[yii\base\Module::layout|layout]] property to +use a particular layout for these controllers. + +Because the `layout` property may be configured at different levels (controllers, modules, application), +behind the scene Yii takes two steps to determine what is the actual layout file being used for a particular controller. + +In the first step, it determines the layout value and the context module: + +- If the [[yii\base\Controller::layout]] property of the controller is not null, use it as the layout value and + the [[yii\base\Controller::module|module]] of the controller as the context module. +- If [[yii\base\Controller::layout|layout]] is null, search through all ancestor modules (including the application itself) of the controller and + find the first module whose [[yii\base\Module::layout|layout]] property is not null. Use that module and + its [[yii\base\Module::layout|layout]] value as the context module and the chosen layout value. + If such a module cannot be found, it means no layout will be applied. + +In the second step, it determines the actual layout file according to the layout value and the context module +determined in the first step. The layout value can be: + +- a path alias (e.g. `@app/views/layouts/main`). +- an absolute path (e.g. `/main`): the layout value starts with a slash. The actual layout file will be + looked for under the application's [[yii\base\Application::layoutPath|layout path]] which defaults to + `@app/views/layouts`. +- a relative path (e.g. `main`): the actual layout file will be looked for under the context module's + [[yii\base\Module::layoutPath|layout path]] which defaults to the `views/layouts` directory under the + [[yii\base\Module::basePath|module directory]]. +- the boolean value `false`: no layout will be applied. + +If the layout value does not contain a file extension, it will use the default one `.php`. + + +### Nested Layouts + +Sometimes you may want to nest one layout in another. For example, in different sections of a Web site, you +want to use different layouts, while all these layouts share the same basic layout that generates the overall +HTML5 page structure. You can achieve this goal by calling [[yii\base\View::beginContent()|beginContent()]] and +[[yii\base\View::endContent()|endContent()]] in the child layouts like the following: + +```php +beginContent('@app/views/layouts/base.php'); ?> + +...child layout content here... + +endContent(); ?> +``` + +As shown above, the child layout content should be enclosed within [[yii\base\View::beginContent()|beginContent()]] and +[[yii\base\View::endContent()|endContent()]]. The parameter passed to [[yii\base\View::beginContent()|beginContent()]] +specifies what is the parent layout. It can be either a layout file or alias. + +Using the above approach, you can nest layouts in more than one levels. + + +### Using Blocks + +Blocks allow you to specify the view content in one place while displaying it in another. They are often used together +with layouts. For example, you can define a block in a content view and display it in the layout. + +You call [[yii\base\View::beginBlock()|beginBlock()]] and [[yii\base\View::endBlock()|endBlock()]] to define a block. +The block can then be accessed via `$view->blocks[$blockID]`, where `$blockID` stands for a unique ID that you assign +to the block when defining it. + +The following example shows how you can use blocks to customize specific parts of a layout in a content view. + +First, in a content view, define one or multiple blocks: + +```php +... + +beginBlock('block1'); ?> + +...content of block1... + +endBlock(); ?> + +... + +beginBlock('block3'); ?> + +...content of block3... + +endBlock(); ?> +``` + +Then, in the layout view, render the blocks if they are available, or display some default content if a block is +not defined. + +```php +... +blocks['block1'])): ?> + blocks['block1'] ?> + + ... default content for block1 ... + + +... + +blocks['block2'])): ?> + blocks['block2'] ?> + + ... default content for block2 ... + + +... + +blocks['block3'])): ?> + blocks['block3'] ?> + + ... default content for block3 ... + +... +``` + + +## Using View Components + +[[yii\base\View|View components]] provides many view-related features. While you can get view components +by creating individual instances of [[yii\base\View]] or its child class, in most cases you will mainly use +the `view` application component. You can configure this component in [application configurations](structure-applications.md#application-configurations) +like the following: + +```php +[ + // ... + 'components' => [ + 'view' => [ + 'class' => 'app\components\View', + ], + // ... + ], +] +``` + +View components provide the following useful view-related features, each described in more details in a separate section: + +* [theming](output-theming.md): allows you to develop and change the theme for your Web site. +* [fragment caching](caching-fragment.md): allows you to cache a fragment within a Web page. +* [client script handling](output-client-scripts.md): supports CSS and JavaScript registration and rendering. +* [asset bundle handling](structure-assets.md): supports registering and rendering of [asset bundles](structure-assets.md). +* [alternative template engines](tutorial-template-engines.md): allows you to use other template engines, such as + [Twig](http://twig.sensiolabs.org/), [Smarty](http://www.smarty.net/). + +You may also frequently use the following minor yet useful features when you are developing Web pages. + + +### Setting Page Titles + +Every Web page should have a title. Normally the title tag is being displayed in a [layout](#layouts). However, in practice +the title is often determined in content views rather than layouts. To solve this problem, [[yii\web\View]] provides +the [[yii\web\View::title|title]] property for you to pass the title information from content views to layouts. + +To make use of this feature, in each content view, you can set the page title like the following: + +```php +title = 'My page title'; +?> +``` + +Then in the layout, make sure you have the following code in the `` section: + +```php +<?= Html::encode($this->title) ?> +``` + + +### Registering Meta Tags + +Web pages usually need to generate various meta tags needed by different parties. Like page titles, meta tags +appear in the `` section and are usually generated in layouts. + +If you want to specify what meta tags to generate in content views, you can call [[yii\web\View::registerMetaTag()]] +in a content view, like the following: + +```php +registerMetaTag(['name' => 'keywords', 'content' => 'yii, framework, php']); +?> +``` + +The above code will register a "keywords" meta tag with the view component. The registered meta tag is +rendered after the layout finishes rendering. By then, the following HTML code will be inserted +at the place where you call [[yii\web\View::head()]] in the layout and generate the following HTML code: + +```php + +``` + +Note that if you call [[yii\web\View::registerMetaTag()]] multiple times, it will register multiple meta tags, +regardless whether the meta tags are the same or not. + +To make sure there is only a single instance of a meta tag type, you can specify a key as a second parameter when calling the method. +For example, the following code registers two "description" meta tags. However, only the second one will be rendered. + +```html +$this->registerMetaTag(['name' => 'description', 'content' => 'This is my cool website made with Yii!'], 'description'); +$this->registerMetaTag(['name' => 'description', 'content' => 'This website is about funny raccoons.'], 'description'); +``` + + +### Registering Link Tags + +Like [meta tags](#adding-meta-tags), link tags are useful in many cases, such as customizing favicon, pointing to +RSS feed or delegating OpenID to another server. You can work with link tags in the similar way as meta tags +by using [[yii\web\View::registerLinkTag()]]. For example, in a content view, you can register a link tag like follows, + +```php +$this->registerLinkTag([ + 'title' => 'Live News for Yii', + 'rel' => 'alternate', + 'type' => 'application/rss+xml', + 'href' => 'http://www.yiiframework.com/rss.xml/', +]); +``` + +The code above will result in + +```html + +``` + +Similar as [[yii\web\View::registerMetaTag()|registerMetaTags()]], you can specify a key when calling +[[yii\web\View::registerLinkTag()|registerLinkTag()]] to avoid generated repeated link tags. + + +## View Events + +[[yii\base\View|View components]] trigger several events during the view rendering process. You may respond +to these events to inject content into views or process the rendering results before they are sent to end users. + +- [[yii\base\View::EVENT_BEFORE_RENDER|EVENT_BEFORE_RENDER]]: triggered at the beginning of rendering a file + in a controller. Handlers of this event may set [[yii\base\ViewEvent::isValid]] to be false to cancel the rendering process. +- [[yii\base\View::EVENT_AFTER_RENDER|EVENT_AFTER_RENDER]]: triggered by the call of [[yii\base\View::beginPage()]] in layouts. + Handlers of this event may obtain the rendering result through [[yii\base\ViewEvent::output]] and may modify + this property to change the rendering result. +- [[yii\base\View::EVENT_BEGIN_PAGE|EVENT_BEGIN_PAGE]]: triggered by the call of [[yii\base\View::beginPage()]] in layouts. +- [[yii\base\View::EVENT_END_PAGE|EVENT_END_PAGE]]: triggered by the call of [[yii\base\View::endPage()]] in layouts. +- [[yii\web\View::EVENT_BEGIN_BODY|EVENT_BEGIN_BODY]]: triggered by the call of [[yii\web\View::beginBody()]] in layouts. +- [[yii\web\View::EVENT_END_BODY|EVENT_END_BODY]]: triggered by the call of [[yii\web\View::endBody()]] in layouts. + +For example, the following code injects the current date at the end of the page body: + +```php +\Yii::$app->view->on(View::EVENT_END_BODY, function () { + echo date('Y-m-d'); +}); +``` + + +## Rendering Static Pages + +Static pages refer to those Web pages whose main content are mostly static without the need of accessing +dynamic data pushed from controllers. + +You can output static pages by putting their code in the view, and then using the code like the following in a controller: + +```php +public function actionAbout() +{ + return $this->render('about'); +} +``` + +If a Web site contains many static pages, it would be very tedious repeating the similar code many times. +To solve this problem, you may introduce a [standalone action](structure-controllers.md#standalone-actions) +called [[yii\web\ViewAction]] in a controller. For example, + +```php +namespace app\controllers; + +use yii\web\Controller; + +class SiteController extends Controller +{ + public function actions() + { + return [ + 'page' => [ + 'class' => 'yii\web\ViewAction', + ], + ]; + } +} +``` + +Now if you create a view named `about` under the directory `@app/views/site/pages`, you will be able to +display this view by the following URL: + +``` +http://localhost/index.php?r=site/page&view=about +``` + +The `GET` parameter `view` tells [[yii\web\ViewAction]] which view is requested. The action will then look +for this view under the directory `@app/views/site/pages`. You may configure [[yii\web\ViewAction::viewPrefix]] +to change the directory for searching these views. + + +## Best Practices + +Views are responsible for presenting models in the format that end users desire. In general, views + +* should mainly contain presentational code, such as HTML, and simple PHP code to traverse, format and render data. +* should not contain code that performs DB queries. Such code should be done in models. +* should avoid direct access to request data, such as `$_GET`, `$_POST`. This belongs to controllers. + If request data is needed, they should be pushed into views by controllers. +* may read model properties, but should not modify them. + +To make views more manageable, avoid creating views that are too complex or contain too much redundant code. +You may use the following techniques to achieve this goal: + +* use [layouts](#layouts) to represent common presentational sections (e.g. page header, footer). +* divide a complicated view into several smaller ones. The smaller views can be rendered and assembled into a bigger + one using the rendering methods that we have described. +* create and use [widgets](structure-widgets.md) as building blocks of views. +* create and use helper classes to transform and format data in views. +