3.5.3 控制器中的方法

如果需要查看完整的控制器方法的介绍,请直接访问CakePHP API http://api.cakephp.org/1.2/class_controller.html.

3.5.3.1 与视图进行交互

3.5.3.1.1 set

set(string $var, mixed $value)
  1. set(string $var, mixed $value)

set() 方法是从你的控制器向视图传输数据的主要方法.一旦你使用了set(),变量就可以在你的视图中访问了.

<?php
    
//First you pass data from the controller:

$this->set('color', 'pink');

//Then, in the view, you can utilize the data:

You have selected <?php echo $color; ?> icing for the cake.

?>
  1. <?php
  2. //First you pass data from the controller:
  3. $this->set('color', 'pink');
  4. //Then, in the view, you can utilize the data:
  5. You have selected <?php echo $color; ?> icing for the cake.
  6. ?>

set()方法还可以携带一个数组作为它的第一个参数.这通常是向视图分配一组信息的快速的方法.但要注意的是,在它被分配给视图前,数组键的下划线会被替换掉, (‘underscored_key’ 将会变成‘underscoredKey’):

<?php
    
$data = array(
    'color' => 'pink',
    'type' => 'sugar',
    'base_price' => 23.95
);

//make $color, $type, and $basePrice 
//available to the view:

$this->set($data);  

?>
  1. <?php
  2. $data = array(
  3. 'color' => 'pink',
  4. 'type' => 'sugar',
  5. 'base_price' => 23.95
  6. );
  7. //make $color, $type, and $basePrice
  8. //available to the view:
  9. $this->set($data);
  10. ?>

3.5.3.1.2 render

render(string $action, string $layout, string $file)

render()方法在每个控制器行为(action)结束后被自动调用。该方法(使用被set()方法传递到视图中的变量)执行所有的显示层逻辑,输出到视图模板,并回显给用户。

在没有明确指明的情况下,render方法使用按照命名规约决定采用哪个模板文件。比如RecipesController的search()被执行时,/app/views/recipes/search.ctp会被自动地作为模板进行输出。

尽管CakePHP会在每个行为(action)逻辑执行结束后自动地调用该方法(除非$this->autoRender被设置为false),你也可以通过手动指定$action的值以输出到该行为(action)对应的视图模板,还可以指定第三个参数$file的值来明确指明使用的视图模板文件。使用$file的时候请尽量利用全局路径常量(比如VIEWS)。

$layout参数允许指定显示试图的时候使用哪个页面模板(layout)。

3.5.3.2 流程控制

3.5.3.2.1 redirect

redirect(string $url, integer $status, boolean $exit)

redirect()应该是你最经常使用的流程控制的方法之一。该方法接收的第一个参数是CakePHP形式的相对URL。当用户成功地下了订单之后,你也许会希望将他带到收款台前执行下一步操作。

function placeOrder() {

    //这里是订单部分的逻辑

    if($success) {
        $this->redirect(array('controller' => 'orders', 'action' => 'thanks'));
    } else {
        $this->redirect(array('controller' => 'orders', 'action' => 'confirm'));
    }
}
  1. function placeOrder() {
  2. //这里是订单部分的逻辑
  3. if($success) {
  4. $this->redirect(array('controller' => 'orders', 'action' => 'thanks'));
  5. } else {
  6. $this->redirect(array('controller' => 'orders', 'action' => 'confirm'));
  7. }
  8. }

redirect()接收的第二个参数允许你定义一个HTTP的状态码(status code)比如视具体情况,你可能选择用301(永久性转移)或者303(see other)。

在第三参数没有被设定为false的情况下,重定向结束之后该方法会自动调用exit()结束程序。

3.5.3.2.2 flash

flash(string $message, string $url, integer $pause)

同样,flash()方法也会将用户重定向到一个新的页面。不同之处在于flash()方法在将用户转向到新页面之前会向其显示一条提示信息。

