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

1973 lines
177 KiB
HTML
Raw Normal View History

2016-06-14 21:54:24 -04:00
<!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>