paulwalko.github.io/_site/notes/learning-note-YII(1.1).html

1973 lines
177 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Yii 是一个基于组件的高性能 PHP 框架, 用于快速开发大型 Web 应用. 它使 Web 开发中的 可复用度最大化, 可以显著提高你的 Web 应用开发速度. Yii 这个名字(读作易(Yee) 或 [ji:])代表 简单(easy), 高效(efficient) 和 可扩展(extensible).">
<title>YII(版本1) 权威指南学习笔记(未完结)</title>
<!-- favicon -->
<link rel="shortcut icon" href="http://localhost:4000/favicon.ico" type="image/icon">
<link rel="icon" href="http://localhost:4000/favicon.ico" type="image/icon">
<link rel="stylesheet" type="text/css" href="http://localhost:4000/stylesheets/base.css">
<link rel="stylesheet" type="text/css" href="http://localhost:4000/stylesheets/simplePagination.css">
<script type="text/javascript" src="http://localhost:4000/javascripts/jquery.js"></script>
<!--[if lt IE 9]>
<script src="http://localhost:4000/javascripts/html5shiv.js"></script>
<![endif]-->
<link rel="stylesheet" type="text/css" href="http://localhost:4000/stylesheets/markdownreader.css">
<link rel="stylesheet" type="text/css" href="http://localhost:4000/stylesheets/pygments_monokai.css">
<link rel="stylesheet" type="text/css" href="http://localhost:4000/stylesheets/code_block.css">
</head>
<body>
<header id="l-header">
<div class="container">
<div class="row logo">
<div class="col-lg-7">
<h1>Paul Walko</h1>
</div>
</div>
<div class="row navicon">
<a href=""><i class="fa fa-navicon"></i></a>
</div>
<div class="row navbar">
<nav class="col-lg-8 col-md-8 col-xs-12">
<ul class="row">
<li class="col-lg-3"><a href="http://localhost:4000/">HOME</a></li>
<li class="col-lg-3">
<ul class="subnav">
<a href="javascript:void(0)">POST</a>
<li><a href="http://localhost:4000/category">CATEGORY</a></li>
<li><a href="http://localhost:4000/tag">TAG</a></li>
</ul>
</li>
<li class="col-lg-3"><a href="http://localhost:4000/about">ABOUT</a></li>
<li class="col-lg-3"><a href="http://localhost:4000/building">PLAY GROUND</a></li>
</ul>
</nav>
<div class="search col-lg-4 col-md-4 col-xs-12">
<form id="dummySearch">
<label for="search"></label>
<input id="search" name="serach" type="text" placeholder="Not That Dummy Search">
<i class="fa fa-search"></i>
</form>
<script>
$(function(){
$('#dummySearch').submit(function(e) {
e.preventDefault();
keyword = $('#search').val();
url = 'https://www.google.com.hk/search?q=site%3Aunifreak.github.io+' + keyword;
location.href = url;
})
})
</script>
</div>
</div>
</div>
</header>
<div class="container">
<div class="row">
<div id="markdown-container" class="col-lg-9">
<header>
<p id="postTitle">YII(版本1) 权威指南学习笔记(未完结)</p>
<ul class="tags clearfix">
<li><i class="fa fa-tag"></i> php</li>
<li><i class="fa fa-tag"></i> framework</li>
</ul>
<p id="postMeta">posted on 19 Nov 2015 under category <a href="http://localhost:4000/category/">notes</a></p>
</header>
<h1 id=">基础</h1>
<h2 id=">入口脚本</h2>
<p>WEB 应用一般为 <code>index.php</code>, 控制台应用一般为 <code>yii.php</code> 并在文件开头加上 <code>#! /usr/bin/env php</code></p>
<p>入口脚本是定义全局常量的好地方</p>
<p>支持三个常量: <code>YII_DEBUG</code>, <code>YII_ENV</code>, <code>YII_ENABLE_ERROR_HANDLER</code></p>
<p>WEB:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 定义全局常量</span>
<span class="nb">defined</span><span class="p">(</span><span class="s1">&#39;YII_DEBUG&#39;</span><span class="p">)</span> <span class="k">or</span> <span class="nb">define</span><span class="p">(</span><span class="s1">&#39;YII_DEBUG&#39;</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
<span class="nb">defined</span><span class="p">(</span><span class="s1">&#39;YII_ENV&#39;</span><span class="p">)</span> <span class="k">or</span> <span class="nb">define</span><span class="p">(</span><span class="s1">&#39;YII_ENV&#39;</span><span class="p">,</span> <span class="s1">&#39;dev&#39;</span><span class="p">);</span>
<span class="c1">// 注册 Composer 自动加载器</span>
<span class="k">require</span><span class="p">(</span><span class="nx">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/../vendor/autoload.php&#39;</span><span class="p">);</span>
<span class="c1">// 包含 Yii 类文件</span>
<span class="k">require</span><span class="p">(</span><span class="nx">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/../vendor/yiisoft/yii2/Yii.php&#39;</span><span class="p">);</span>
<span class="c1">// 加载应用配置</span>
<span class="nv">$config</span> <span class="o">=</span> <span class="k">require</span><span class="p">(</span><span class="nx">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/../config/web.php&#39;</span><span class="p">);</span>
<span class="c1">// 创建、配置、运行一个应用</span>
<span class="p">(</span><span class="k">new</span> <span class="nx">yii\web\Application</span><span class="p">(</span><span class="nv">$config</span><span class="p">))</span><span class="o">-&gt;</span><span class="na">run</span><span class="p">();</span>
</code></pre></div>
<p>控制台:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="x">#!/usr/bin/env php</span>
<span class="cp">&lt;?php</span>
<span class="nb">defined</span><span class="p">(</span><span class="s1">&#39;YII_DEBUG&#39;</span><span class="p">)</span> <span class="k">or</span> <span class="nb">define</span><span class="p">(</span><span class="s1">&#39;YII_DEBUG&#39;</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span>
<span class="c1">// fcgi 默认没有定义 STDIN 和 STDOUT</span>
<span class="nb">defined</span><span class="p">(</span><span class="s1">&#39;STDIN&#39;</span><span class="p">)</span> <span class="k">or</span> <span class="nb">define</span><span class="p">(</span><span class="s1">&#39;STDIN&#39;</span><span class="p">,</span> <span class="nb">fopen</span><span class="p">(</span><span class="s1">&#39;php://stdin&#39;</span><span class="p">,</span> <span class="s1">&#39;r&#39;</span><span class="p">));</span>
<span class="nb">defined</span><span class="p">(</span><span class="s1">&#39;STDOUT&#39;</span><span class="p">)</span> <span class="k">or</span> <span class="nb">define</span><span class="p">(</span><span class="s1">&#39;STDOUT&#39;</span><span class="p">,</span> <span class="nb">fopen</span><span class="p">(</span><span class="s1">&#39;php://stdout&#39;</span><span class="p">,</span> <span class="s1">&#39;w&#39;</span><span class="p">));</span>
<span class="c1">// 注册 Composer 自动加载器</span>
<span class="k">require</span><span class="p">(</span><span class="nx">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/vendor/autoload.php&#39;</span><span class="p">);</span>
<span class="c1">// 包含 Yii 类文件</span>
<span class="k">require</span><span class="p">(</span><span class="nx">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/vendor/yiisoft/yii2/Yii.php&#39;</span><span class="p">);</span>
<span class="c1">// 加载应用配置</span>
<span class="nv">$config</span> <span class="o">=</span> <span class="k">require</span><span class="p">(</span><span class="nx">__DIR__</span> <span class="o">.</span> <span class="s1">&#39;/config/console.php&#39;</span><span class="p">);</span>
<span class="nv">$application</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">yii\console\Application</span><span class="p">(</span><span class="nv">$config</span><span class="p">);</span>
<span class="nv">$exitCode</span> <span class="o">=</span> <span class="nv">$application</span><span class="o">-&gt;</span><span class="na">run</span><span class="p">();</span>
<span class="k">exit</span><span class="p">(</span><span class="nv">$exitCode</span><span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id=">应用主体</h2>
<p>创建: <code>Yii::createWebApplication($configFile)</code></p>
<p>访问: 可以在任何地方使用 <code>Yii::app()|YiiBase::app</code> 访问</p>
<h2 id=">控制器</h2>
<p>控制器路由格式: <code>moduleID/controllerID/actionID</code></p>
<p>控制器创建决策步骤:</p>
<ol>
<li>如果指定了 <code>CWebApplication::catchAllRequest</code>, 用户指定的 ID 将被忽略. (通常用于设置应用为维护状态, 显示一个静态页面)</li>
<li>如果在 <code>CWebApplication::controllerMap</code> 中找到 ID, 相应的控制器配置则被用于创建控制器</li>
<li>如果 ID 为 <code>path/to/xyz</code> 形式, 则按控制器路由格式解析并创建</li>
</ol>
<p>创建:</p>
<ul>
<li><p>默认控制器在 <code>CWebApplication::defaultController</code> 中定义, 默认动作为 <code>index</code>, 对应的方法名为 <code>actionIndex</code>, 可通过 <code>CController::defaultAction</code> 修改</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">SiteController</span> <span class="k">extends</span> <span class="nx">CController</span> <span class="p">{</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>也可以由一个动作类来定义动作, 以便重用动作:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">UpdateAction</span> <span class="k">extends</span> <span class="nx">CAction</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// place the action logic here</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>然后需覆盖控制器类的 <code>actions</code> 方法:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">PostController</span> <span class="k">extends</span> <span class="nx">CController</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">actions</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;edit&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;application.controllers.post.UpdateAction&#39;</span><span class="p">,</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
</ul>
<p>动作参数绑定:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// in PostController:</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">actionCreate</span><span class="p">(</span><span class="k">array</span> <span class="nv">$category</span><span class="p">,</span> <span class="nv">$language</span> <span class="o">=</span> <span class="s1">&#39;en&#39;</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 动作参数绑定功能将会把传入 action 的参数和 $_GET 中的数据绑定</span>
<span class="c1">// 在此, 如果 $_GET 中没有 language 这一项, $language 默认为 &#39;en&#39;</span>
<span class="c1">// 因为没有为 $category 提供默认值, 如果 $_GET 中没有 category 这一项则会报错</span>
<span class="c1">// array 类型声明会确保 $category 为一个数组(自动将基本类型转换为数组)</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>过滤器可被配置在动作执行之前或之后执行, 如访问控制过滤器, 性能过滤器(参见<a href="#accessControlFilter">访问控制过滤器</a>)</p>
<ul>
<li><p>定义:</p>
<ul>
<li>可被定义为一个 <code>filter</code> 前缀的控制器方法:</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">filterAccessControl</span><span class="p">(</span><span class="nv">$filterChain</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 调用 $filterChain-&gt;run() 以继续后续过滤器与动作的执行。</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<ul>
<li>也可是一个 <code>CFilter</code> 或其子类的实例:</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">PerformanceFilter</span> <span class="k">extends</span> <span class="nx">CFilter</span> <span class="p">{</span>
<span class="k">protected</span> <span class="k">function</span> <span class="nf">preFilter</span><span class="p">(</span><span class="nv">$filterChain</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 动作被执行之前应用的逻辑</span>
<span class="k">return</span> <span class="k">true</span><span class="p">;</span> <span class="c1">// 如果动作不应被执行,此处返回 false</span>
<span class="p">}</span>
<span class="k">protected</span> <span class="k">function</span> <span class="nf">postFilter</span><span class="p">(</span><span class="nv">$filterChain</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// 动作执行之后应用的逻辑</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>配置使用: 需要覆盖控制器的 <code>filter()</code> 方法:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">PostController</span> <span class="k">extends</span> <span class="nx">CController</span> <span class="p">{</span>
<span class="o">......</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">filters</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;postOnly + edit, create&#39;</span><span class="p">,</span> <span class="c1">// 使用 filter 前缀方法定义的过滤器</span>
<span class="k">array</span><span class="p">(</span> <span class="c1">// 使用类定义的过滤器</span>
<span class="s1">&#39;application.filters.PerformanceFilter - edit, create&#39;</span><span class="p">,</span>
<span class="s1">&#39;unit&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;second&#39;</span><span class="p">,</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
</ul>
<h2 id=">模型</h2>
<p>Yii 实现了两种类型的模型: 表单模型和 Active Record, 二者都继承于 CModel</p>
<p>如果用户输入被收集然后丢弃, 应该创建一个表单模型; 如果用户输入被收集后要保存到数据库, 应使用一个 Active Record</p>
<p>详见<a href="#FormModel">表单模型</a><a href="#ActiveRecord">Active Record</a></p>
<h2 id=">视图</h2>
<p><code>CController::render(&#39;edit&#39;)</code> 将会在 <code>protected/views/ControllerID</code> 目录下寻找 <code>edit.php</code> 视图文件</p>
<p>可以通过在视图中使用 <code>$this-&gt;propertyName</code> 访问控制器的任何属性, 也可以在控制器中将数据传递到视图中:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">render</span><span class="p">(</span><span class="s1">&#39;edit&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;var1&#39;</span><span class="o">=&gt;</span><span class="nv">$value1</span><span class="p">,</span>
<span class="s1">&#39;var2&#39;</span><span class="o">=&gt;</span><span class="nv">$value2</span><span class="p">,</span>
<span class="p">));</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p><code>protected/views/layouts/main.php</code> 是默认的<code>布局</code>文件, 可通过 <code>CWebApplication::layout</code> 自定义. 要渲染一个不带布局的视图, 需调用 <code>CController::renderPartial</code></p>
<p><code>小物件</code><code>CWidget</code> 或其子类的实例, 它也可以有自己的视图文件</p>
<ul>
<li><p>定义</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">MyWidget</span> <span class="k">extends</span> <span class="nx">CWidget</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 此方法会被 CController::beginWidget() 调用</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">run</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 此方法会被 CController::endWidget() 调用</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>按如下视图脚本来使用一个小物件:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">beginWidget</span><span class="p">(</span><span class="s1">&#39;path.to.WidgetClass&#39;</span><span class="p">,</span> <span class="nv">$config</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">...可能会由小物件获取的内容主体...</span>
<span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">endWidget</span><span class="p">();</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">// 或</span>
<span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">widget</span><span class="p">(</span><span class="s1">&#39;path.to.WidgetClass&#39;</span><span class="p">,</span> <span class="nv">$config</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
</ul>
<p>系统视图用于展示 Yii 的错误和日志消息, 如如果 CHttpException 抛出一个 404 错误, 那么 <code>error404</code> 就会被展示. Yii 在 <code>framework/views</code> 下提供了默认的系统视图, 也可以通过在 <code>protected/views/system</code> 下创建同名视图文件进行自定义</p>
<h2 id=">组件</h2>
<p>加载: 通过配置应用的 <code>components|CApplication::components</code> 属性</p>
<ul>
<li>可以配置 <code>enabled</code> 为 <code>false</code> 禁用组件</li>
<li>组件是按需创建的, 但是可以将组件 ID 列入应用的 <code>preload|CWebApplication::preload</code> 属性中强制其加载</li>
</ul>
<p>访问: <code>Yii::app()-&gt;ComponentID</code></p>
<p>预定义的核心应用组件:</p>
<ul>
<li>assetManager</li>
<li>authManager</li>
<li>cache</li>
<li>clientScript</li>
<li>coreMessage</li>
<li>db</li>
<li>errorHandler</li>
<li>format</li>
<li>messages</li>
<li>request</li>
<li>securityManager</li>
<li>session</li>
<li>statePersister</li>
<li>urlManager</li>
<li>user</li>
<li>themeManager</li>
</ul>
<p>组件属性</p>
<ul>
<li>可以通过直接定义一个公共成员变量定义</li>
<li>也可以使用 getter 和 setter 更灵活的定义</li>
<li>通过 getter 和 setter 定义的属性和类成员变量有个区别: 他们是不区分大小写的</li>
</ul>
<p>组件事件</p>
<ol>
<li><p>定义组件事件(<code>on</code> 开头)</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">onClicked</span><span class="p">(</span><span class="nv">$event</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">raiseEvent</span><span class="p">(</span><span class="s1">&#39;onClicked&#39;</span><span class="p">,</span> <span class="nv">$event</span><span class="p">);</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>定义事件回调</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">function</span> <span class="nf">callbackName</span><span class="p">(</span><span class="nv">$event</span><span class="p">)</span> <span class="p">{</span>
<span class="o">......</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>绑定事件回调</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$component</span><span class="o">-&gt;</span><span class="na">onClicked</span><span class="o">=</span><span class="nv">$callback</span><span class="p">;</span>
<span class="c1">// 或使用匿名函数</span>
<span class="nv">$component</span><span class="o">-&gt;</span><span class="na">onclicked</span><span class="o">=</span><span class="k">function</span><span class="p">(</span><span class="nv">$event</span><span class="p">)</span> <span class="p">{</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
</ol>
<p>组件行为</p>
<ul>
<li><p>行为类必须实现 <code>IBehavior</code></p></li>
<li><p>大多数行为可继承自 <code>CBehavior</code>, 如果行为需要绑定到模型, 则也可以继承自 <code>CModelBehavior</code><code>CActiveRecordBehavior</code></p></li>
<li><p>两个同名行为绑定到同一个组件下是有可能的, 在这种情况下, 先绑定的行为则拥有优先权</p></li>
<li><p>当和 events, 一起使用时, 行为会更加强大. 当行为被绑定到组件时,行为里的一些方法就可以绑定到组件的一些事件上了. 这样一来,行为就有机观察或者改变组件的常规执行流程</p></li>
<li><p>一个行为的属性也可以通过绑定到的组件来访问. 这些属性包含公共成员变量以及通过 getters 和/或 setters 方式设置的属性. 例如, 若一个行为有一个 <code>xyz</code> 的属性,此行为被绑定到组件 <code>$a</code>, 然后我们可以使用表达式 <code>$a-&gt;xyz</code> 访问此行为的属性</p></li>
<li><p>绑定行为:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// $name 在组件中实现了对行为的唯一识别</span>
<span class="nv">$component</span><span class="o">-&gt;</span><span class="na">attachBehavior</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span><span class="nv">$behavior</span><span class="p">);</span>
<span class="c1">// test() 是行为中的方法。</span>
<span class="nv">$component</span><span class="o">-&gt;</span><span class="na">test</span><span class="p">();</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>访问行为:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$behavior</span><span class="o">=</span><span class="nv">$component</span><span class="o">-&gt;</span><span class="na">tree</span><span class="p">;</span>
<span class="c1">// 等于下行代码:</span>
<span class="c1">// $behavior=$component-&gt;asa(&#39;tree&#39;);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>禁用行为:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$component</span><span class="o">-&gt;</span><span class="na">disableBehavior</span><span class="p">(</span><span class="nv">$name</span><span class="p">);</span>
<span class="c1">// 下面的代码将抛出一个异常</span>
<span class="nv">$component</span><span class="o">-&gt;</span><span class="na">test</span><span class="p">();</span>
<span class="nv">$component</span><span class="o">-&gt;</span><span class="na">enableBehavior</span><span class="p">(</span><span class="nv">$name</span><span class="p">);</span>
<span class="c1">// 现在就可以使用了</span>
<span class="nv">$component</span><span class="o">-&gt;</span><span class="na">test</span><span class="p">();</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
</ul>
<h2 id=">模块</h2>
<p>模块是一个独立的软件单元,它包含 模型, 视图, 控制器 和其他支持的组件. 如 <code>forum</code> 模块的典型目录结构</p>
<div class="highlight"><pre><code class="language-text" data-lang="text">forum/
ForumModule.php 模块类文件
components/ 包含可复用的用户组件
views/ 包含小物件的视图文件
controllers/ 包含控制器类文件
DefaultController.php 默认的控制器类文件
extensions/ 包含第三方扩展
models/ 包含模块类文件
views/ 包含控制器视图和布局文件
layouts/ 包含布局文件
default/ 包含 DefaultController 的视图文件
index.php 首页视图文件
</code></pre></div>
<p>模块可以嵌套</p>
<p>使用模块:</p>
<ol>
<li><p>继承 CWebModule, 并命名为 ucfirst($id).&#39;Module&#39;</p></li>
<li><p>将模块目录放入 <code>modules</code> 目录中, 然后在应用的 <code>modules</code> 配置 属性中声明模块 ID. 模块也可以在配置是带有初始属性值</p></li>
<li><p>使用 CController::module 访问</p></li>
</ol>
<h2 id=">路径别名和名字空间</h2>
<p><code>YiiBase::getPathOfAlias()</code> 获取别名的真实路径, <code>YiiBase::setPathOfAlias()</code> 设置新的别名的真实路径</p>
<p>预定义的根别名</p>
<ul>
<li>system</li>
<li>zii</li>
<li>application</li>
<li>webroot</li>
<li>ext</li>
<li>模块 ID</li>
</ul>
<p>使用别名导入类: <code>Yii::import(&#39;system.web.CController&#39;)</code>. (所有核心类已被预先导入)</p>
<p>预导入: 在 <code>CWebApplication::run()</code> 之前执行:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nx">Yii</span><span class="o">::</span><span class="nv">$classMap</span><span class="o">=</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;ClassName1&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;path/to/ClassName1.php&#39;</span><span class="p">,</span>
<span class="s1">&#39;ClassName2&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;path/to/ClassName2.php&#39;</span><span class="p">,</span>
<span class="o">......</span>
<span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>导入目录: <code>Yii::import(&#39;system.web.*&#39;);</code></p>
<h1 id="><a name="FormModel">使用表单</a></h1>
<h2 id=">创建模型</h2>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">LoginForm</span> <span class="k">extends</span> <span class="nx">CFormModel</span> <span class="p">{</span>
<span class="c1">// 定义特性(我们把用于存储用户输入或数据库数据的属性成为特性(attribute))</span>
<span class="k">public</span> <span class="nv">$username</span><span class="p">;</span>
<span class="k">public</span> <span class="nv">$password</span><span class="p">;</span>
<span class="k">public</span> <span class="nv">$rememberMe</span><span class="o">=</span><span class="k">false</span><span class="p">;</span>
<span class="k">private</span> <span class="nv">$_identity</span><span class="p">;</span>
<span class="c1">// 验证规则</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">rules</span><span class="p">()</span> <span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * 每个验证规则的格式为: </span>
<span class="sd"> * array(&#39;AttributeList&#39;, &#39;Validator&#39;, &#39;on&#39;=&gt;&#39;ScenarioList&#39;, ...附加选项)</span>
<span class="sd"> * 有三种方法指定 Validator</span>
<span class="sd"> * 1. 指定为模型类中的一个方法, 该方法定义格式为</span>
<span class="sd"> * public function ValidatiorName($attributes, $params) {...}</span>
<span class="sd"> * 2. 一个继承自 CValidator 的验证器类, 此时附加选项用于初始化实例的属性值</span>
<span class="sd"> * 3. 一个预定义的验证器类的别名, 以下是完整列表</span>
<span class="sd"> * boolean, captcha, compare, email, default, exists, file</span>
<span class="sd"> * filter, in, length, match, numerical, required, type, unique, url</span>
<span class="sd"> */</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;username, password&#39;</span><span class="p">,</span> <span class="s1">&#39;required&#39;</span><span class="p">),</span> <span class="c1">// 必填</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;rememberMe&#39;</span><span class="p">,</span> <span class="s1">&#39;boolean&#39;</span><span class="p">),</span> <span class="c1">// 布尔</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;password&#39;</span><span class="p">,</span> <span class="s1">&#39;authenticate&#39;</span><span class="p">),</span> <span class="c1">// 需验证</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="sd">/**</span>
<span class="sd"> * authenticate Validator</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">authenticate</span><span class="p">(</span><span class="nv">$attribute</span><span class="p">,</span><span class="nv">$params</span><span class="p">)</span>
<span class="p">{</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_identity</span><span class="o">=</span><span class="k">new</span> <span class="nx">UserIdentity</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">username</span><span class="p">,</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">password</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_identity</span><span class="o">-&gt;</span><span class="na">authenticate</span><span class="p">())</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">addError</span><span class="p">(</span><span class="s1">&#39;password&#39;</span><span class="p">,</span><span class="s1">&#39;错误的用户名或密码。&#39;</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p><a name="massiveAssign">块赋值(massive assignment)</a></p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$model</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">LoginForm</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;LoginForm&#39;</span><span class="p">]))</span> <span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * 只有被认为 &#39;安全&#39; 的特性才会被赋值</span>
<span class="sd"> * 特性如果出现在相应场景的一个验证规则中, 即被认为是安全的</span>
<span class="sd"> * 也可以用特殊的 `safe` Validator 来让特性变为安全的</span>
<span class="sd"> * </span>
<span class="sd"> * 为了使块赋值正确工作, 对应于模型类 `C` 中的特性 `a` 的表单域, 请命名其为 `C[a]`</span>
<span class="sd"> */</span>
<span class="nv">$model</span><span class="o">-&gt;</span><span class="na">attributes</span> <span class="o">=</span> <span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;LoginForm&#39;</span><span class="p">];</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p><a name="triggerValidation">触发验证</a></p>
<ul>
<li>验证是基于 <code>场景(scenario)</code></li>
<li>可以手动调用 <code>CModel::validate()</code> 触发; 对于 <code>CActiveRecord</code>, 会在 <code>CAcitveRecord::save()</code> 时自动触发验证</li>
<li>验证错误可以使用 <code>CModel::getError()</code><code>CModel::getErrors()</code> 获取</li>
</ul>
<p>标签</p>
<ul>
<li><code>CModel</code> 默认将返回特性的名字作为其标签</li>
<li>也可使用 <code>CModel::attributesLabels</code> 方法自定义标签</li>
</ul>
<h2 id=">创建动作</h2>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">actionLogin</span><span class="p">()</span> <span class="p">{</span>
<span class="nv">$model</span><span class="o">=</span><span class="k">new</span> <span class="nx">LoginForm</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;LoginForm&#39;</span><span class="p">]))</span> <span class="p">{</span>
<span class="c1">// 收集用户输入的数据</span>
<span class="nv">$model</span><span class="o">-&gt;</span><span class="na">attributes</span><span class="o">=</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;LoginForm&#39;</span><span class="p">];</span>
<span class="c1">// 验证用户输入,并在判断输入正确后重定向到前一页</span>
<span class="k">if</span><span class="p">(</span><span class="nv">$model</span><span class="o">-&gt;</span><span class="na">validate</span><span class="p">())</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">redirect</span><span class="p">(</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">user</span><span class="o">-&gt;</span><span class="na">returnUrl</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// 显示登录表单</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">render</span><span class="p">(</span><span class="s1">&#39;login&#39;</span><span class="p">,</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;model&#39;</span><span class="o">=&gt;</span><span class="nv">$model</span><span class="p">));</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id=">创建视图</h2>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="x">&lt;div class=&quot;form&quot;&gt;</span>
<span class="cp">&lt;?php</span> <span class="nv">$form</span><span class="o">=</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">beginWidget</span><span class="p">(</span><span class="s1">&#39;CActiveForm&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">errorSummary</span><span class="p">(</span><span class="nv">$model</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> &lt;div class=&quot;row&quot;&gt;</span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">label</span><span class="p">(</span><span class="nv">$model</span><span class="p">,</span><span class="s1">&#39;username&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">textField</span><span class="p">(</span><span class="nv">$model</span><span class="p">,</span><span class="s1">&#39;username&#39;</span><span class="p">)</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> &lt;/div&gt;</span>
<span class="x"> &lt;div class=&quot;row&quot;&gt;</span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">label</span><span class="p">(</span><span class="nv">$model</span><span class="p">,</span><span class="s1">&#39;password&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">passwordField</span><span class="p">(</span><span class="nv">$model</span><span class="p">,</span><span class="s1">&#39;password&#39;</span><span class="p">)</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> &lt;/div&gt;</span>
<span class="x"> &lt;div class=&quot;row rememberMe&quot;&gt;</span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">checkBox</span><span class="p">(</span><span class="nv">$model</span><span class="p">,</span><span class="s1">&#39;rememberMe&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">label</span><span class="p">(</span><span class="nv">$model</span><span class="p">,</span><span class="s1">&#39;rememberMe&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> &lt;/div&gt;</span>
<span class="x"> &lt;div class=&quot;row submit&quot;&gt;</span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nx">CHtml</span><span class="o">::</span><span class="na">submitButton</span><span class="p">(</span><span class="s1">&#39;Login&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> &lt;/div&gt;</span>
<span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">endWidget</span><span class="p">();</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">&lt;/div&gt;&lt;!-- form --&gt;</span>
</code></pre></div>
<h2 id=">收集表格输入(批量)</h2>
<p>action:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">actionBatchUpdate</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// 假设每一项item是一个 &#39;Item&#39; 类的实例,</span>
<span class="c1">// 提取要通过批量模式更新的项</span>
<span class="nv">$items</span><span class="o">=</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">getItemsToUpdate</span><span class="p">();</span>
<span class="k">if</span><span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;Item&#39;</span><span class="p">]))</span>
<span class="p">{</span>
<span class="nv">$valid</span><span class="o">=</span><span class="k">true</span><span class="p">;</span>
<span class="k">foreach</span><span class="p">(</span><span class="nv">$items</span> <span class="k">as</span> <span class="nv">$i</span><span class="o">=&gt;</span><span class="nv">$item</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;Item&#39;</span><span class="p">][</span><span class="nv">$i</span><span class="p">]))</span>
<span class="nv">$item</span><span class="o">-&gt;</span><span class="na">attributes</span><span class="o">=</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;Item&#39;</span><span class="p">][</span><span class="nv">$i</span><span class="p">];</span>
<span class="nv">$valid</span><span class="o">=</span><span class="nv">$valid</span> <span class="o">&amp;&amp;</span> <span class="nv">$item</span><span class="o">-&gt;</span><span class="na">validate</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">if</span><span class="p">(</span><span class="nv">$valid</span><span class="p">)</span> <span class="c1">// 如果所有项目有效</span>
<span class="c1">// ...则在此处做一些操作</span>
<span class="p">}</span>
<span class="c1">// 显示视图收集表格输入</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">render</span><span class="p">(</span><span class="s1">&#39;batchUpdate&#39;</span><span class="p">,</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;items&#39;</span><span class="o">=&gt;</span><span class="nv">$items</span><span class="p">));</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>view:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="x">&lt;div class=&quot;form&quot;&gt;</span>
<span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nx">CHtml</span><span class="o">::</span><span class="na">beginForm</span><span class="p">();</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">&lt;table&gt;</span>
<span class="x">&lt;tr&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Price&lt;/th&gt;&lt;th&gt;Count&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;</span>
<span class="cp">&lt;?php</span> <span class="k">foreach</span><span class="p">(</span><span class="nv">$items</span> <span class="k">as</span> <span class="nv">$i</span><span class="o">=&gt;</span><span class="nv">$item</span><span class="p">)</span><span class="o">:</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">&lt;tr&gt;</span>
<span class="x">&lt;td&gt;</span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nx">CHtml</span><span class="o">::</span><span class="na">activeTextField</span><span class="p">(</span><span class="nv">$item</span><span class="p">,</span><span class="s2">&quot;[</span><span class="si">$i</span><span class="s2">]name&quot;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x">&lt;/td&gt;</span>
<span class="x">&lt;td&gt;</span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nx">CHtml</span><span class="o">::</span><span class="na">activeTextField</span><span class="p">(</span><span class="nv">$item</span><span class="p">,</span><span class="s2">&quot;[</span><span class="si">$i</span><span class="s2">]price&quot;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x">&lt;/td&gt;</span>
<span class="x">&lt;td&gt;</span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nx">CHtml</span><span class="o">::</span><span class="na">activeTextField</span><span class="p">(</span><span class="nv">$item</span><span class="p">,</span><span class="s2">&quot;[</span><span class="si">$i</span><span class="s2">]count&quot;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x">&lt;/td&gt;</span>
<span class="x">&lt;td&gt;</span><span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nx">CHtml</span><span class="o">::</span><span class="na">activeTextArea</span><span class="p">(</span><span class="nv">$item</span><span class="p">,</span><span class="s2">&quot;[</span><span class="si">$i</span><span class="s2">]description&quot;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x">&lt;/td&gt;</span>
<span class="x">&lt;/tr&gt;</span>
<span class="cp">&lt;?php</span> <span class="k">endforeach</span><span class="p">;</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">&lt;/table&gt;</span>
<span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nx">CHtml</span><span class="o">::</span><span class="na">submitButton</span><span class="p">(</span><span class="s1">&#39;Save&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nx">CHtml</span><span class="o">::</span><span class="na">endForm</span><span class="p">();</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">&lt;/div&gt;</span>
</code></pre></div>
<h2 id="todo">使用表单生成器 @todo</h2>
<p>action: </p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">actionLogin</span><span class="p">()</span> <span class="p">{</span>
<span class="nv">$model</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">LoginForm</span><span class="p">;</span>
<span class="nv">$form</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">CForm</span><span class="p">(</span><span class="s1">&#39;application.views.site.loginForm&#39;</span><span class="p">,</span> <span class="nv">$model</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="nv">$form</span><span class="o">-&gt;</span><span class="na">submitted</span><span class="p">(</span><span class="s1">&#39;login&#39;</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="nv">$form</span><span class="o">-&gt;</span><span class="na">validate</span><span class="p">())</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">redirect</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;site/index&#39;</span><span class="p">));</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">render</span><span class="p">(</span><span class="s1">&#39;login&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;form&#39;</span><span class="o">=&gt;</span><span class="nv">$form</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>protected/views/site/loginForm.php:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;title&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;Please provide your login credential&#39;</span><span class="p">,</span>
<span class="s1">&#39;elements&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;username&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="c1">// 可选 type: text, hidden, password, textarea, file, radio</span>
<span class="c1">// checkbox, listbox, dropdownlist, checkboxlist, radiolist</span>
<span class="s1">&#39;type&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;text&#39;</span><span class="p">,</span>
<span class="s1">&#39;maxlength&#39;</span><span class="o">=&gt;</span><span class="mi">32</span><span class="p">,</span>
<span class="p">),</span>
<span class="s1">&#39;password&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;type&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;password&#39;</span><span class="p">,</span>
<span class="s1">&#39;maxlength&#39;</span><span class="o">=&gt;</span><span class="mi">32</span><span class="p">,</span>
<span class="p">),</span>
<span class="s1">&#39;rememberMe&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;type&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;checkbox&#39;</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">),</span>
<span class="s1">&#39;buttons&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;login&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;type&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;submit&#39;</span><span class="p">,</span>
<span class="s1">&#39;label&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;Login&#39;</span><span class="p">,</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>view:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="x">&lt;h1&gt;Login&lt;/h1&gt;</span>
<span class="x">&lt;div class=&quot;form&quot;&gt;</span>
<span class="cp">&lt;?php</span> <span class="k">echo</span> <span class="nv">$form</span><span class="p">;</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">&lt;/div&gt;</span>
</code></pre></div>
<h1 id=">使用数据库</h1>
<h2 id="dao">数据访问对象(DAO)</h2>
<p>建立数据库连接:</p>
<ul>
<li>使用 <code>CDbConnection</code>:</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$connection</span><span class="o">=</span><span class="k">new</span> <span class="nx">CDbConnection</span><span class="p">(</span><span class="nv">$dsn</span><span class="p">,</span><span class="nv">$username</span><span class="p">,</span><span class="nv">$password</span><span class="p">);</span>
<span class="c1">// 建立连接。你可以使用 try...catch 捕获可能抛出的异常</span>
<span class="nv">$connection</span><span class="o">-&gt;</span><span class="na">active</span><span class="o">=</span><span class="k">true</span><span class="p">;</span>
<span class="o">......</span>
<span class="nv">$connection</span><span class="o">-&gt;</span><span class="na">active</span><span class="o">=</span><span class="k">false</span><span class="p">;</span> <span class="c1">// 关闭连接</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<ul>
<li>作为应用组件配置, 然后使用 <code>Yii::app()-&gt;db</code> 访问</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">array</span><span class="p">(</span>
<span class="o">......</span>
<span class="s1">&#39;components&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="o">......</span>
<span class="s1">&#39;db&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;class&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;CDbConnection&#39;</span><span class="p">,</span>
<span class="s1">&#39;connectionString&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;mysql:host=localhost;dbname=testdb&#39;</span><span class="p">,</span>
<span class="s1">&#39;username&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;root&#39;</span><span class="p">,</span>
<span class="s1">&#39;password&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;password&#39;</span><span class="p">,</span>
<span class="s1">&#39;emulatePrepare&#39;</span><span class="o">=&gt;</span><span class="k">true</span><span class="p">,</span> <span class="c1">// needed by some MySQL installations</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">)</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id="sql">执行 SQL 语句</h2>
<ol>
<li><p>创建 <code>CDbCommand</code> 实例</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$connection</span><span class="o">=</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">db</span><span class="p">;</span> <span class="c1">// 假设你已经建立了一个 &quot;db&quot; 连接</span>
<span class="c1">// 如果没有,你可能需要显式建立一个连接:</span>
<span class="c1">// $connection=new CDbConnection($dsn,$username,$password);</span>
<span class="nv">$command</span><span class="o">=</span><span class="nv">$connection</span><span class="o">-&gt;</span><span class="na">createCommand</span><span class="p">(</span><span class="nv">$sql</span><span class="p">);</span>
<span class="c1">// 如果需要,此 SQL 语句可通过如下方式修改:</span>
<span class="c1">// $command-&gt;text=$newSQL;</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>使用以下方法执行语句</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$rowCount</span><span class="o">=</span><span class="nv">$command</span><span class="o">-&gt;</span><span class="na">execute</span><span class="p">();</span> <span class="c1">// 执行无查询 SQL(Insert, delete, update)</span>
<span class="nv">$dataReader</span><span class="o">=</span><span class="nv">$command</span><span class="o">-&gt;</span><span class="na">query</span><span class="p">();</span> <span class="c1">// 执行一个 SQL 查询(select), 返回 CDbDataReader 实例</span>
<span class="nv">$rows</span><span class="o">=</span><span class="nv">$command</span><span class="o">-&gt;</span><span class="na">queryAll</span><span class="p">();</span> <span class="c1">// 查询并返回结果中的所有行</span>
<span class="nv">$row</span><span class="o">=</span><span class="nv">$command</span><span class="o">-&gt;</span><span class="na">queryRow</span><span class="p">();</span> <span class="c1">// 查询并返回结果中的第一行</span>
<span class="nv">$column</span><span class="o">=</span><span class="nv">$command</span><span class="o">-&gt;</span><span class="na">queryColumn</span><span class="p">();</span> <span class="c1">// 查询并返回结果中的第一列</span>
<span class="nv">$value</span><span class="o">=</span><span class="nv">$command</span><span class="o">-&gt;</span><span class="na">queryScalar</span><span class="p">();</span> <span class="c1">// 查询并返回结果中第一行的第一个字段</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>获取查询结果</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$dataReader</span><span class="o">=</span><span class="nv">$command</span><span class="o">-&gt;</span><span class="na">query</span><span class="p">();</span>
<span class="c1">// 重复调用 read() 直到它返回 false</span>
<span class="k">while</span><span class="p">((</span><span class="nv">$row</span><span class="o">=</span><span class="nv">$dataReader</span><span class="o">-&gt;</span><span class="na">read</span><span class="p">())</span><span class="o">!==</span><span class="k">false</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="c1">// 或使用 foreach 遍历数据中的每一行</span>
<span class="k">foreach</span><span class="p">(</span><span class="nv">$dataReader</span> <span class="k">as</span> <span class="nv">$row</span><span class="p">)</span> <span class="p">{</span> <span class="o">...</span> <span class="p">}</span>
<span class="c1">// 一次性提取所有行到一个数组</span>
<span class="nv">$rows</span><span class="o">=</span><span class="nv">$dataReader</span><span class="o">-&gt;</span><span class="na">readAll</span><span class="p">();</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>使用事务</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$transaction</span><span class="o">=</span><span class="nv">$connection</span><span class="o">-&gt;</span><span class="na">beginTransaction</span><span class="p">();</span>
<span class="k">try</span> <span class="p">{</span>
<span class="nv">$connection</span><span class="o">-&gt;</span><span class="na">createCommand</span><span class="p">(</span><span class="nv">$sql1</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">execute</span><span class="p">();</span>
<span class="nv">$connection</span><span class="o">-&gt;</span><span class="na">createCommand</span><span class="p">(</span><span class="nv">$sql2</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">execute</span><span class="p">();</span>
<span class="c1">//.... other SQL executions</span>
<span class="nv">$transaction</span><span class="o">-&gt;</span><span class="na">commit</span><span class="p">();</span>
<span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">Exception</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 如果有一条查询失败,则会抛出异常</span>
<span class="nv">$transaction</span><span class="o">-&gt;</span><span class="na">rollBack</span><span class="p">();</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>使用 Prepare Statment</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 一条带有两个占位符 &quot;:username&quot;&quot;:email&quot;的 SQL</span>
<span class="nv">$sql</span><span class="o">=</span><span class="s2">&quot;INSERT INTO tbl_user (username, email) VALUES(:username,:email)&quot;</span><span class="p">;</span>
<span class="nv">$command</span><span class="o">=</span><span class="nv">$connection</span><span class="o">-&gt;</span><span class="na">createCommand</span><span class="p">(</span><span class="nv">$sql</span><span class="p">);</span>
<span class="c1">// 用实际的用户名替换占位符 &quot;:username&quot; </span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">bindParam</span><span class="p">(</span><span class="s2">&quot;:username&quot;</span><span class="p">,</span><span class="nv">$username</span><span class="p">,</span><span class="nx">PDO</span><span class="o">::</span><span class="na">PARAM_STR</span><span class="p">);</span>
<span class="c1">// 用实际的 Email 替换占位符 &quot;:email&quot; </span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">bindParam</span><span class="p">(</span><span class="s2">&quot;:email&quot;</span><span class="p">,</span><span class="nv">$email</span><span class="p">,</span><span class="nx">PDO</span><span class="o">::</span><span class="na">PARAM_STR</span><span class="p">);</span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">execute</span><span class="p">();</span>
<span class="c1">// 使用新的参数集插入另一行</span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">bindParam</span><span class="p">(</span><span class="s2">&quot;:username&quot;</span><span class="p">,</span><span class="nv">$username2</span><span class="p">,</span><span class="nx">PDO</span><span class="o">::</span><span class="na">PARAM_STR</span><span class="p">);</span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">bindParam</span><span class="p">(</span><span class="s2">&quot;:email&quot;</span><span class="p">,</span><span class="nv">$email2</span><span class="p">,</span><span class="nx">PDO</span><span class="o">::</span><span class="na">PARAM_STR</span><span class="p">);</span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">execute</span><span class="p">();</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>绑定结果列</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$sql</span><span class="o">=</span><span class="s2">&quot;SELECT username, email FROM tbl_user&quot;</span><span class="p">;</span>
<span class="nv">$dataReader</span><span class="o">=</span><span class="nv">$connection</span><span class="o">-&gt;</span><span class="na">createCommand</span><span class="p">(</span><span class="nv">$sql</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">query</span><span class="p">();</span>
<span class="c1">// 使用 $username 变量绑定第一列 (username) </span>
<span class="nv">$dataReader</span><span class="o">-&gt;</span><span class="na">bindColumn</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="nv">$username</span><span class="p">);</span>
<span class="c1">// 使用 $email 变量绑定第二列 (email) </span>
<span class="nv">$dataReader</span><span class="o">-&gt;</span><span class="na">bindColumn</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="nv">$email</span><span class="p">);</span>
<span class="k">while</span><span class="p">(</span><span class="nv">$dataReader</span><span class="o">-&gt;</span><span class="na">read</span><span class="p">()</span><span class="o">!==</span><span class="k">false</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// $username 和 $email 含有当前行中的 username 和 email </span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>使用表前缀</p>
<p>配置 <code>CDbConnection::tablePrefix</code> 属性为所希望的表前缀, 然后便可以在 SQL 语句中使用 `` 代表表的名字</p></li>
</ol>
<h2 id=">查询构建器</h2>
<p>查询构建器构建于一个 <code>CDbCommand</code> 实例上</p>
<p>查询构建器不能用于修改一个已经存在的 SQL 查询</p>
<p>可用的查询构建器示例:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 1. 数据查询</span>
<span class="c1">// SELECT *</span>
<span class="nx">select</span><span class="p">()</span>
<span class="c1">// SELECT `id`, `username`</span>
<span class="nx">select</span><span class="p">(</span><span class="s1">&#39;id, username&#39;</span><span class="p">)</span>
<span class="c1">// SELECT `tbl_user`.`id`, `username` AS `name`</span>
<span class="nx">select</span><span class="p">(</span><span class="s1">&#39;tbl_user.id, username as name&#39;</span><span class="p">)</span>
<span class="c1">// SELECT `id`, `username`</span>
<span class="nx">select</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;username&#39;</span><span class="p">))</span>
<span class="c1">// SELECT `id`, count(*) as num</span>
<span class="nx">select</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;count(*) as num&#39;</span><span class="p">))</span>
<span class="c1">// SELECT DISTINCT `id`, `username`</span>
<span class="nx">selectDistinct</span><span class="p">(</span><span class="s1">&#39;id, username&#39;</span><span class="p">)</span>
<span class="c1">// FROM `tbl_user`</span>
<span class="nx">from</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">)</span>
<span class="c1">// FROM `tbl_user` `u`, `public`.`tbl_profile` `p`</span>
<span class="nx">from</span><span class="p">(</span><span class="s1">&#39;tbl_user u, public.tbl_profile p&#39;</span><span class="p">)</span>
<span class="c1">// FROM `tbl_user`, `tbl_profile`</span>
<span class="nx">from</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;tbl_profile&#39;</span><span class="p">))</span>
<span class="c1">// FROM `tbl_user`, (select * from tbl_profile) p</span>
<span class="nx">from</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;(select * from tbl_profile) p&#39;</span><span class="p">))</span>
<span class="c1">// WHERE id=1 or id=2</span>
<span class="nx">where</span><span class="p">(</span><span class="s1">&#39;id=1 or id=2&#39;</span><span class="p">)</span>
<span class="c1">// WHERE id=:id1 or id=:id2</span>
<span class="nx">where</span><span class="p">(</span><span class="s1">&#39;id=:id1 or id=:id2&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;:id1&#39;</span><span class="o">=&gt;</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;:id2&#39;</span><span class="o">=&gt;</span><span class="mi">2</span><span class="p">))</span>
<span class="c1">// WHERE id=1 OR id=2</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;or&#39;</span><span class="p">,</span> <span class="s1">&#39;id=1&#39;</span><span class="p">,</span> <span class="s1">&#39;id=2&#39;</span><span class="p">))</span>
<span class="c1">// WHERE id=1 AND (type=2 OR type=3)</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;and&#39;</span><span class="p">,</span> <span class="s1">&#39;id=1&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;or&#39;</span><span class="p">,</span> <span class="s1">&#39;type=2&#39;</span><span class="p">,</span> <span class="s1">&#39;type=3&#39;</span><span class="p">)))</span>
<span class="c1">// WHERE `id` IN (1, 2)</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;in&#39;</span><span class="p">,</span> <span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span>
<span class="c1">// WHERE `id` NOT IN (1, 2)</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;not in&#39;</span><span class="p">,</span> <span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">)))</span>
<span class="c1">// when using LIKE, remember to escape user inputed `%` and `_`</span>
<span class="c1">// WHERE `name` LIKE &#39;%Qiang%&#39;</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;like&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;%Qiang%&#39;</span><span class="p">))</span>
<span class="c1">// WHERE `name` LIKE &#39;%Qiang&#39; AND `name` LIKE &#39;%Xue&#39;</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;like&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;%Qiang&#39;</span><span class="p">,</span> <span class="s1">&#39;%Xue&#39;</span><span class="p">)))</span>
<span class="c1">// WHERE `name` LIKE &#39;%Qiang&#39; OR `name` LIKE &#39;%Xue&#39;</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;or like&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;%Qiang&#39;</span><span class="p">,</span> <span class="s1">&#39;%Xue&#39;</span><span class="p">)))</span>
<span class="c1">// WHERE `name` NOT LIKE &#39;%Qiang%&#39;</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;not like&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;%Qiang%&#39;</span><span class="p">))</span>
<span class="c1">// WHERE `name` NOT LIKE &#39;%Qiang%&#39; OR `name` NOT LIKE &#39;%Xue%&#39;</span>
<span class="nx">where</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;or not like&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;%Qiang%&#39;</span><span class="p">,</span> <span class="s1">&#39;%Xue%&#39;</span><span class="p">)))</span>
<span class="c1">// WHERE ... OR ...</span>
<span class="nx">orWhere</span><span class="p">()</span>
<span class="c1">// WHERE ... AND ...</span>
<span class="nx">andWhere</span><span class="p">()</span>
<span class="c1">// ORDER BY `name`, `id` DESC</span>
<span class="nx">order</span><span class="p">(</span><span class="s1">&#39;name, id desc&#39;</span><span class="p">)</span>
<span class="c1">// ORDER BY `tbl_profile`.`name`, `id` DESC</span>
<span class="nx">order</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;tbl_profile.name&#39;</span><span class="p">,</span> <span class="s1">&#39;id desc&#39;</span><span class="p">))</span>
<span class="c1">// LIMIT 10</span>
<span class="nx">limit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="c1">// LIMIT 10 OFFSET 20</span>
<span class="nx">limit</span><span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
<span class="c1">// OFFSET 20</span>
<span class="nx">offset</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
<span class="c1">// JOIN `tbl_profile` ON user_id=id</span>
<span class="nb">join</span><span class="p">(</span><span class="s1">&#39;tbl_profile&#39;</span><span class="p">,</span> <span class="s1">&#39;user_id=id&#39;</span><span class="p">)</span>
<span class="c1">// LEFT JOIN `pub`.`tbl_profile` `p` ON p.user_id=id AND type=1</span>
<span class="nx">leftJoin</span><span class="p">(</span><span class="s1">&#39;pub.tbl_profile p&#39;</span><span class="p">,</span> <span class="s1">&#39;p.user_id=id AND type=:type&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;:type&#39;</span><span class="o">=&gt;</span><span class="mi">1</span><span class="p">))</span>
<span class="c1">// RIGHT JOIN</span>
<span class="nx">rightJoin</span><span class="p">()</span>
<span class="c1">// CROSS JOIN</span>
<span class="nx">crossJoin</span><span class="p">()</span>
<span class="c1">// NATURAL JOIN</span>
<span class="nx">natrualJoin</span><span class="p">()</span>
<span class="c1">// GROUP BY `name`, `id`</span>
<span class="nx">group</span><span class="p">(</span><span class="s1">&#39;name, id&#39;</span><span class="p">)</span>
<span class="c1">// GROUP BY `tbl_profile`.`name`, `id`</span>
<span class="nx">group</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;tbl_profile.name&#39;</span><span class="p">,</span> <span class="s1">&#39;id&#39;</span><span class="p">))</span>
<span class="c1">// HAVING id=1 or id=2</span>
<span class="nx">having</span><span class="p">(</span><span class="s1">&#39;id=1 or id=2&#39;</span><span class="p">)</span>
<span class="c1">// HAVING id=1 OR id=2</span>
<span class="nx">having</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;or&#39;</span><span class="p">,</span> <span class="s1">&#39;id=1&#39;</span><span class="p">,</span> <span class="s1">&#39;id=2&#39;</span><span class="p">))</span>
<span class="c1">// UNION (select * from tbl_profile)</span>
<span class="nx">union</span><span class="p">(</span><span class="s1">&#39;select * from tbl_profile&#39;</span><span class="p">)</span>
<span class="c1">// 2. 数据操作(不同于数据查询, 数据操作会立即执行)</span>
<span class="c1">// INSERT INTO `tbl_user` (`name`, `email`) VALUES (:name, :email)</span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">insert</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;name&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;Tester&#39;</span><span class="p">,</span>
<span class="s1">&#39;email&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;tester@example.com&#39;</span><span class="p">,</span>
<span class="p">));</span>
<span class="c1">// UPDATE `tbl_user` SET `name`=:name WHERE id=:id</span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">update</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;name&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;Tester&#39;</span><span class="p">,</span>
<span class="p">),</span> <span class="s1">&#39;id=:id&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;:id&#39;</span><span class="o">=&gt;</span><span class="mi">1</span><span class="p">));</span>
<span class="c1">// DELETE FROM `tbl_user` WHERE id=:id</span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">delete</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;id=:id&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;:id&#39;</span><span class="o">=&gt;</span><span class="mi">1</span><span class="p">));</span>
<span class="c1">// 3. Schema 操作</span>
<span class="c1">// CREATE TABLE `tbl_user` (</span>
<span class="c1">// `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,</span>
<span class="c1">// `username` varchar(255) NOT NULL,</span>
<span class="c1">// `location` point</span>
<span class="c1">// ) ENGINE=InnoDB</span>
<span class="nx">createTable</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;id&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;pk&#39;</span><span class="p">,</span>
<span class="s1">&#39;username&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;string NOT NULL&#39;</span><span class="p">,</span>
<span class="s1">&#39;location&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;point&#39;</span><span class="p">,</span>
<span class="p">),</span> <span class="s1">&#39;ENGINE=InnoDB&#39;</span><span class="p">)</span>
<span class="c1">// RENAME TABLE `tbl_users` TO `tbl_user`</span>
<span class="nx">renameTable</span><span class="p">(</span><span class="s1">&#39;tbl_users&#39;</span><span class="p">,</span> <span class="s1">&#39;tbl_user&#39;</span><span class="p">)</span>
<span class="c1">// DROP TABLE `tbl_user`</span>
<span class="nx">dropTable</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">)</span>
<span class="c1">// TRUNCATE TABLE `tbl_user`</span>
<span class="nx">truncateTable</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">)</span>
<span class="c1">// ALTER TABLE `tbl_user` ADD `email` varchar(255) NOT NULL</span>
<span class="nx">addColumn</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;email&#39;</span><span class="p">,</span> <span class="s1">&#39;string NOT NULL&#39;</span><span class="p">)</span>
<span class="c1">// ALTER TABLE `tbl_user` DROP COLUMN `location`</span>
<span class="nx">dropColumn</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;location&#39;</span><span class="p">)</span>
<span class="c1">// ALTER TABLE `tbl_users` CHANGE `name` `username` varchar(255) NOT NULL</span>
<span class="nx">renameColumn</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;name&#39;</span><span class="p">,</span> <span class="s1">&#39;username&#39;</span><span class="p">)</span>
<span class="c1">// ALTER TABLE `tbl_user` CHANGE `username` `username` varchar(255) NOT NULL</span>
<span class="nx">alterColumn</span><span class="p">(</span><span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;username&#39;</span><span class="p">,</span> <span class="s1">&#39;string NOT NULL&#39;</span><span class="p">)</span>
<span class="c1">// ALTER TABLE `tbl_profile` ADD CONSTRAINT `fk_profile_user_id`</span>
<span class="c1">// FOREIGN KEY (`user_id`) REFERENCES `tbl_user` (`id`)</span>
<span class="c1">// ON DELETE CASCADE ON UPDATE CASCADE</span>
<span class="nx">addForeignKey</span><span class="p">(</span><span class="s1">&#39;fk_profile_user_id&#39;</span><span class="p">,</span> <span class="s1">&#39;tbl_profile&#39;</span><span class="p">,</span> <span class="s1">&#39;user_id&#39;</span><span class="p">,</span>
<span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;CASCADE&#39;</span><span class="p">,</span> <span class="s1">&#39;CASCADE&#39;</span><span class="p">)</span>
<span class="c1">// ALTER TABLE `tbl_profile` DROP FOREIGN KEY `fk_profile_user_id`</span>
<span class="nx">dropForeignKey</span><span class="p">(</span><span class="s1">&#39;fk_profile_user_id&#39;</span><span class="p">,</span> <span class="s1">&#39;tbl_profile&#39;</span><span class="p">)</span>
<span class="c1">// CREATE INDEX `idx_username` ON `tbl_user` (`username`)</span>
<span class="nx">createIndex</span><span class="p">(</span><span class="s1">&#39;idx_username&#39;</span><span class="p">,</span> <span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span> <span class="s1">&#39;username&#39;</span><span class="p">)</span>
<span class="c1">// DROP INDEX `idx_username` ON `tbl_user`</span>
<span class="nx">dropIndex</span><span class="p">(</span><span class="s1">&#39;idx_username&#39;</span><span class="p">,</span> <span class="s1">&#39;tbl_user&#39;</span><span class="p">)</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>也可通过使用属性赋值方式:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$command</span><span class="o">-&gt;</span><span class="na">select</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;username&#39;</span><span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>或在创建 <code>CDbCommand</code> 是传配置参数的方式构建:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$row</span> <span class="o">=</span> <span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">db</span><span class="o">-&gt;</span><span class="na">createCommand</span><span class="p">(</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;select&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;username&#39;</span><span class="p">),</span>
<span class="s1">&#39;from&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;tbl_user&#39;</span><span class="p">,</span>
<span class="s1">&#39;where&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;id=:id&#39;</span><span class="p">,</span>
<span class="s1">&#39;params&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;:id&#39;</span><span class="o">=&gt;</span><span class="mi">1</span><span class="p">),</span>
<span class="p">))</span><span class="o">-&gt;</span><span class="na">queryRow</span><span class="p">();</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>构建完成后, 可以使用在<a href="#runSql">执行 SQL 语句</a>中讲到的方法执行之; 也可使用 <code>CDbCommand::getText()</code> 获取最后构建完工后的 SQL 语句, 绑定的参数被保存在 <code>CDbCommand::params</code></p>
<p>同一个 <code>CDbCommand</code> 实例可用于多次构建不同的查询, 但是记得要再另一次之前调用 <code>CDbCommand::reset()</code> 以清理上次的查询</p>
<h2 id="active-record"><a name="ActiveRecord">Active Record</a></h2>
<p>每个 AR 类代表一个数据表(或视图), 数据表(或视图)的列在 AR 类中体现为类的属性, 一个 AR 实例则表示表中的一行</p>
<p>最佳应用是模型化数据表为 PHP 结构和执行不包含复杂 SQL 语句的查询. 对于复杂查询的场景, 应使用 Yii DAO</p>
<p>如果你数据库的表结构很少改动, 你应该通过配置 <code>CDbConnection::schemaCachingDuration</code> 属性的值为一个大于零的值开启表结构缓存</p>
<p>通过 AR 使用多个数据库有两种方式. 如果数据库的结构不同, 你可以创建不同的 AR 基类实现不同的 <code>getDbConnection()</code>; 否则, 动态改变静态变量 <code>CActiveRecord::db</code> 是一个好主意</p>
<p>由于 AR 类经常在多处被引用, 我们可以导入包含 AR 类的整个目录, 而不是一个个导入. 见<a href="#">路径别名和命名空间</a></p>
<p>通过 Yii 的日志功能, 可以查看 AR 在背后到底执行了那些语句</p>
<p>定义 AR 类:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">Post</span> <span class="k">extends</span> <span class="nx">CActiveRecord</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">function</span> <span class="nf">model</span><span class="p">(</span><span class="nv">$className</span><span class="o">=</span><span class="nx">__CLASS__</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">parent</span><span class="o">::</span><span class="na">model</span><span class="p">(</span><span class="nv">$className</span><span class="p">);</span>
<span class="p">}</span>
<span class="sd">/**</span>
<span class="sd"> * 默认情况下, AR 类的名字和数据表的名字相同. 如果不同, 请覆盖 `CActiveRecord::tableName` 方法</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">tableName</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="s1">&#39;tbl_post&#39;</span><span class="p">;</span>
<span class="p">}</span>
<span class="sd">/**</span>
<span class="sd"> * AR 依靠表中良好定义的主键. 如果一个表没有主键,则必须在相应的 AR 类中通过如下方式覆盖 primaryKey() 方法指定哪一列或哪几列作为主键:</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">primaryKey</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="s1">&#39;id&#39;</span><span class="p">;</span>
<span class="c1">// 对于复合主键,要返回一个类似如下的数组</span>
<span class="c1">// return array(&#39;pk1&#39;, &#39;pk2&#39;);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>创建记录:</p>
<ul>
<li>如果表的主键是自增的, 在插入完成后, AR 实例将包含一个更新的主键</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$post</span><span class="o">=</span><span class="k">new</span> <span class="nx">Post</span><span class="p">;</span>
<span class="nv">$post</span><span class="o">-&gt;</span><span class="na">title</span><span class="o">=</span><span class="s1">&#39;sample post&#39;</span><span class="p">;</span>
<span class="nv">$post</span><span class="o">-&gt;</span><span class="na">content</span><span class="o">=</span><span class="s1">&#39;content for the sample post&#39;</span><span class="p">;</span>
<span class="c1">// 如果要使用 Mysql 的 NOW(), 必须使用 CDbExpression, 单纯的 &#39;NOW()&#39; 将会被作为字符串对待</span>
<span class="nv">$post</span><span class="o">-&gt;</span><span class="na">create_time</span><span class="o">=</span><span class="k">new</span> <span class="nx">CDbExpression</span><span class="p">(</span><span class="s1">&#39;NOW()&#39;</span><span class="p">);</span>
<span class="nv">$post</span><span class="o">-&gt;</span><span class="na">save</span><span class="p">();</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>读取记录:</p>
<ul>
<li><code>find</code> 系列返回一个 AR 实例, 或者 <code>null</code></li>
<li><code>findAll</code> 系列返回 AR 实例数组, 或者空数组</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 1. 常规</span>
<span class="c1">// 查找满足指定条件的结果中的第一行</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">find</span><span class="p">(</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">find</span><span class="p">(</span><span class="s1">&#39;postID=:postID&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;:postID&#39;</span><span class="o">=&gt;</span><span class="mi">10</span><span class="p">));</span>
<span class="c1">// 查找具有指定主键值的那一行</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findByPk</span><span class="p">(</span><span class="nv">$postID</span><span class="p">,</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 查找具有指定属性值的行</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findByAttributes</span><span class="p">(</span><span class="nv">$attributes</span><span class="p">,</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 通过指定的 SQL 语句查找结果中的第一行</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findBySql</span><span class="p">(</span><span class="nv">$sql</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 查找满足指定条件的所有行</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">(</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 查找带有指定主键的所有行</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findAllByPk</span><span class="p">(</span><span class="nv">$postIDs</span><span class="p">,</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 查找带有指定属性值的所有行</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findAllByAttributes</span><span class="p">(</span><span class="nv">$attributes</span><span class="p">,</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 通过指定的SQL语句查找所有行</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findAllBySql</span><span class="p">(</span><span class="nv">$sql</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 获取满足指定条件的行数</span>
<span class="nv">$n</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">count</span><span class="p">(</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 通过指定的 SQL 获取结果行数</span>
<span class="nv">$n</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">countBySql</span><span class="p">(</span><span class="nv">$sql</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 检查是否至少有一行复合指定的条件</span>
<span class="nv">$exists</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">exists</span><span class="p">(</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 2. 使用 `CDbCriteria`</span>
<span class="nv">$criteria</span><span class="o">=</span><span class="k">new</span> <span class="nx">CDbCriteria</span><span class="p">;</span>
<span class="nv">$criteria</span><span class="o">-&gt;</span><span class="na">select</span><span class="o">=</span><span class="s1">&#39;title&#39;</span><span class="p">;</span> <span class="c1">// 只选择 &#39;title&#39;</span>
<span class="nv">$criteria</span><span class="o">-&gt;</span><span class="na">condition</span><span class="o">=</span><span class="s1">&#39;postID=:postID&#39;</span><span class="p">;</span>
<span class="nv">$criteria</span><span class="o">-&gt;</span><span class="na">params</span><span class="o">=</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;:postID&#39;</span><span class="o">=&gt;</span><span class="mi">10</span><span class="p">);</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">find</span><span class="p">(</span><span class="nv">$criteria</span><span class="p">);</span> <span class="c1">// $params 不需要了</span>
<span class="c1">// 3. 传递数组</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">find</span><span class="p">(</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;select&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;title&#39;</span><span class="p">,</span>
<span class="s1">&#39;condition&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;postID=:postID&#39;</span><span class="p">,</span>
<span class="s1">&#39;params&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;:postID&#39;</span><span class="o">=&gt;</span><span class="mi">10</span><span class="p">),</span>
<span class="p">));</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>更新记录:</p>
<ul>
<li><p>如果一个 AR 实例是使用 new 操作符创建的, 调用 <code>CActiveRecord::save</code> 将会向数据表中插入一行新数据; 如果 AR 实例是某个 find 或 findAll 方法的结果, 调用 <code>CActiveRecord::save</code> 将更新表中现有的行. 实际上, 我们是使用 <code>CActiveRecord::isNewRecord</code> 说明一个 AR 实例是不是新的</p></li>
<li><p>直接更新数据表中的一行或多行而不首先载入也是可行的:</p></li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 更新符合指定条件的行</span>
<span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">updateAll</span><span class="p">(</span><span class="nv">$attributes</span><span class="p">,</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 更新符合指定条件和主键的行</span>
<span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">updateByPk</span><span class="p">(</span><span class="nv">$pk</span><span class="p">,</span><span class="nv">$attributes</span><span class="p">,</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 更新满足指定条件的行的计数列</span>
<span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">updateCounters</span><span class="p">(</span><span class="nv">$counters</span><span class="p">,</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>删除记录:</p>
<ul>
<li>实例化后删除: 这样删除之后, AR 实例仍不改变</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findByPk</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span> <span class="c1">// 假设有一个帖子,其 ID 为 10</span>
<span class="nv">$post</span><span class="o">-&gt;</span><span class="na">delete</span><span class="p">();</span> <span class="c1">// 从数据表中删除此行</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<ul>
<li>不实例化直接删除</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 删除符合指定条件的行</span>
<span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">deleteAll</span><span class="p">(</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="c1">// 删除符合指定条件和主键的行</span>
<span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">deleteByPk</span><span class="p">(</span><span class="nv">$pk</span><span class="p">,</span><span class="nv">$condition</span><span class="p">,</span><span class="nv">$params</span><span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>如果要确定两个 AR 是否是同一个记录, 只需对比它们的主键值, 或直接调用 <code>CActiveRecord::equals()</code></p>
<p>通过以下几个占位符方法, 可以自定义 AR 的工作流:</p>
<table><thead>
<tr>
<th>占位符方法</th>
<th>含义</th>
</tr>
</thead><tbody>
<tr>
<td>beforeValidate, afterValidate</td>
<td>在验证之前(后)执行</td>
</tr>
<tr>
<td>beforeSave, afterSave</td>
<td>在保存 AR 实例之前(后)执行</td>
</tr>
<tr>
<td>beforeFind, afterFind</td>
<td>在执行查询之前(后)执行</td>
</tr>
<tr>
<td>afterConstruct</td>
<td>在 AR 实例化之后执行</td>
</tr>
</tbody></table>
<p>数据验证和块赋值参见<a href="#triggerValidation">触发验证</a><a href="#massiveAssign">块赋值</a></p>
<p>事务处理, 参见<a href="#">使用事务</a></p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$model</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">();</span>
<span class="nv">$transaction</span><span class="o">=</span><span class="nv">$model</span><span class="o">-&gt;</span><span class="na">dbConnection</span><span class="o">-&gt;</span><span class="na">beginTransaction</span><span class="p">();</span>
<span class="k">try</span> <span class="p">{</span>
<span class="c1">// 查找和保存是可能由另一个请求干预的两个步骤</span>
<span class="c1">// 这样我们使用一个事务以确保其一致性和完整性</span>
<span class="nv">$post</span><span class="o">=</span><span class="nv">$model</span><span class="o">-&gt;</span><span class="na">findByPk</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="nv">$post</span><span class="o">-&gt;</span><span class="na">title</span><span class="o">=</span><span class="s1">&#39;new post title&#39;</span><span class="p">;</span>
<span class="nv">$post</span><span class="o">-&gt;</span><span class="na">save</span><span class="p">();</span>
<span class="nv">$transaction</span><span class="o">-&gt;</span><span class="na">commit</span><span class="p">();</span>
<span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="nx">Exception</span> <span class="nv">$e</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$transaction</span><span class="o">-&gt;</span><span class="na">rollBack</span><span class="p">();</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>命名范围: 即查询时的过滤器</p>
<ul>
<li>定义</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">Post</span> <span class="k">extends</span> <span class="nx">CActiveRecord</span> <span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * 默认命名范围, 隐式应用于所有关于此模型的 SELECT 查询</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">defaultScope</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;condition&#39;</span><span class="o">=&gt;</span><span class="s2">&quot;language=&#39;&quot;</span><span class="o">.</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">language</span><span class="o">.</span><span class="s2">&quot;&#39;&quot;</span><span class="p">,</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="sd">/**</span>
<span class="sd"> * 这里定义的命名范围可以被显式应用于 SELECT,UPDATE,CREATE,DELETE 操作</span>
<span class="sd"> * @return {[type]} [description]</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">scopes</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;published&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;condition&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;status=1&#39;</span><span class="p">,</span>
<span class="p">),</span>
<span class="s1">&#39;recently&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;order&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;create_time DESC&#39;</span><span class="p">,</span>
<span class="s1">&#39;limit&#39;</span><span class="o">=&gt;</span><span class="mi">5</span><span class="p">,</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<ul>
<li>使用</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">published</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">recently</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">();</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id="active-record">关系型 Active Record</h2>
<p>为了使用关系型 AR, 建议在关联的表中定义主键-外键约束</p>
<p>关系包括: <code>BELONGS_TO</code>, <code>HAS_MANY</code>, <code>HAS_ONE</code>, <code>MANY_MANY</code>, <code>STAT</code></p>
<p>使用 <code>STAT</code> 关系已获取统计数据</p>
<p>适当使用 <code>together</code> 查询选项, 会加快查询速度</p>
<p>在 AR 查询中, 基础表的别名为 <code>t</code>, 其他关联表的别名和关系的名称一样</p>
<p>声明关系</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">Post</span> <span class="k">extends</span> <span class="nx">CActiveRecord</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">relations</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;author&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">BELONGS_TO</span><span class="p">,</span> <span class="s1">&#39;User&#39;</span><span class="p">,</span> <span class="s1">&#39;author_id&#39;</span><span class="p">),</span>
<span class="s1">&#39;categories&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">MANY_MANY</span><span class="p">,</span> <span class="s1">&#39;Category&#39;</span><span class="p">,</span>
<span class="s1">&#39;tbl_post_category(post_id, category_id)&#39;</span><span class="p">),</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">class</span> <span class="nc">User</span> <span class="k">extends</span> <span class="nx">CActiveRecord</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">relations</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;posts&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">HAS_MANY</span><span class="p">,</span> <span class="s1">&#39;Post&#39;</span><span class="p">,</span> <span class="s1">&#39;author_id&#39;</span><span class="p">),</span>
<span class="s1">&#39;profile&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">HAS_ONE</span><span class="p">,</span> <span class="s1">&#39;Profile&#39;</span><span class="p">,</span> <span class="s1">&#39;owner_id&#39;</span><span class="p">),</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 使用额外的选项</span>
<span class="c1">// 可用选项包括: select, condition, params, on, order, with, joinType, alias, together, join, group, having, index</span>
<span class="c1">// 当使用 `STAT` 关系时, 可用的选项包括: select, defaultValue, condition, params, order, group, having</span>
<span class="k">class</span> <span class="nc">User</span> <span class="k">extends</span> <span class="nx">CActiveRecord</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">relations</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;posts&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">HAS_MANY</span><span class="p">,</span> <span class="s1">&#39;Post&#39;</span><span class="p">,</span> <span class="s1">&#39;author_id&#39;</span><span class="p">,</span>
<span class="s1">&#39;order&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;posts.create_time DESC&#39;</span><span class="p">,</span>
<span class="s1">&#39;with&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;categories&#39;</span><span class="p">),</span>
<span class="s1">&#39;profile&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">HAS_ONE</span><span class="p">,</span> <span class="s1">&#39;Profile&#39;</span><span class="p">,</span> <span class="s1">&#39;owner_id&#39;</span><span class="p">),</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>执行关联查询</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 1. 懒惰式加载: </span>
<span class="c1">// 获取 ID 为 10 的帖子</span>
<span class="nv">$post</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findByPk</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
<span class="c1">// 获取帖子的作者(author): 此处将执行一个关联查询。</span>
<span class="nv">$author</span><span class="o">=</span><span class="nv">$post</span><span class="o">-&gt;</span><span class="na">author</span><span class="p">;</span>
<span class="c1">// 2. 渴求式加载(比懒惰式高效)</span>
<span class="c1">// 2.1 常规方式</span>
<span class="c1">// 获取 post 及其作者和分类</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">with</span><span class="p">(</span><span class="s1">&#39;author&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">();</span>
<span class="c1">// 获取 post 及其作者和分类, 以及作者简介(author.profile) 和帖子(author.posts)</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">with</span><span class="p">(</span>
<span class="s1">&#39;author.profile&#39;</span><span class="p">,</span>
<span class="s1">&#39;author.posts&#39;</span><span class="p">,</span>
<span class="s1">&#39;categories&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">();</span>
<span class="c1">// 2.2 指定 `CDbCeteria::with` 属性</span>
<span class="nv">$criteria</span><span class="o">=</span><span class="k">new</span> <span class="nx">CDbCriteria</span><span class="p">;</span>
<span class="nv">$criteria</span><span class="o">-&gt;</span><span class="na">with</span><span class="o">=</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;author.profile&#39;</span><span class="p">,</span>
<span class="s1">&#39;author.posts&#39;</span><span class="p">,</span>
<span class="s1">&#39;categories&#39;</span><span class="p">,</span>
<span class="p">);</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">(</span><span class="nv">$criteria</span><span class="p">);</span>
<span class="c1">// 2.3 配置数组</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">(</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;with&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;author.profile&#39;</span><span class="p">,</span>
<span class="s1">&#39;author.posts&#39;</span><span class="p">,</span>
<span class="s1">&#39;categories&#39;</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">);</span>
<span class="c1">// 3. 动态</span>
<span class="c1">// 3.1</span>
<span class="nx">User</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">with</span><span class="p">(</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;posts&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;order&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;posts.create_time ASC&#39;</span><span class="p">),</span>
<span class="s1">&#39;profile&#39;</span><span class="p">,</span>
<span class="p">))</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">();</span>
<span class="c1">// 3.2</span>
<span class="nv">$user</span><span class="o">=</span><span class="nx">User</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findByPk</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nv">$user</span><span class="o">-&gt;</span><span class="na">posts</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;condition&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;status=1&#39;</span><span class="p">));</span>
<span class="c1">// 如果关系中没有相关的实例,则相应的属性将为 null(BELONGS_TO, HAS_ONE) 或一个空数组(HAS_MANY, MANY_MANY)</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>使用命名空间</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 1.</span>
<span class="k">class</span> <span class="nc">User</span> <span class="k">extends</span> <span class="nx">CActiveRecord</span> <span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">relations</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;posts&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="nx">self</span><span class="o">::</span><span class="na">HAS_MANY</span><span class="p">,</span> <span class="s1">&#39;Post&#39;</span><span class="p">,</span> <span class="s1">&#39;author_id&#39;</span><span class="p">,</span>
<span class="s1">&#39;with&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;comments:approved&#39;</span><span class="p">),</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 2.</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">published</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">recently</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">with</span><span class="p">(</span><span class="s1">&#39;comments&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">();</span>
<span class="c1">// 3.</span>
<span class="nv">$posts</span><span class="o">=</span><span class="nx">Post</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">published</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">recently</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">with</span><span class="p">(</span><span class="s1">&#39;comments&#39;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">findAll</span><span class="p">();</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id="todo">数据库迁移 @todo</h2>
<h1 id=">缓存</h1>
<h2 id=">配置</h2>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">array</span><span class="p">(</span>
<span class="o">......</span>
<span class="s1">&#39;components&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="o">......</span>
<span class="s1">&#39;cache&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="c1">// 支持的缓存包括:</span>
<span class="c1">// - CMemCache</span>
<span class="c1">// - CXCache</span>
<span class="c1">// - CEAcceleratorCache</span>
<span class="c1">// - CDbCache: 默认使用 runtime 目录下的 SQLite3 数据库</span>
<span class="c1">// - CZendDataCache</span>
<span class="c1">// - CFileCache</span>
<span class="c1">// - CDummyCache: 只是为了开发阶段模拟尚未实现的缓存功能</span>
<span class="s1">&#39;class&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;system.caching.CMemCache&#39;</span><span class="p">,</span>
<span class="s1">&#39;servers&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;host&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;server1&#39;</span><span class="p">,</span> <span class="s1">&#39;port&#39;</span><span class="o">=&gt;</span><span class="mi">11211</span><span class="p">,</span> <span class="s1">&#39;weight&#39;</span><span class="o">=&gt;</span><span class="mi">60</span><span class="p">),</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;host&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;server2&#39;</span><span class="p">,</span> <span class="s1">&#39;port&#39;</span><span class="o">=&gt;</span><span class="mi">11211</span><span class="p">,</span> <span class="s1">&#39;weight&#39;</span><span class="o">=&gt;</span><span class="mi">40</span><span class="p">),</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id=">数据缓存(用于存储变量)</h2>
<h3 id=">存取操作</h3>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 生成</span>
<span class="c1">// 过期时间 30 可选, 同一个应用中的缓存 id 必须唯一</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">cache</span><span class="o">-&gt;</span><span class="na">set</span><span class="p">(</span><span class="nv">$id</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="mi">30</span><span class="p">);</span>
<span class="c1">// 调取</span>
<span class="c1">// 1. 单个</span>
<span class="nv">$value</span><span class="o">=</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">cache</span><span class="o">-&gt;</span><span class="na">get</span><span class="p">(</span><span class="nv">$id</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$value</span> <span class="o">===</span> <span class="k">false</span><span class="p">)</span>
<span class="p">{</span>
<span class="c1">// 因为在缓存中没找到 $value ,重新生成它 </span>
<span class="c1">// 并将它存入缓存以备以后使用:</span>
<span class="c1">// Yii::app()-&gt;cache-&gt;set($id,$value);</span>
<span class="p">}</span>
<span class="c1">// 2. 批量</span>
<span class="nv">$values</span><span class="o">=</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">cache</span><span class="o">-&gt;</span><span class="na">mget</span><span class="p">(</span><span class="k">array</span> <span class="nv">$ids</span><span class="p">);</span>
<span class="c1">// 删除</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">cache</span><span class="o">-&gt;</span><span class="na">delete</span><span class="p">(</span><span class="nv">$id</span><span class="p">);</span>
<span class="c1">// 重刷</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">cache</span><span class="o">-&gt;</span><span class="na">flush</span><span class="p">();</span>
<span class="c1">// CCache 实现了 ArrayAccess, 所以也可通过下列方式操作:</span>
<span class="nv">$cache</span><span class="o">=</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">cache</span><span class="p">;</span>
<span class="nv">$cache</span><span class="p">[</span><span class="s1">&#39;var1&#39;</span><span class="p">]</span><span class="o">=</span><span class="nv">$value1</span><span class="p">;</span> <span class="c1">// 相当于: $cache-&gt;set(&#39;var1&#39;,$value1);</span>
<span class="nv">$value2</span><span class="o">=</span><span class="nv">$cache</span><span class="p">[</span><span class="s1">&#39;var2&#39;</span><span class="p">];</span> <span class="c1">// 相当于: $value2=$cache-&gt;get(&#39;var2&#39;);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h3 id=">缓存依赖</h3>
<p>除了过期设置,缓存数据也可能会因为依赖条件发生变化而失效。例如,如果我们缓存了某些文件的内容,而这些文件发生了改变,我们就应该让缓存的数据失效</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 此值将在30秒后失效</span>
<span class="c1">// 也可能因依赖的文件发生了变化而更快失效</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">cache</span><span class="o">-&gt;</span><span class="na">set</span><span class="p">(</span><span class="nv">$id</span><span class="p">,</span> <span class="nv">$value</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="k">new</span> <span class="nx">CFileCacheDependency</span><span class="p">(</span><span class="s1">&#39;FileName&#39;</span><span class="p">));</span>
<span class="c1">// 可用的依赖有:</span>
<span class="c1">// - CFileCacheDependency: 如果文件的最后修改时间发生改变</span>
<span class="c1">// - CDirectoryCacheDependency: 如果目录和其子目录中的文件发生改变</span>
<span class="c1">// - CDbCacheDependency: 如果指定 SQL 语句的查询结果发生改变</span>
<span class="c1">// - CGlobalStateCacheDependency: 如果指定的全局状态发生改变</span>
<span class="c1">// - CChainedCacheDependency: 如果链中的任何依赖发生改变</span>
<span class="c1">// - CExpressionDependency: 如果指定的 PHP 表达式的结果发生改变</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id=">片段缓存</h2>
<h3 id=">基本使用方法</h3>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span> <span class="k">if</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">beginCache</span><span class="p">(</span><span class="nv">$id</span><span class="p">))</span> <span class="p">{</span> <span class="c1">// 如果缓存有效, 则输出缓存... ?&gt;</span>
<span class="o">...</span><span class="nx">被缓存的内容</span><span class="o">...</span>
<span class="o">&lt;?</span><span class="nx">php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">endCache</span><span class="p">();</span> <span class="p">}</span> <span class="c1">// ...否则在此处存储缓存 ?&gt;</span>
</code></pre></div>
<h3 id=">使用缓存选项</h3>
<p><code>beginCache()</code> 和 <code>endCache()</code> 方法是 <code>COutputCache</code> widget 的包装, 因此 <code>COutputCache</code> 的所有属性都可以在缓存选项中初始化</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span> <span class="k">if</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">beginCache</span><span class="p">(</span>
<span class="nv">$id</span><span class="p">,</span>
<span class="k">array</span><span class="p">(</span>
<span class="c1">// 过期时间, 默认 60</span>
<span class="s1">&#39;duration&#39;</span> <span class="o">=&gt;</span> <span class="mi">3600</span><span class="p">,</span>
<span class="c1">// 依赖, 可以是一个实现 `ICacheDependency` 的对象, </span>
<span class="c1">// 或能生成依赖对象想的配置数组</span>
<span class="c1">// 参见数据缓存</span>
<span class="s1">&#39;dependency&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;class&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;system.caching.dependencies.CDbCacheDependency&#39;</span><span class="p">,</span>
<span class="s1">&#39;sql&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;SELECT MAX(lastModified) FROM Post&#39;</span>
<span class="p">),</span>
<span class="c1">// 变化, 指定缓存将根据哪些因素变化</span>
<span class="c1">// 其他可用的有: `varyByRoute`, `varyBySession`, `varyByParam`, `varyByLanguage`</span>
<span class="s1">&#39;varyByExpression&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;Yii::app()-&gt;user-&gt;isGuest&#39;</span><span class="p">,</span>
<span class="c1">// 请求类型: 只对指定的请求类型启用缓存</span>
<span class="s1">&#39;requestTypes&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;GET&#39;</span><span class="p">)</span>
<span class="p">)</span>
<span class="p">)</span>
<span class="p">)</span> <span class="p">{</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x"> ...被缓存的内容...</span>
<span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">endCache</span><span class="p">();</span> <span class="p">}</span> <span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h3 id=">嵌套</h3>
<p>片段缓存可以嵌套, 当数据存储在外部缓存无效, 内部缓存仍然可以提供有效的内部片段. 然而反之就不行了</p>
<h2 id=">页面缓存</h2>
<p>如果想要缓存整个页面, 我们应该跳过产生网页内容的动作执行. 我们可以使用 <code>COutputCache</code><code>CHttpCacheFilter</code> 作为动作过滤器来完成这一任务</p>
<h3 id="coutputcache">COutputCache</h3>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">filters</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;COutputCache&#39;</span><span class="p">,</span>
<span class="s1">&#39;duration&#39;</span><span class="o">=&gt;</span><span class="mi">100</span><span class="p">,</span>
<span class="s1">&#39;varyByParam&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h3 id="chttpcachefilter">CHttpCacheFilter</h3>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">filters</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;CHttpCacheFilter + index&#39;</span><span class="p">,</span>
<span class="s1">&#39;lastModified&#39;</span><span class="o">=&gt;</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">db</span><span class="o">-&gt;</span><span class="na">createCommand</span><span class="p">(</span><span class="s2">&quot;SELECT MAX(`update_time`) FROM &quot;</span><span class="p">)</span><span class="o">-&gt;</span><span class="na">queryScalar</span><span class="p">(),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id=">动态内容</h2>
<p>动态内容是指, 即使是在片段缓存包括的内容中也不会被缓存, 而是通过传递给 <code>renderDynamic</code> 的有效回调函数生成</p>
<p>回调可以是指向当前控制器类的方法或者全局函数的字符串名,
也可以是一个数组名指向一个类的方法</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span> <span class="k">if</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">beginCache</span><span class="p">(</span><span class="nv">$id</span><span class="p">))</span> <span class="p">{</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">...被缓存的片段内容...</span>
<span class="x"> </span><span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">renderDynamic</span><span class="p">(</span><span class="nv">$callback</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">...被缓存的片段内容...</span>
<span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">endCache</span><span class="p">();</span> <span class="p">}</span> <span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h1 id=">专题</h1>
<h2 id="url">URL 管理</h2>
<p><strong>配置 URL 格式及转发规则:</strong></p>
<p><em>NOTE: 过多的规则会导致应用性能下降</em></p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 在配置文件中:</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;components&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;urlManager&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;urlFormat&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;path&#39;</span><span class="p">,</span> <span class="c1">// 格式</span>
<span class="s1">&#39;rules&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span> <span class="c1">// 转发规则</span>
<span class="c1">// 1.</span>
<span class="c1">// `pattern` 用于和当前 URL 的 path info 做匹配</span>
<span class="c1">// 匹配成功的话, 会跳转到对应的 `route` 指定的 CA</span>
<span class="s1">&#39;pattern1&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;route1&#39;</span><span class="p">,</span>
<span class="c1">// 2.</span>
<span class="c1">// 也可以使用数组形式指定转发规则, 如此一来便能针对同一 pattern 指定多个规则, 或者通过 verb 来支持 RESTful URL</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;route1&#39;</span><span class="p">,</span>
<span class="s1">&#39;pattern&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;pattern1&#39;</span><span class="p">,</span>
<span class="s1">&#39;urlSuffix&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;.xml&#39;</span><span class="p">,</span>
<span class="s1">&#39;caseSensitive&#39;</span><span class="o">=&gt;</span><span class="k">false</span>
<span class="p">),</span>
<span class="c1">// 可用的选项包括: `pattern`, `urlSuffix`, `caseSensitive`, `defualtParams`, `matchValue`, `verb`, `parsisngOnly`</span>
<span class="c1">// 3.</span>
<span class="c1">// 使用命名参数: &lt;ParamName:ParamPattern&gt;</span>
<span class="c1">// 当 URL 被解析后, 命名的参数会自动生成到 $_GET 中</span>
<span class="s1">&#39;http://&lt;user:\w+&gt;.example.com/&lt;lang:\w+&gt;/profile&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;user/profile&#39;</span><span class="p">,</span>
<span class="c1">// 示例:</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;api/&lt;controller&gt;/update&#39;</span><span class="p">,</span>
<span class="s1">&#39;pattern&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;api/&lt;controller:\w+&gt;/&lt;id:\d+&gt;&#39;</span><span class="p">,</span>
<span class="s1">&#39;verb&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;PUT, POST&#39;</span>
<span class="p">),</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;api/&lt;controller&gt;/delete&#39;</span><span class="p">,</span>
<span class="s1">&#39;pattern&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;api/&lt;controller:\w+&gt;/&lt;id:\d+&gt;&#39;</span><span class="p">,</span>
<span class="s1">&#39;verb&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;DELETE&#39;</span>
<span class="p">),</span>
<span class="s1">&#39;&lt;controller:\w+&gt;/&lt;action:\w+&gt;/&lt;id:\d+&gt;&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;&lt;controller&gt;/&lt;action&gt;&#39;</span><span class="p">,</span>
<span class="s1">&#39;&lt;action:(login|logout|about)&gt;&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;site/&lt;action&gt;&#39;</span><span class="p">,</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p><strong>生成 URL:</strong></p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// createUrl 生成相对 URL</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">createUrl</span><span class="p">(</span><span class="s1">&#39;post/read&#39;</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;id&#39;</span> <span class="o">=&gt;</span> <span class="mi">100</span><span class="p">));</span>
<span class="c1">// 如果要得到绝对 URL, 可以使用 `Yii::app()-&gt;hostInfo;` 或 `CController::createAbsosluteUrl`</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p><strong>使用自定义 URL 解析器:</strong></p>
<ul>
<li>定义解析器类</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="sd">/**</span>
<span class="sd"> * 自定义 URL 解析器必须继承 CBasUrlRule, 并实现 `createUrl()` 和 `parseUrl`</span>
<span class="sd"> */</span>
<span class="k">class</span> <span class="nc">CarUrlRule</span> <span class="k">extends</span> <span class="nx">CBaseUrlRule</span>
<span class="p">{</span>
<span class="k">public</span> <span class="nv">$connectionID</span> <span class="o">=</span> <span class="s1">&#39;db&#39;</span><span class="p">;</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">createUrl</span><span class="p">(</span><span class="nv">$manager</span><span class="p">,</span><span class="nv">$route</span><span class="p">,</span><span class="nv">$params</span><span class="p">,</span><span class="nv">$ampersand</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$route</span><span class="o">===</span><span class="s1">&#39;car/index&#39;</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$params</span><span class="p">[</span><span class="s1">&#39;manufacturer&#39;</span><span class="p">],</span> <span class="nv">$params</span><span class="p">[</span><span class="s1">&#39;model&#39;</span><span class="p">]))</span>
<span class="k">return</span> <span class="nv">$params</span><span class="p">[</span><span class="s1">&#39;manufacturer&#39;</span><span class="p">]</span> <span class="o">.</span> <span class="s1">&#39;/&#39;</span> <span class="o">.</span> <span class="nv">$params</span><span class="p">[</span><span class="s1">&#39;model&#39;</span><span class="p">];</span>
<span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$params</span><span class="p">[</span><span class="s1">&#39;manufacturer&#39;</span><span class="p">]))</span>
<span class="k">return</span> <span class="nv">$params</span><span class="p">[</span><span class="s1">&#39;manufacturer&#39;</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">return</span> <span class="k">false</span><span class="p">;</span> <span class="c1">// this rule does not apply</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">parseUrl</span><span class="p">(</span><span class="nv">$manager</span><span class="p">,</span><span class="nv">$request</span><span class="p">,</span><span class="nv">$pathInfo</span><span class="p">,</span><span class="nv">$rawPathInfo</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nb">preg_match</span><span class="p">(</span><span class="s1">&#39;%^(\w+)(/(\w+))?$%&#39;</span><span class="p">,</span> <span class="nv">$pathInfo</span><span class="p">,</span> <span class="nv">$matches</span><span class="p">))</span>
<span class="p">{</span>
<span class="c1">// check $matches[1] and $matches[3] to see</span>
<span class="c1">// if they match a manufacturer and a model in the database</span>
<span class="c1">// If so, set $_GET[&#39;manufacturer&#39;] and/or $_GET[&#39;model&#39;]</span>
<span class="c1">// and return &#39;car/index&#39;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="k">false</span><span class="p">;</span> <span class="c1">// this rule does not apply</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<ul>
<li>指定 URL 转发规则:</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;class&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;application.components.CarUrlRule&#39;</span><span class="p">,</span>
<span class="s1">&#39;connectionID&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;db&#39;</span><span class="p">,</span>
<span class="p">),</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id=">验证</h2>
<p>验证即核查一个人是否真实他声称的那个人(用户名, 密码); 授权即检查是否有权操作特定的资源</p>
<p>定义身份类:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">UserIdentity</span> <span class="k">extends</span> <span class="nx">CUserIdentity</span> <span class="p">{</span>
<span class="k">private</span> <span class="nv">$_id</span><span class="p">;</span>
<span class="sd">/**</span>
<span class="sd"> * 这是身份类的主要工作, 实现验证</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">authenticate</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// 使用 User AR 获取数据</span>
<span class="nv">$record</span><span class="o">=</span><span class="nx">User</span><span class="o">::</span><span class="na">model</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">findByAttributes</span><span class="p">(</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="o">=&gt;</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">username</span><span class="p">));</span>
<span class="k">if</span><span class="p">(</span><span class="nv">$record</span><span class="o">===</span><span class="k">null</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">errorCode</span><span class="o">=</span><span class="nx">self</span><span class="o">::</span><span class="na">ERROR_USERNAME_INVALID</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">{</span>
<span class="p">(</span><span class="nv">$record</span><span class="o">-&gt;</span><span class="na">password</span><span class="o">!==</span><span class="nb">md5</span><span class="p">(</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">password</span><span class="p">))</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">errorCode</span><span class="o">=</span><span class="nx">self</span><span class="o">::</span><span class="na">ERROR_PASSWORD_INVALID</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_id</span><span class="o">=</span><span class="nv">$record</span><span class="o">-&gt;</span><span class="na">id</span><span class="p">;</span>
<span class="c1">// 使用 setState 把 title 信息存储为状态传递给 CWebUser</span>
<span class="c1">// 之后便可以使用 Yii::app()-&gt;user-&gt;title 访问</span>
<span class="c1">// CWebUser 默认会存储状态信息到 session, 但如果 CWebUser::allowAutoLogin 为 true, 则会存到 cookie, 切勿将敏感信息存入 cookie</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">setState</span><span class="p">(</span><span class="s1">&#39;title&#39;</span><span class="p">,</span> <span class="nv">$record</span><span class="o">-&gt;</span><span class="na">title</span><span class="p">);</span>
<span class="nv">$this</span><span class="o">-&gt;</span><span class="na">errorCode</span><span class="o">=</span><span class="nx">self</span><span class="o">::</span><span class="na">ERROR_NONE</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="o">!</span><span class="nv">$this</span><span class="o">-&gt;</span><span class="na">errorCode</span><span class="p">;</span>
<span class="p">}</span>
<span class="sd">/**</span>
<span class="sd"> * 重写 getId, 默认的实现是直接返回用户名</span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">getId</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">_id</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>登录和注销:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 1. 使用提供的用户名和密码登录用户</span>
<span class="nv">$identity</span><span class="o">=</span><span class="k">new</span> <span class="nx">UserIdentity</span><span class="p">(</span><span class="nv">$username</span><span class="p">,</span><span class="nv">$password</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="nv">$identity</span><span class="o">-&gt;</span><span class="na">authenticate</span><span class="p">())</span> <span class="p">{</span>
<span class="c1">// 将用户身份信息存入持久存储(默认为 Session)</span>
<span class="c1">// 之后便可以用 `Yii::app-&gt;user-&gt;isGuest` 判断用户是否登录</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">user</span><span class="o">-&gt;</span><span class="na">login</span><span class="p">(</span><span class="nv">$identity</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">echo</span> <span class="nv">$identity</span><span class="o">-&gt;</span><span class="na">errorMessage</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">......</span>
<span class="c1">// 注销当前用户</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">user</span><span class="o">-&gt;</span><span class="na">logout</span><span class="p">();</span>
<span class="c1">// 2. 使用 Cookie 登录</span>
<span class="c1">// 要确保用户部件的allowAutoLogin被设置为true。</span>
<span class="c1">// 保留用户登陆状态时间7天</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">user</span><span class="o">-&gt;</span><span class="na">login</span><span class="p">(</span><span class="nv">$identity</span><span class="p">,</span><span class="mi">3600</span><span class="o">*</span><span class="mi">24</span><span class="o">*</span><span class="mi">7</span><span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>如果使用 cookie 登录, 要确保不要保存敏感信息到 State, 而是保存到持久存储(数据库) 上, 最好(参见<a href="#security">安全</a>):</p>
<ul>
<li>当用户成功登录时, 保存同一个随机串到 cookie State 和数据库</li>
<li>在之后的的自动 cookie 登录时, 对比 cookie 中和数据库中的随机串是否一致</li>
<li>如果用户通过登录表单登录, 刷新这个随机串</li>
</ul>
<h2 id=">授权</h2>
<p>访问控制过滤器的定义参见<a href="#filter">过滤器</a></p>
<p>过滤器定义之后, 还要通过重载 <code>CController::accessRules</code> 指定具体授权规则</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">class</span> <span class="nc">PostController</span> <span class="k">extends</span> <span class="nx">CController</span> <span class="p">{</span>
<span class="sd">/**</span>
<span class="sd"> * 配置数组的值可为</span>
<span class="sd"> * 第一项: `deny` 或者 `allow`</span>
<span class="sd"> * actions: action 名字</span>
<span class="sd"> * users: *: 任何用户, ?: 匿名用户, @: 验证通过的用户 </span>
<span class="sd"> */</span>
<span class="k">public</span> <span class="k">function</span> <span class="nf">accessRules</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;deny&#39;</span><span class="p">,</span>
<span class="s1">&#39;actions&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;create&#39;</span><span class="p">,</span> <span class="s1">&#39;edit&#39;</span><span class="p">),</span>
<span class="s1">&#39;users&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;?&#39;</span><span class="p">),</span>
<span class="p">),</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;allow&#39;</span><span class="p">,</span>
<span class="s1">&#39;actions&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;delete&#39;</span><span class="p">),</span>
<span class="s1">&#39;roles&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;admin&#39;</span><span class="p">),</span>
<span class="p">),</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;deny&#39;</span><span class="p">,</span>
<span class="s1">&#39;actions&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;delete&#39;</span><span class="p">),</span>
<span class="s1">&#39;users&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;*&#39;</span><span class="p">),</span>
<span class="p">),</span>
<span class="c1">// 为了确保某类动作在没允许情况下不被执行</span>
<span class="k">array</span><span class="p">(</span><span class="s1">&#39;deny&#39;</span><span class="p">,</span>
<span class="s1">&#39;action&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;delete&#39;</span><span class="p">,</span>
<span class="p">)</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>如果授权失败</p>
<ul>
<li><p>已经配置 <code>CWebUser::loingUrl</code>, 则重定向到此 URL, 可以这样配置:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">array</span><span class="p">(</span>
<span class="o">......</span>
<span class="s1">&#39;components&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;user&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="c1">// 这实际上是默认值</span>
<span class="s1">&#39;loginUrl&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;site/login&#39;</span><span class="p">),</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">)</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>否则显示一个 401 HTTP 例外</p></li>
</ul>
<p>如果希望在用户登录成功后转到之前页面:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">request</span><span class="o">-&gt;</span><span class="na">redirect</span><span class="p">(</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">user</span><span class="o">-&gt;</span><span class="na">returnUrl</span><span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id="role-based-access-control">基于角色的访问控制(Role-Based Access Control)</h2>
<p><code>授权项目</code>可分为<code>操作(operations)</code>, <code>任务(tasks)</code><code>角色(roles)</code> 一个角色由若干任务组成, 一个任务由若干操作组成, 而一个操作就是一个<code>许可</code>, 不可再分. Yii 还允许一个角色包含其他角色或操作, 一个任务可以包含其他操作, 一个操作可以包括其他操作. 授权项目是通过它的名字唯一识别的</p>
<p>一个授权项目可能与一个<code>业务规则</code>关联. 业务规则是一段 PHP 代码, 在进行涉及授权项目的访问检查时将会被执行. 仅在执行返回 <code>true</code> 时, 用户才会被视为拥有此授权项目所代表的权限许可</p>
<p>Yii 提供了两种授权管理器: <code>CPhpAuthManager</code><code>CDbAuthManager</code>. 前者将授权数据存储在一个 PHP 脚本文件中而后者存储在数据库中. 配置 <code>CWebApplication::authManager</code> 应用组件时, 我们需要指定使用哪个授权管理器组件类, 以及所选授权管理器组件的初始化属性值:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;components&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;db&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;class&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;CDbConnection&#39;</span><span class="p">,</span>
<span class="s1">&#39;connectionString&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;sqlite:path/to/file.db&#39;</span><span class="p">,</span>
<span class="p">),</span>
<span class="s1">&#39;authManager&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;class&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;CDbAuthManager&#39;</span><span class="p">,</span>
<span class="s1">&#39;connectionID&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;db&#39;</span><span class="p">,</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>然后, 我们便可以使用 <code>Yii::app()-&gt;authManager</code> 访问</p>
<p>定义授权等级体总共分三步</p>
<ol>
<li>定义授权项目
<ul>
<li><code>CAuthManager::createRole</code></li>
<li><code>CAuthManager::createTask</code></li>
<li><code>CAuthManager::createOperation</code></li>
</ul></li>
<li>建立授权项目之间的关系
<ul>
<li><code>CAuthManager::addItemChild</code></li>
<li><code>CAuthManager::removeItemChild</code></li>
<li><code>CAuthItem::addChild</code></li>
<li><code>CAuthItem::removeChild</code></li>
</ul></li>
<li>分配角色给用户
<ul>
<li><code>CAuthManager::assign</code></li>
<li><code>CAuthManager::revoke</code></li>
</ul></li>
</ol>
<p>如:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 并不需要在每个请求中都要运行</span>
<span class="nv">$auth</span><span class="o">=</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">authManager</span><span class="p">;</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createOperation</span><span class="p">(</span><span class="s1">&#39;createPost&#39;</span><span class="p">,</span><span class="s1">&#39;create a post&#39;</span><span class="p">);</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createOperation</span><span class="p">(</span><span class="s1">&#39;readPost&#39;</span><span class="p">,</span><span class="s1">&#39;read a post&#39;</span><span class="p">);</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createOperation</span><span class="p">(</span><span class="s1">&#39;updatePost&#39;</span><span class="p">,</span><span class="s1">&#39;update a post&#39;</span><span class="p">);</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createOperation</span><span class="p">(</span><span class="s1">&#39;deletePost&#39;</span><span class="p">,</span><span class="s1">&#39;delete a post&#39;</span><span class="p">);</span>
<span class="nv">$bizRule</span><span class="o">=</span><span class="s1">&#39;return Yii::app()-&gt;user-&gt;id==$params[&quot;post&quot;]-&gt;authID;&#39;</span><span class="p">;</span>
<span class="nv">$task</span><span class="o">=</span><span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createTask</span><span class="p">(</span><span class="s1">&#39;updateOwnPost&#39;</span><span class="p">,</span><span class="s1">&#39;update a post by author himself&#39;</span><span class="p">,</span><span class="nv">$bizRule</span><span class="p">);</span>
<span class="nv">$task</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;updatePost&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">=</span><span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createRole</span><span class="p">(</span><span class="s1">&#39;reader&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;readPost&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">=</span><span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createRole</span><span class="p">(</span><span class="s1">&#39;author&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;reader&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;createPost&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;updateOwnPost&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">=</span><span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createRole</span><span class="p">(</span><span class="s1">&#39;editor&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;reader&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;updatePost&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">=</span><span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createRole</span><span class="p">(</span><span class="s1">&#39;admin&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;editor&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;author&#39;</span><span class="p">);</span>
<span class="nv">$role</span><span class="o">-&gt;</span><span class="na">addChild</span><span class="p">(</span><span class="s1">&#39;deletePost&#39;</span><span class="p">);</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">assign</span><span class="p">(</span><span class="s1">&#39;reader&#39;</span><span class="p">,</span><span class="s1">&#39;readerA&#39;</span><span class="p">);</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">assign</span><span class="p">(</span><span class="s1">&#39;author&#39;</span><span class="p">,</span><span class="s1">&#39;authorB&#39;</span><span class="p">);</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">assign</span><span class="p">(</span><span class="s1">&#39;editor&#39;</span><span class="p">,</span><span class="s1">&#39;editorC&#39;</span><span class="p">);</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">assign</span><span class="p">(</span><span class="s1">&#39;admin&#39;</span><span class="p">,</span><span class="s1">&#39;adminD&#39;</span><span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>权限检查:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">if</span><span class="p">(</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">user</span><span class="o">-&gt;</span><span class="na">checkAccess</span><span class="p">(</span><span class="s1">&#39;createPost&#39;</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// 创建帖子</span>
<span class="p">}</span>
<span class="c1">// 也可传参</span>
<span class="nv">$params</span><span class="o">=</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;post&#39;</span><span class="o">=&gt;</span><span class="nv">$post</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">user</span><span class="o">-&gt;</span><span class="na">checkAccess</span><span class="p">(</span><span class="s1">&#39;updateOwnPost&#39;</span><span class="p">,</span><span class="nv">$params</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// 更新帖子</span>
<span class="p">}</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>默认角色就是一个隐式分配给每个用户的角色, 这些用户包括通过身份验证的用户和游客</p>
<p>配置:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;components&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;authManager&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;class&#39;</span><span class="o">=&gt;</span><span class="s1">&#39;CDbAuthManager&#39;</span><span class="p">,</span>
<span class="s1">&#39;defaultRoles&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span><span class="s1">&#39;authenticated&#39;</span><span class="p">,</span> <span class="s1">&#39;guest&#39;</span><span class="p">),</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<p>定义:</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="nv">$bizRule</span><span class="o">=</span><span class="s1">&#39;return !Yii::app()-&gt;user-&gt;isGuest;&#39;</span><span class="p">;</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createRole</span><span class="p">(</span><span class="s1">&#39;authenticated&#39;</span><span class="p">,</span> <span class="s1">&#39;authenticated user&#39;</span><span class="p">,</span> <span class="nv">$bizRule</span><span class="p">);</span>
<span class="nv">$bizRule</span><span class="o">=</span><span class="s1">&#39;return Yii::app()-&gt;user-&gt;isGuest;&#39;</span><span class="p">;</span>
<span class="nv">$auth</span><span class="o">-&gt;</span><span class="na">createRole</span><span class="p">(</span><span class="s1">&#39;guest&#39;</span><span class="p">,</span> <span class="s1">&#39;guest user&#39;</span><span class="p">,</span> <span class="nv">$bizRule</span><span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div>
<h2 id=">安全</h2>
<ul>
<li><p>XSS: 跨站脚本攻击</p>
<p>定义: 攻击者常常向易受攻击的 web 应用注入 JavaScript, VBScript, ActiveX, HTML 或 Flash 来迷惑访问者以收集访问者的信息</p>
<p>防范: 在显示用户输入的内容之前进行检查</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="x">// 这里将 CHtmlPurifier 作为一个 Widget 来过滤用户输入</span>
<span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">beginWidget</span><span class="p">(</span><span class="s1">&#39;CHtmlPurifier&#39;</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span>
<span class="x">//...这里显示用户输入的内容...</span>
<span class="cp">&lt;?php</span> <span class="nv">$this</span><span class="o">-&gt;</span><span class="na">endWidget</span><span class="p">();</span> <span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>CSRF: 跨站请求伪造</p>
<p>定义: 攻击者在用户浏览器在访问恶意网站的时候, 让用户的浏览器向一个受信任的网站发起攻击者指定的请求</p>
<p>防范: GET 请求只允许检索数据而不能修改服务器上的任何数据, 而 POST 请求应当含有一些可以被服务器识别的随机数值, 用来保证表单数据的来源和运行结果发送的去向是相同的</p>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 启用 CsrfValidation 组件</span>
<span class="c1">// 该组件会自动在用 CHtml::form 生成的表单中嵌入一个保存随机值的隐藏项, 在表单提交的时候发送到服务器进行验证</span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;components&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;request&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;enableCsrfValidation&#39;</span><span class="o">=&gt;</span><span class="k">true</span><span class="p">,</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
<li><p>Cookie 攻击</p>
<p>定义: session ID 通常存储在 Cookie中, 如果攻击者窃取到了一个有效的 session ID, 他就可以使用这个 session ID 对应的 session 信息</p>
<p>防范:</p>
<ul>
<li>您可以使用 SSL 来产生一个安全通道, 且只通过 HTTPS 连接来传送验证 cookie</li>
<li>设置 cookie 的过期时间, 对所有的 cookie 和 session 令牌也这样做</li>
<li>防范跨站代码攻击, 因为它可以在用户的浏览器触发任意代码, 这些代码可能会泄露用户的 cookie</li>
<li>在 cookie 有变动的时候验证 cookie 的内容</li>
</ul>
<div class="highlight"><pre><code class="language-php" data-lang="php"><span class="cp">&lt;?php</span>
<span class="c1">// 1. 启用 CookieValidation 组件 </span>
<span class="k">return</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;components&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;request&#39;</span><span class="o">=&gt;</span><span class="k">array</span><span class="p">(</span>
<span class="s1">&#39;enableCookieValidation&#39;</span><span class="o">=&gt;</span><span class="k">true</span><span class="p">,</span>
<span class="p">),</span>
<span class="p">),</span>
<span class="p">);</span>
<span class="c1">// 然后只使用 CHttpRequest::cookies 进行 cookie 操作(而不是 $_COOKIES)</span>
<span class="c1">// 检索一个名为$name的cookie值</span>
<span class="nv">$cookie</span><span class="o">=</span><span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">request</span><span class="o">-&gt;</span><span class="na">cookies</span><span class="p">[</span><span class="nv">$name</span><span class="p">];</span>
<span class="nv">$value</span><span class="o">=</span><span class="nv">$cookie</span><span class="o">-&gt;</span><span class="na">value</span><span class="p">;</span>
<span class="o">......</span>
<span class="c1">// 设置一个cookie</span>
<span class="nv">$cookie</span><span class="o">=</span><span class="k">new</span> <span class="nx">CHttpCookie</span><span class="p">(</span><span class="nv">$name</span><span class="p">,</span><span class="nv">$value</span><span class="p">);</span>
<span class="nx">Yii</span><span class="o">::</span><span class="na">app</span><span class="p">()</span><span class="o">-&gt;</span><span class="na">request</span><span class="o">-&gt;</span><span class="na">cookies</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span><span class="o">=</span><span class="nv">$cookie</span><span class="p">;</span>
<span class="cp">?&gt;</span><span class="x"></span>
</code></pre></div></li>
</ul>
</div>
<div id="markdown-outline" class="col-lg-3">
</div>
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES * * */
var disqus_shortname = 'unifreak-github';
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
</div>
</div>
<footer id="l-footer">
<div class="container">
<div class="row">
<div id="contact" class="col-lg-6 col-lg-offset-1 col-md-6 col-md-offset-1 col-sm-9">
<h3>CONTACT</h3>
<div class="row">
<address id="address" class="col-lg-6 col-md-6 col-sm-6">
China Beijing Beijing<br>
ChaoYang, 0<br>
AnHuiLi<br>
Paul Walko<br>
</address>
<ul class="col-lg-6 col-md-6 col-sm-6">
<li class="qq"><i class="fa fa-qq"></i> &nbsp;&nbsp;744910240</li>
<li class="email"><i class="fa fa-envelope"></i> <a href="mailto:fanghao90s@gmail.com"> &nbsp;&nbsp;fanghao90s@gmail.com</a></li>
<li class="tel"><i class="fa fa-mobile"></i> <a href="tel:15671542698">&nbsp;&nbsp; 5025096242</a></li>
<li class="github"><i class="fa fa-github"></i> <a href="http://www.github.com/UniFreak"> &nbsp;&nbsp;http://www.github.com/UniFreak</a></li>
</ul>
</div>
</div>
<div id="rss" class="col-lg-2 col-md-2 col-sm-2">
<h3>SUBSCRIBE<br>
via</h3>
<a href="feed.xml">
<i class="rss fa fa-rss-square"></i>
</a>
</div>
</div>
<p id="legal">
Copyright (c) 2015 paulwalko | Powered by <a href="http://jekyllrb.com">Jekyll</a> &amp; <a href="http://github.com">GitHub</a> | designed &amp; build by <a href="http://unifreak.github.io">UniFreak</a>
</p>
</div>
</footer>
<script type="text/javascript" src="http://localhost:4000/javascripts/base.js"></script>
<!-- 百度统计 -->
<script type="text/javascript" src="http://localhost:4000/javascripts/baidu_statistics.js"></script>
<script type="text/javascript" src="http://localhost:4000/javascripts/markdownreader.js"></script>
</body>
</html>