Qiang Xue
10 years ago
16 changed files with 2541 additions and 81 deletions
@ -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; |
||||
}, |
||||
], |
||||
] |
||||
``` |
||||
|
||||
> 补充:请谨慎注册太多应用组件,应用组件就像全局变量,使用太多可能加大测试和维护的难度。 |
||||
一般情况下可以在需要时再创建本地组件。 |
||||
|
||||
|
||||
## 引导启动组件 <a name="bootstrapping-components"></a> |
||||
|
||||
上面提到一个应用组件只会在第一次访问时实例化,如果处理请求过程没有访问的话就不实例化。 |
||||
有时你想在每个请求处理过程都实例化某个组件即便它不会被访问, |
||||
可以将该组件ID加入到应用主体的 [[yii\base\Application::bootstrap|bootstrap]] 属性中。 |
||||
|
||||
例如, 如下的应用主体配置保证了 `log` 组件一直被加载。 |
||||
|
||||
```php |
||||
[ |
||||
'bootstrap' => [ |
||||
// 将 log 组件 ID 加入引导让它始终载入 |
||||
'log', |
||||
], |
||||
'components' => [ |
||||
'log' => [ |
||||
// "log" 组件的配置 |
||||
], |
||||
], |
||||
] |
||||
``` |
||||
|
||||
|
||||
## 核心应用组件 <a name="core-application-components"></a> |
||||
|
||||
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) 一节。 |
@ -0,0 +1,524 @@
|
||||
应用主体 |
||||
============ |
||||
|
||||
应用主体是管理 Yii 应用系统整体结构和生命周期的对象。 |
||||
每个Yii应用系统只能包含一个应用主体,应用主体在 [入口脚本](structure-entry-scripts.md) 中创建并能通过表达式 `\Yii::$app` 全局范围内访问。 |
||||
|
||||
> 补充: 当我们说"一个应用",它可能是一个应用主体对象,也可能是一个应用系统,是根据上下文来决定[译:中文为避免歧义,Application翻译为应用主体]。 |
||||
|
||||
Yii有两种应用主体: [[yii\web\Application|网页应用主体]] and |
||||
[[yii\console\Application|控制台应用主体]], 如名称所示,前者主要处理网页请求,后者处理控制台请求。 |
||||
|
||||
|
||||
## 应用主体配置 <a name="application-configurations"></a> |
||||
|
||||
如下所示,当 [入口脚本](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) 当中。 |
||||
|
||||
## 应用主体属性 <a name="application-properties"></a> |
||||
|
||||
应用主体配置文件中有许多重要的属性要配置,这些属性指定应用主体的运行环境。 |
||||
比如,应用主体需要知道如何加载 [控制器](structure-controllers.md) ,临时文件保存到哪儿等等。 |
||||
以下我们简述这些属性。 |
||||
|
||||
### 必要属性 <a name="required-properties"></a> |
||||
|
||||
在一个应用中,至少要配置2个属性: [[yii\base\Application::id|id]] 和 [[yii\base\Application::basePath|basePath]]。 |
||||
|
||||
|
||||
#### [[yii\base\Application::id|id]] <a name="id"></a> |
||||
|
||||
[[yii\base\Application::id|id]] 属性用来区分其他应用的唯一标识ID。主要给程序使用。 |
||||
为了方便协作,最好使用数字作为应用主体ID,但不强制要求为数字。 |
||||
|
||||
|
||||
#### [[yii\base\Application::basePath|basePath]] <a name="basePath"></a> |
||||
|
||||
|
||||
[[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的路径)。 |
||||
|
||||
|
||||
### 重要属性 <a name="important-properties"></a> |
||||
|
||||
本小节所描述的属性通常需要设置,因为不用的应用属性不同。 |
||||
|
||||
|
||||
#### [[yii\base\Application::aliases|aliases]] <a name="aliases"></a> |
||||
|
||||
该属性允许你用一个数组定义多个 [别名](concept-aliases.md)。数组的key为别名名称,值为对应的路径。例如: |
||||
|
||||
```php |
||||
[ |
||||
'aliases' => [ |
||||
'@name1' => 'path/to/path1', |
||||
'@name2' => 'path/to/path2', |
||||
], |
||||
] |
||||
``` |
||||
|
||||
使用这个属性来定义别名,代替 [[Yii::setAlias()]] 方法来设置。 |
||||
|
||||
|
||||
#### [[yii\base\Application::bootstrap|bootstrap]] <a name="bootstrap"></a> |
||||
|
||||
这个属性很实用,它允许你用数组指定启动阶段[[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]] <a name="catchAll"></a> |
||||
|
||||
该属性仅 [[yii\web\Application|Web applications]] 网页应用支持。 |
||||
它指定一个要处理所有用户请求的 [控制器方法](structure-controllers.md),通常在维护模式下使用,同一个方法处理所有用户请求。 |
||||
|
||||
该配置为一个数组,第一项指定动作的路由,剩下的数组项(key-value 成对)指定传递给动作的参数,例如: |
||||
|
||||
```php |
||||
[ |
||||
'catchAll' => [ |
||||
'offline/notice', |
||||
'param1' => 'value1', |
||||
'param2' => 'value2', |
||||
], |
||||
] |
||||
``` |
||||
|
||||
|
||||
#### [[yii\base\Application::components|components]] <a name="components"></a> |
||||
|
||||
这是最重要的属性,它允许你注册多个在其他地方使用的[应用组件](#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]] <a name="controllerMap"></a> |
||||
|
||||
该属性允许你指定一个控制器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]] <a name="controllerNamespace"></a> |
||||
|
||||
该属性指定控制器类默认的命名空间,默认为`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]] <a name="language"></a> |
||||
|
||||
该属性指定应用展示给终端用户的语言,默认为 `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]] <a name="modules"></a> |
||||
|
||||
该属性指定应用所包含的 [模块](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]] <a name="name"></a> |
||||
|
||||
该属性指定你可能想展示给终端用户的应用名称,不同于需要唯一性的 [[yii\base\Application::id|id]] 属性, |
||||
该属性可以不唯一,该属性用于显示应用的用途。 |
||||
|
||||
如果其他地方的代码没有用到,可以不配置该属性。 |
||||
|
||||
|
||||
#### [[yii\base\Application::params|params]] <a name="params"></a> |
||||
|
||||
该属性为一个数组,指定可以全局访问的参数,代替程序中硬编码的数字和字符,应用中的参数定义到一个单独的文件并随时可以访问是一个好习惯。 |
||||
例如用参数定义缩略图的长宽如下: |
||||
|
||||
```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]] <a name="sourceLanguage"></a> |
||||
|
||||
该属性指定应用代码的语言,默认为 `'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]] <a name="timeZone"></a> |
||||
|
||||
该属性提供一种方式修改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]] <a name="version"></a> |
||||
|
||||
该属性指定应用的版本,默认为`'1.0'`,其他代码不使用的话可以不配置。 |
||||
|
||||
|
||||
### 实用属性 <a name="useful-properties"></a> |
||||
|
||||
本小节描述的属性不经常设置,通常使用系统默认值。如果你想改变默认值,可以配置这些属性。 |
||||
|
||||
|
||||
#### [[yii\base\Application::charset|charset]] <a name="charset"></a> |
||||
|
||||
该属性指定应用使用的字符集,默认值为 `'UTF-8'`,绝大部分应用都在使用,除非已有的系统大量使用非unicode数据才需要更改该属性。 |
||||
|
||||
|
||||
#### [[yii\base\Application::defaultRoute|defaultRoute]] <a name="defaultRoute"></a> |
||||
|
||||
该属性指定未配置的请求的响应 [路由](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]] <a name="extensions"></a> |
||||
|
||||
该属性用数组列表指定应用安装和使用的 [扩展](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]] <a name="layout"></a> |
||||
|
||||
该属性指定渲染 [视图](structure-views.md) 默认使用的布局名字,默认值为 `'main'` 对应[布局路径](#layoutPath)下的 `main.php` 文件, |
||||
如果 [布局路径](#layoutPath) 和 [视图路径](#viewPath) 都是默认值,默认布局文件可以使用路径别名`@app/views/layouts/main.php` |
||||
|
||||
如果不想设置默认布局文件,可以设置该属性为 `false`,这种做法比较罕见。 |
||||
|
||||
|
||||
#### [[yii\base\Application::layoutPath|layoutPath]] <a name="layoutPath"></a> |
||||
|
||||
该属性指定查找布局文件的路径,默认值为 [视图路径](#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]] <a name="runtimePath"></a> |
||||
|
||||
该属性指定临时文件如日志文件、缓存文件等保存路径,默认值为带别名的 `@app/runtime`。 |
||||
|
||||
可以配置该属性为一个目录或者路径 [别名](concept-aliases.md),注意应用运行时有对该路径的写入权限, |
||||
以及终端用户不能访问改路径因为临时文件可能包含一些敏感信息。 |
||||
|
||||
为了简化访问该路径,Yii预定义别名 `@runtime` 代表该路径。 |
||||
|
||||
|
||||
#### [[yii\base\Application::viewPath|viewPath]] <a name="viewPath"></a> |
||||
|
||||
该路径指定视图文件的根目录,默认值为带别名的 `@app/views`,可以配置它为一个目录或者路径 [别名](concept-aliases.md). |
||||
|
||||
|
||||
#### [[yii\base\Application::vendorPath|vendorPath]] <a name="vendorPath"></a> |
||||
|
||||
该属性指定 [Composer](http://getcomposer.org) 管理的供应商路径,该路径包含应用使用的包括Yii框架在内的所有第三方库。 |
||||
默认值为带别名的 `@app/vendor` 。 |
||||
|
||||
可以配置它为一个目录或者路径 [别名](concept-aliases.md),当你修改时,务必修改对应的 Composer 配置。 |
||||
|
||||
为了简化访问该路径,Yii预定义别名 `@vendor` 代表该路径。 |
||||
|
||||
|
||||
#### [[yii\console\Application::enableCoreCommands|enableCoreCommands]] <a name="enableCoreCommands"></a> |
||||
|
||||
该属性仅 [[yii\console\Application|console applications]] 控制台应用支持, 用来指定是否启用Yii中的核心命令,默认值为 `true`。 |
||||
|
||||
|
||||
## 应用事件 <a name="application-events"></a> |
||||
|
||||
应用在处理请求过程中会触发事件,可以在配置文件配置事件处理代码,如下所示: |
||||
|
||||
```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]] <a name="beforeRequest"></a> |
||||
|
||||
该事件在应用处理请求*before*之前,实际的事件名为 `beforeRequest`。 |
||||
|
||||
在事件触发前,应用主体已经实例化并配置好了,所以通过事件机制将你的代码嵌入到请求处理过程中非常不错。 |
||||
例如在事件处理中根据某些参数动态设置[[yii\base\Application::language]]语言属性。 |
||||
|
||||
|
||||
### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_AFTER_REQUEST]] <a name="afterRequest"></a> |
||||
|
||||
该事件在应用处理请求*after*之后但在返回响应*before*之前触发,实际的事件名为`afterRequest`。 |
||||
|
||||
该事件触发时,请求已经被处理完,可以做一些请求后处理或自定义响应。 |
||||
|
||||
注意 [[yii\web\Response|response]] 组件在发送响应给终端用户时也会触发一些事件,这些事件都在本事件*after*之后触发。 |
||||
|
||||
|
||||
### [[yii\base\Application::EVENT_BEFORE_REQUEST|EVENT_BEFORE_ACTION]] <a name="beforeAction"></a> |
||||
|
||||
该事件在每个 [控制器动作](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]] <a name="afterAction"></a> |
||||
|
||||
该事件在每个 [控制器动作](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` 相反,也就是说,控制器最先触发,然后是模块(如果有模块),最后为应用主体。 |
||||
|
||||
|
||||
## 应用主体生命周期 <a name="application-lifecycle"></a> |
||||
|
||||
当运行 [入口脚本](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. 入口脚本接收应用主体传来的退出状态并完成请求的处理。 |
@ -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 <a name="actions"></a> |
||||
|
||||
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 <a name="routes"></a> |
||||
|
||||
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 <a name="creating-controllers"></a> |
||||
|
||||
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 <a name="controller-ids"></a> |
||||
|
||||
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 <a name="controller-class-naming"></a> |
||||
|
||||
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 <a name="controller-map"></a> |
||||
|
||||
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 <a name="default-controller"></a> |
||||
|
||||
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 <a name="creating-actions"></a> |
||||
|
||||
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 <a name="action-ids"></a> |
||||
|
||||
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 <a name="inline-actions"></a> |
||||
|
||||
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 <a name="standalone-actions"></a> |
||||
|
||||
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 |
||||
<?php |
||||
namespace app\components; |
||||
|
||||
use yii\base\Action; |
||||
|
||||
class HelloWorldAction extends Action |
||||
{ |
||||
public function run() |
||||
{ |
||||
return "Hello World"; |
||||
} |
||||
} |
||||
``` |
||||
|
||||
|
||||
### Action Results <a name="action-results"></a> |
||||
|
||||
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 <a name="action-parameters"></a> |
||||
|
||||
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 <a name="default-action"></a> |
||||
|
||||
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 <a name="controller-lifecycle"></a> |
||||
|
||||
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 <a name="best-practices"></a> |
||||
|
||||
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). |
@ -0,0 +1,104 @@
|
||||
入口脚本 |
||||
============= |
||||
|
||||
入口脚本是应用启动流程中的第一环,一个应用(不管是网页应用还是控制台应用)只有一个入口脚本。终端用户的请求通过入口脚本实例化应用并将将请求转发到应用。 |
||||
|
||||
Web 应用的入口脚本必须放在终端用户能够访问的目录下,通常命名为 `index.php`,也可以使用 Web 服务器能定位到的其他名称。 |
||||
|
||||
控制台应用的入口脚本一般在应用根目录下命名为 `yii`(后缀为.php),该文件需要有执行权限,这样用户就能通过命令 `./yii <route> [arguments] [options]` 来运行控制台应用。 |
||||
|
||||
入口脚本主要完成以下工作: |
||||
|
||||
* 定义全局常量; |
||||
* 注册 [Composer 自动加载器](http://getcomposer.org/doc/01-basic-usage.md#autoloading); |
||||
* 包含 [[Yii]] 类文件; |
||||
* 加载应用配置; |
||||
* 创建一个[应用](structure-applications.md)实例并配置; |
||||
* 调用 [[yii\base\Application::run()]] 来处理请求。 |
||||
|
||||
|
||||
## Web 应用 <a name="web-applications"></a> |
||||
|
||||
以下是[基础应用模版](start-installation.md)入口脚本的代码: |
||||
|
||||
```php |
||||
<?php |
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
defined('YII_ENV') or define('YII_ENV', 'dev'); |
||||
|
||||
// 注册 Composer 自动加载器 |
||||
require(__DIR__ . '/../vendor/autoload.php'); |
||||
|
||||
// 包含 Yii 类文件 |
||||
require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php'); |
||||
|
||||
// 加载应用配置 |
||||
$config = require(__DIR__ . '/../config/web.php'); |
||||
|
||||
// 创建、配置、运行一个应用 |
||||
(new yii\web\Application($config))->run(); |
||||
``` |
||||
|
||||
|
||||
## 控制台应用 <a name="console-applications"></a> |
||||
|
||||
以下是一个控制台应用的入口脚本: |
||||
|
||||
```php |
||||
#!/usr/bin/env php |
||||
<?php |
||||
/** |
||||
* Yii console bootstrap file. |
||||
* |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright (c) 2008 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
defined('YII_DEBUG') or define('YII_DEBUG', true); |
||||
|
||||
// fcgi 默认没有定义 STDIN 和 STDOUT |
||||
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); |
||||
defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w')); |
||||
|
||||
// 注册 Composer 自动加载器 |
||||
require(__DIR__ . '/vendor/autoload.php'); |
||||
|
||||
// 包含 Yii 类文件 |
||||
require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php'); |
||||
|
||||
// 加载应用配置 |
||||
$config = require(__DIR__ . '/config/console.php'); |
||||
|
||||
$application = new yii\console\Application($config); |
||||
$exitCode = $application->run(); |
||||
exit($exitCode); |
||||
``` |
||||
|
||||
|
||||
## 定义常量 <a name="defining-constants"></a> |
||||
|
||||
入口脚本是定义全局常量的最好地方,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 文件时,常量就能生效。 |
@ -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 <a name="attributes"></a> |
||||
|
||||
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 <a name="defining-attributes"></a> |
||||
|
||||
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 <a name="attribute-labels"></a> |
||||
|
||||
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 name="scenarios"></a> |
||||
|
||||
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 <a name="validation-rules"></a> |
||||
|
||||
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 <a name="massive-assignment"></a> |
||||
|
||||
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 <a name="safe-attributes"></a> |
||||
|
||||
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 <a name="unsafe-attributes"></a> |
||||
|
||||
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 <a name="data-exporting"></a> |
||||
|
||||
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 name="fields"></a> |
||||
|
||||
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 <a name="best-practices"></a> |
||||
|
||||
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. |
@ -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) |
@ -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 <a name="creating-views"></a> |
||||
|
||||
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 |
||||
<?php |
||||
use yii\helpers\Html; |
||||
use yii\widgets\ActiveForm; |
||||
|
||||
/* @var $this yii\web\View */ |
||||
/* @var $form yii\widgets\ActiveForm */ |
||||
/* @var $model app\models\LoginForm */ |
||||
|
||||
$this->title = 'Login'; |
||||
?> |
||||
<h1><?= Html::encode($this->title) ?></h1> |
||||
|
||||
<p>Please fill out the following fields to login:</p> |
||||
|
||||
<?php $form = ActiveForm::begin(); ?> |
||||
<?= $form->field($model, 'username') ?> |
||||
<?= $form->field($model, 'password')->passwordInput() ?> |
||||
<?= Html::submitButton('Login') ?> |
||||
<?php ActiveForm::end(); ?> |
||||
``` |
||||
|
||||
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 <a name="security"></a> |
||||
|
||||
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 |
||||
<?php |
||||
use yii\helpers\Html; |
||||
?> |
||||
|
||||
<div class="username"> |
||||
<?= Html::encode($user->name) ?> |
||||
</div> |
||||
``` |
||||
|
||||
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 |
||||
<?php |
||||
use yii\helpers\HtmlPurifier; |
||||
?> |
||||
|
||||
<div class="post"> |
||||
<?= HtmlPurifier::process($post->text) ?> |
||||
</div> |
||||
``` |
||||
|
||||
> 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 <a name="organizing-views"></a> |
||||
|
||||
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 <a name="rendering-views"></a> |
||||
|
||||
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 <a name="rendering-in-controllers"></a> |
||||
|
||||
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 <a name="rendering-in-widgets"></a> |
||||
|
||||
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 <a name="rendering-in-views"></a> |
||||
|
||||
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 |
||||
<?= $this->render('_overview') ?> |
||||
``` |
||||
|
||||
|
||||
### Rendering in Other Places <a name="rendering-in-other-places"></a> |
||||
|
||||
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 <a name="named-views"></a> |
||||
|
||||
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 <a name="accessing-data-in-views"></a> |
||||
|
||||
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: <?= $this->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 <a name="sharing-data-among-views"></a> |
||||
|
||||
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 |
||||
<?= yii\widgets\Breadcrumbs::widget([ |
||||
'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], |
||||
]) ?> |
||||
``` |
||||
|
||||
|
||||
## Layouts <a name="layouts"></a> |
||||
|
||||
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 <a name="creating-layouts"></a> |
||||
|
||||
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 |
||||
<?php |
||||
use yii\helpers\Html; |
||||
|
||||
/* @var $this yii\web\View */ |
||||
/* @var $content string */ |
||||
?> |
||||
<?php $this->beginPage() ?> |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8"/> |
||||
<?= Html::csrfMetaTags() ?> |
||||
<title><?= Html::encode($this->title) ?></title> |
||||
<?php $this->head() ?> |
||||
</head> |
||||
<body> |
||||
<?php $this->beginBody() ?> |
||||
<header>My Company</header> |
||||
<?= $content ?> |
||||
<footer>© 2014 by My Company</footer> |
||||
<?php $this->endBody() ?> |
||||
</body> |
||||
</html> |
||||
<?php $this->endPage() ?> |
||||
``` |
||||
|
||||
As you can see, the layout generates the HTML tags that are common to all pages. Within the `<body>` 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 `<head>` 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 `<body>` 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 `<body>` 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 <a name="accessing-data-in-layouts"></a> |
||||
|
||||
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 <a name="using-layouts"></a> |
||||
|
||||
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 <a name="nested-layouts"></a> |
||||
|
||||
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 |
||||
<?php $this->beginContent('@app/views/layouts/base.php'); ?> |
||||
|
||||
...child layout content here... |
||||
|
||||
<?php $this->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 <a name="using-blocks"></a> |
||||
|
||||
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 |
||||
... |
||||
|
||||
<?php $this->beginBlock('block1'); ?> |
||||
|
||||
...content of block1... |
||||
|
||||
<?php $this->endBlock(); ?> |
||||
|
||||
... |
||||
|
||||
<?php $this->beginBlock('block3'); ?> |
||||
|
||||
...content of block3... |
||||
|
||||
<?php $this->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 |
||||
... |
||||
<?php if (isset($this->blocks['block1'])): ?> |
||||
<?= $this->blocks['block1'] ?> |
||||
<?php else: ?> |
||||
... default content for block1 ... |
||||
<?php endif; ?> |
||||
|
||||
... |
||||
|
||||
<?php if (isset($this->blocks['block2'])): ?> |
||||
<?= $this->blocks['block2'] ?> |
||||
<?php else: ?> |
||||
... default content for block2 ... |
||||
<?php endif; ?> |
||||
|
||||
... |
||||
|
||||
<?php if (isset($this->blocks['block3'])): ?> |
||||
<?= $this->blocks['block3'] ?> |
||||
<?php else: ?> |
||||
... default content for block3 ... |
||||
<?php endif; ?> |
||||
... |
||||
``` |
||||
|
||||
|
||||
## Using View Components <a name="using-view-components"></a> |
||||
|
||||
[[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 <a name="setting-page-titles"></a> |
||||
|
||||
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 |
||||
<?php |
||||
$this->title = 'My page title'; |
||||
?> |
||||
``` |
||||
|
||||
Then in the layout, make sure you have the following code in the `<head>` section: |
||||
|
||||
```php |
||||
<title><?= Html::encode($this->title) ?></title> |
||||
``` |
||||
|
||||
|
||||
### Registering Meta Tags <a name="registering-meta-tags"></a> |
||||
|
||||
Web pages usually need to generate various meta tags needed by different parties. Like page titles, meta tags |
||||
appear in the `<head>` 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 |
||||
<?php |
||||
$this->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 |
||||
<meta name="keywords" content="yii, framework, 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 <a name="registering-link-tags"></a> |
||||
|
||||
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 |
||||
<link title="Live News for Yii" rel="alternate" type="application/rss+xml" href="http://www.yiiframework.com/rss.xml/"> |
||||
``` |
||||
|
||||
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 <a name="view-events"></a> |
||||
|
||||
[[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 <a name="rendering-static-pages"></a> |
||||
|
||||
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 <a name="best-practices"></a> |
||||
|
||||
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. |
||||
|
Loading…
Reference in new issue