传递给该方法的第一个参数就是提示信息的内容,第二个参数则是CakePHP形式的相对URL。CakePHP将会显示这条信息($message)$pause秒,然后执行转向。

有关页面内提示信息(flash message)的显示请参看SessionComponentsetFlash()方法。

3.5.3.3 Callbacks

CakePHP提供了一系列的回调函数可以在控制器的行为(action)执行前后进行特别的处理。

beforeFilter()

该函数在控制器中的所有行为(action)执行之前被自动调用。在该函数中进行session或者用户权限的处理是非常方便的。

beforeRender()

该函数在控制器中行为(action)执行之后,向视图文件输出渲染之前被调用。该函数被使用得并不多,不过当你想在手动进行render()并在之前做点什么的话就派上用场了。

afterFilter()

该函数在每个控制器行为(action)之后被调用。

afterRender()

该函数在每个行为(action)被渲染输出之后调用。

CakePHP同样有与脚手架(scaffolding)关联的回调函数。

_beforeScaffold($method)

$method: 被调用的行为方法名。比如index, edit等。

_afterScaffoldSave($method)

$method: 被调用的行为方法名。可能是edit或者update。

_afterScaffoldSaveError($method)

$method: 被调用的行为方法名。可能是edit或者update。

_scaffoldError($method)

$method: 被调用的行为方法名。比如index, edit等。

3.5.3.4 其他有用的方法

3.5.3.4.1 constructClasses

该方法加载控制器需要的模型(model)。该加载过程通常由CakePHP控制,不过该方法也适用于那些从特定函数进行控制器访问的情况。比如你需要在命令行脚本或者其他地方使用CakePHP的时候,constructClasses()可能会帮上你的忙。

3.5.3.4.2 referer

返回当前请求的参照页面的URL。

3.5.3.4.3 disableCache

该函数可以告知用户的浏览器不要缓存当前请求页面的内容。注意该缓存不同于下一章会提到的视图的缓存。

3.5.3.4.4 postConditions

postConditions(array $data, mixed $op, string $bool, boolean $exclusive)

该方法可以将接收到的POST的数据(与HtmlHelper相兼容的数据形式)转换成find方法可以使用的数据集。该方法对从POST数据快速建立搜索逻辑非常有效,比如,管理员需要查看订单以确定需要配送的货物的时候,可以利用CakePHP的Form和HtmlHelper创建一个基于订单(Order)的表单,然后在控制器的行为中使用POST得到的数据进行搜索:

function index() {
    $o = $this->Orders->findAll($this->postConditions($this->data));
    $this->set('orders', $o);
}
  1. function index() {
  2. $o = $this->Orders->findAll($this->postConditions($this->data));
  3. $this->set('orders', $o);
  4. }

如果$this->data['Order']['destination']"Old Towne Bakery"相等,postConditions就将这个条件转换成一个Model->findAll()能使用的条件数组。在这个例子中就是array("Order.destination" = > "Old Towne Bakery")

如果你想使用其他的SQL运算符,像下例一样,使用第二个参数。

/*
$this->data的内容
array(
    'Order' => array(
        'num_items' => '4',
        'referrer' => 'Ye Olde'
    )
)
*/

//查找至少有4件货物以及包含"Ye Olde"的订单
$o = $this->Order->findAll($this->postConditions(
    $this->data,
    array('>=', 'LIKE')
));
  1. /*
  2. $this->data的内容
  3. array(
  4. 'Order' => array(
  5. 'num_items' => '4',
  6. 'referrer' => 'Ye Olde'
  7. )
  8. )
  9. */
  10. //查找至少有4件货物以及包含"Ye Olde"的订单
  11. $o = $this->Order->findAll($this->postConditions(
  12. $this->data,
  13. array('>=', 'LIKE')
  14. ));

指定运算符的关键在于$this->data数组中字段的顺序。由于num_items在第一位,于是>=是应用于该字段的。

