回调方法

如果你需要在模型操作之前或之后要插入一些处理逻辑,请使用模型的回调(callback)函数。这些函数可以定义在模型类中(包括 AppModel)。一定要注意这些特殊函数各自的预期返回值。

当使用回调函数时,应该记住行为(behavior)的回调函数在模型的回调函数 之前 执行。

beforeFind

beforeFind(array $query)

该回调函数在任何与 find 相关的操作前被调用。传入该回调的 $query 参数包含当前查询的信息,比如:conditions、fields,等。

如果不希望开始该查询操作(可能基于相关的 $query 选项而做出的决定),请返回false。否则,返回可能修改过的 $query ,或者任何你想传递给 find 及其它类似方法的信息。

可以使用该回调方法基于用户的角色来限制其查询操作,或者根据当前负载作出缓存的决定。

afterFind

afterFind(array $results, boolean $primary = false)

使用此回调函数来修改从 find 操作返回的结果,或者执行任何其它 find 之后的逻辑。传入该回调的 $results 参数包含模型的 find 操作返回的结果,即,象这样:

  1. $results = array(
  2. 0 => array(
  3. 'ModelName' => array(
  4. 'field1' => 'value1',
  5. 'field2' => 'value2',
  6. ),
  7. ),
  8. );

该回调函数的返回值应该是触发该回调的 find 操作的(可能是经过修改的)结果。

$primary 参数表示当前模型是查询发起的模型,还是作为关联来查询的模型。如果模型是作为关联来查询的,$results 的格式会不同;不是通常从 find 操作得到的结果,会得到如下的内容:

  1. $results = array(
  2. 'field_1' => 'value1',
  3. 'field_2' => 'value2'
  4. );

警告

期望 $primary 为 true 的代码,如果使用递归 find,可能会遇到来自 PHP 的致命错误 "Cannot use string offset as an array"。

下面是如何使用 afterfind 回调来格式化日期的例子:

  1. public function afterFind($results, $primary = false) {
  2. foreach ($results as $key => $val) {
  3. if (isset($val['Event']['begindate'])) {
  4. $results[$key]['Event']['begindate'] = $this->dateFormatAfterFind(
  5. $val['Event']['begindate']
  6. );
  7. }
  8. }
  9. return $results;
  10. }
  11.  
  12. public function dateFormatAfterFind($dateString) {
  13. return date('d-m-Y', strtotime($dateString));
  14. }

beforeValidate

beforeValidate(array $options = array())

使用此回调函数来在验证之前修改模型数据,有必要也可以修改验证规则。这个函数也必须返回 true,否则当前的 save() 操作会终止。

afterValidate

afterValidate()

在检查数据的错误之后调用。如果需要,用该回调来执行任何数据的清理和准备。

beforeSave

beforeSave(array $options = array())

在此回调函数内放入任何保存之前的逻辑。这个函数会紧接着在验证数据成功之后、但在数据保存之前执行。如果想要保存操作成功,此函数也应该返回 true。

对于数据保存前需要进行的处理逻辑,该回调函数非常方便。如果存储引擎需要日期使用特定的格式,可以从 $this->data 访问并进行修改。

下面是个用 beforeSave 回调进行日期转换的例子。例子中的代码用于一个应用程序,其begindate 字段在数据库中的格式为 YYYY-MM-DD,而显示为格式 DD-MM-YYYY。当然这很容易改变。在适当的模型中使用下面的代码。

  1. public function beforeSave($options = array()) {
  2. if (!empty($this->data['Event']['begindate']) &&
  3. !empty($this->data['Event']['enddate'])
  4. ) {
  5.  
  6. $this->data['Event']['begindate'] = $this->dateFormatBeforeSave(
  7. $this->data['Event']['begindate']
  8. );
  9. $this->data['Event']['enddate'] = $this->dateFormatBeforeSave(
  10. $this->data['Event']['enddate']
  11. );
  12. }
  13. return true;
  14. }
  15.  
  16. public function dateFormatBeforeSave($dateString) {
  17. return date('Y-m-d', strtotime($dateString));
  18. }

小技巧

请确保 beforeSave() 回调返回 true,否则保存会失败。

afterSave

afterSave(boolean $created, array $options = array())

如果需要在每次保存操作后执行一些逻辑,可以将这些逻辑放在该回调方法中。保存的数据会在 $this->data 中。

如果是插入新记录(而不是更新记录),参数 $created 会为 true。

$options 数组参数就是传给 Model::save() 方法的同一个参数。

beforeDelete

beforeDelete(boolean $cascade = true)

在此回调函数内放置任何删除之前的逻辑。若要删除操作继续,此函数应该返回 true,要终止则返回 false。

如果依赖于该记录的记录也要删除,则参数 $cascade 会为 true

小技巧

请确保 beforeDelete() 回调返回 true,否则删除会失败。

  1. // 使用 app/Model/ProductCategory.php
  2. // 在下面的例子中,如果一个产品类别还包含产品,则不允许删除此类别。
  3. // 在 ProductsController.php 中调用 $this->Product->delete($id) 来设置
  4. // $this->id。
  5. // 假设 'ProductCategory hasMany Product',可以在模型中使用 $this->Product。
  6. public function beforeDelete($cascade = true) {
  7. $count = $this->Product->find("count", array(
  8. "conditions" => array("product_category_id" => $this->id)
  9. ));
  10. if ($count == 0) {
  11. return true;
  12. }
  13. return false;
  14. }

afterDelete

afterDelete()

在这个回调函数里放置每次删除之后要执行的逻辑。

  1. // 也许在从数据库中删除一条记录之后,也要删除相关联的文件
  2. public function afterDelete() {
  3. $file = new File($this->data['SomeModel']['file_path']);
  4. $file->delete();
  5. }

onError

onError()

任何问题发生时被调用。