第三个参数允许你告诉CakePHP在这些条件之间使用什么逻辑运算符进行连接。这里可以使用"AND","OR",以及"XOR"等等。

最后,如果最后的参数被设为true,并且$op是一个数组,那么不包含在$op中的字段将不会被包含在返回的条件之中。

3.5.3.4.5 paginate

该方法用于将模型(model)取到的数据进行分页。可以设置每页显示的件数,查找的条件以及其他的选项。要查看更详细的信息请参阅pagination

3.5.3.4.6 requestAction

requestAction(string $url, array $options)

该方法可以在任何位置调用控制器中的行为(action)并将行为中的数据返回。参数$url传递一个CakePHP形式的相对URL(/controllername/actionname/params)。如果需要传递数据给被调用的行为,请使用$options参数。

通过在options中指定'return'可以取到被渲染结束的视图数据:requestAction($url, array('return'));

在不使用缓存的情况下,requestAction可能会导致非常低下的性能。在控制器(controller)或者模型(model)这么使用是非常不合适的。

requestAction最适合用在(使用了缓存的)页面元素(element)的相关处理上,用于在渲染页面之前取得该元素需要的数据。假设有这么一个例子,我们需要在页面显示最近的评论这样一个页面元素。那么首先我们创建一个控制器的函数来返回这些数据。

// controllers/comments_controller.php
class CommentsController extends AppController {
    function latest() {
        return $this->Comment->find('all', array('order' => 'Comment.created DESC', 'limit' => 10));
    }
}
  1. // controllers/comments_controller.php
  2. class CommentsController extends AppController {
  3. function latest() {
  4. return $this->Comment->find('all', array('order' => 'Comment.created DESC', 'limit' => 10));
  5. }
  6. }

然后在页面元素中调用这个函数:

// views/elements/latest_comments.ctp

$comments = $this->requestAction('/comments/latest');
foreach($comments as $comment) {
    echo $comment['Comment']['title'];
}
  1. // views/elements/latest_comments.ctp
  2. $comments = $this->requestAction('/comments/latest');
  3. foreach($comments as $comment) {
  4. echo $comment['Comment']['title'];
  5. }

这样我们就可以把这个页面元素放到任何我们想要输出最近的评论的地方:

echo $this->element('latest_comments');
  1. echo $this->element('latest_comments');

采用这种写法的话,不管该页面元素什么时候被渲染输出,都会产生一个给控制器的请求,然后数据被处理加工,返回给页面元素。不过我们前面说了这不适合用来处理未被缓存的页面输出。所以我们稍微修改一下,变成这样:

echo $this->element('latest_comments', array('cache'=>'+1 hour'));
  1. echo $this->element('latest_comments', array('cache'=>'+1 hour'));

于是,在缓存的有效期内,requestAction不会被重复地执行。

另外,requestAction现在也可以接收cake风格的URL:

echo $this->requestAction(array('controller' => 'articles', 'action' => 'featured'), array('return'));
  1. echo $this->requestAction(array('controller' => 'articles', 'action' => 'featured'), array('return'));

这就允许requestAction的调用可以利用Router::url增加一定的性能。这种URL的格式与HtmlHelper::link所使用的基本一样。一点不同的是,如果你在URL中使用命名参数,那么requestAction的URL数组必须把这些参数用'named'作为key包(wrap)起来。这是因为requestAction只把命名参数数组与Controller::params的成员数组进行合并而不会把key为'named'的参数数组替换。

echo $this->requestAction('/articles/featured/limit:3');
  1. echo $this->requestAction('/articles/featured/limit:3');

写成数组形式的话:

echo $this->requestAction(array('controller' => 'articles', 'action' => 'featured', 'named' => array('limit' => 3)));
  1. echo $this->requestAction(array('controller' => 'articles', 'action' => 'featured', 'named' => array('limit' => 3)));