如前所述,模型层的一个角色是从多种存储中获取数据。 CakePHP 模型类带有很多功能,帮助你搜索这些数据,排序,分页并且进行过滤。你将要使用的很多功能集成于模型的 Model::find()
find(string $type = 'first', array $params = array())
Find 是所有模型数据检索功能的主力。 $type 可以是 'all', 'first', 'count', 'list', 'neighbors', 'threaded'或者任何自定义查找类型。 切记,$type 是区分大小写的。 使用大写字母(例如 All)将得不到期望的结果。
$params 用于向不同的 find 传递所有的参数,其默认有如下的键值 - 每一个都是可选的:
1 array( 2 'conditions' => array('Model.field' => $thisValue), //array of conditions 3 'recursive' => 1, //int 4 'fields' => array('Model.field1', 'DISTINCT Model.field2'), //array of field names 5 'order' => array('Model.created', 'Model.field3 DESC'), //string or array defining order 6 'group' => array('Model.field'), //fields to GROUP BY 7 'limit' => n, //int 8 'page' => n, //int 9 'offset' => n, //int 10 'callbacks' => true //other possible values are false, 'before', 'after' 11 )
也可以添加和使用其它的参数,提供给一些查找类型、行为以及你自己的模型方法。
find('first', $params) 返回一个结果,你可以在任何期望获得一个结果的情况下使用它。 下面是几个简单的(控制器代码)示例:
1 public function some_function() { 2 // ... 3 $semiRandomArticle = $this->Article->find('first'); 4 $lastCreated = $this->Article->find('first', array( 5 'order' => array('Article.created' => 'desc') 6 )); 7 $specificallyThisOne = $this->Article->find('first', array( 8 'conditions' => array('Article.id' => 1) 9 )); 10 // ... 11 }
在第一个示例中,没有向 find 传递任何参数 - 所以没有任何条件和排序。这种形式的 find('first') 调用返回的格式如下:
1 Array 2 ( 3 [ModelName] => Array 4 ( 5 [id] => 83 6 [field1] => value1 7 [field2] => value2 8 [field3] => value3 9 ) 10 11 [AssociatedModelName] => Array 12 ( 13 [id] => 1 14 [field1] => value1 15 [field2] => value2 16 [field3] => value3 17 ) 18 )
find(‘count’)
find('count', $params) 返回一个整数值。下面是几个简单的(控制器代码)示例:
1 public function some_function() { 2 // ... 3 $total = $this->Article->find('count'); 4 $pending = $this->Article->find('count', array( 5 'conditions' => array('Article.status' => 'pending') 6 )); 7 $authors = $this->Article->User->find('count'); 8 $publishedAuthors = $this->Article->find('count', array( 9 'fields' => 'DISTINCT Article.user_id', 10 'conditions' => array('Article.status !=' => 'pending') 11 )); 12 // ... 13 }
注解
不要向 find('count') 传递 fields 数组。你只能为 DISTINCE count 指定列(其它情况下,计数结果总是相同的 - 仅取决于条件)。
find('all', $params) 返回一个数组(可能有多个)结果。
实际上,它是全部 find() 的变体(包括分页)。下面是几个简单的(控制器代码)示例:
1 public function some_function() { 2 // ... 3 $allArticles = $this->Article->find('all'); 4 $pending = $this->Article->find('all', array( 5 'conditions' => array('Article.status' => 'pending') 6 )); 7 $allAuthors = $this->Article->User->find('all'); 8 $allPublishedAuthors = $this->Article->User->find('all', array( 9 'conditions' => array('Article.status !=' => 'pending') 10 )); 11 // ... 12 }
注解
上面的例子中, $allAuthors 将包含 users 表的每个用户。没有要应用的条件被传递给那个 find。
调用 find('all') 的结果格式如下:
1 Array 2 ( 3 [0] => Array 4 ( 5 [ModelName] => Array 6 ( 7 [id] => 83 8 [field1] => value1 9 [field2] => value2 10 [field3] => value3 11 ) 12 13 [AssociatedModelName] => Array 14 ( 15 [id] => 1 16 [field1] => value1 17 [field2] => value2 18 [field3] => value3 19 ) 20 21 ) 22 )
find(‘list’)
find('list', $params) 返回一个索引数组,用在想要一个用于类似 HTML 输入表单中的 select 元素所需的列表的场合。下面是几个简单的(控制器代码)示例:
1 public function some_function() { 2 // ... 3 $allArticles = $this->Article->find('list'); 4 $pending = $this->Article->find('list', array( 5 'conditions' => array('Article.status' => 'pending') 6 )); 7 $allAuthors = $this->Article->User->find('list'); 8 $allPublishedAuthors = $this->Article->find('list', array( 9 'fields' => array('User.id', 'User.name'), 10 'conditions' => array('Article.status !=' => 'pending'), 11 'recursive' => 0 12 )); 13 // ... 14 }
注解
上面的例子中, $allAuthors 将包含 users 表的每个用户。没有要应用的条件被传递给那个 find。
调用 find('list') 的结果格式如下:
1 Array 2 ( 3 //[id] => 'displayValue', 4 [1] => 'displayValue1', 5 [2] => 'displayValue2', 6 [4] => 'displayValue4', 7 [5] => 'displayValue5', 8 [6] => 'displayValue6', 9 [3] => 'displayValue3', 10 )
当调用 find('list') 时,传递的 fields 参数用于决定使用什么做数组的键、值和(可选的)结果的分组。默认情况下,模型的主键被当作键,显示列用作值(可以用模型的 displayField) 属性配置)
一些清晰的示例:
1 public function some_function() { 2 // ... 3 $justusernames = $this->Article->User->find('list', array( 4 'fields' => array('User.username') 5 )); 6 $usernameMap = $this->Article->User->find('list', array( 7 'fields' => array('User.username', 'User.first_name') 8 )); 9 $usernameGroups = $this->Article->User->find('list', array( 10 'fields' => array('User.username', 'User.first_name', 'User.group') 11 )); 12 // ... 13 }
在上面的例子中,结果变量类似下面这样:
1 $justusernames = Array 2 ( 3 //[id] => 'username', 4 [213] => 'AD7six', 5 [25] => '_psychic_', 6 [1] => 'PHPNut', 7 [2] => 'gwoo', 8 [400] => 'jperras', 9 ) 10 11 $usernameMap = Array 12 ( 13 //[username] => 'firstname', 14 ['AD7six'] => 'Andy', 15 ['_psychic_'] => 'John', 16 ['PHPNut'] => 'Larry', 17 ['gwoo'] => 'Gwoo', 18 ['jperras'] => 'Jo?l', 19 ) 20 21 $usernameGroups = Array 22 ( 23 ['User'] => Array 24 ( 25 ['PHPNut'] => 'Larry', 26 ['gwoo'] => 'Gwoo', 27 ) 28 29 ['Admin'] => Array 30 ( 31 ['_psychic_'] => 'John', 32 ['AD7six'] => 'Andy', 33 ['jperras'] => 'Jo?l', 34 ) 35 36 )
find(‘threaded’)
find('threaded', $params) 返回一个嵌套数组,如果你想使用模型数据的 parent_id 列建立相应的嵌套结果。下面是几个简单的(控制器代码)示例:
1 public function some_function() { 2 // ... 3 $allCategories = $this->Category->find('threaded'); 4 $comments = $this->Comment->find('threaded', array( 5 'conditions' => array('article_id' => 50) 6 )); 7 // ... 8 }
小技巧
处理嵌套数据的更好的方法是使用 树 行为
在上面的例子中,$allCategories 将包含一个呈现整个分类结构的嵌套数组。调用 find('threaded') 的结果格式如下:
1 Array 2 ( 3 [0] => Array 4 ( 5 [ModelName] => Array 6 ( 7 [id] => 83 8 [parent_id] => null 9 [field1] => value1 10 [field2] => value2 11 [field3] => value3 12 ) 13 14 [AssociatedModelName] => Array 15 ( 16 [id] => 1 17 [field1] => value1 18 [field2] => value2 19 [field3] => value3 20 ) 21 22 [children] => Array 23 ( 24 [0] => Array 25 ( 26 [ModelName] => Array 27 ( 28 [id] => 42 29 [parent_id] => 83 30 [field1] => value1 31 [field2] => value2 32 [field3] => value3 33 ) 34 35 [AssociatedModelName] => Array 36 ( 37 [id] => 2 38 [field1] => value1 39 [field2] => value2 40 [field3] => value3 41 ) 42 43 [children] => Array 44 ( 45 ) 46 ) 47 ... 48 ) 49 ) 50 )
结果呈现的顺序是可以改变的,因为它受 order 处理的影响。如果将 'order' => 'name ASC' 作为参数传递给find('threaded'),其结果将按 name 排序。类似于此的所有 order 都能被使用,此方法没有内置的首次返回的顶层结果的顺序。
警告
如果指定了 fields,就必须包含 parent_id (或者它的当前别名):
1 public function some_function() { 2 $categories = $this->Category->find('threaded', array( 3 'fields' => array('id', 'name', 'parent_id') 4 )); 5 }
否则,上面例子中返回的数组将不是预期的嵌套结构。
find('neighbors', $params) 执行与 ‘first’ 相同的查找,但返回的是所请求的前一行和后一行。下面是一个简单的(控制器代码)示例: :
1 public function some_function() { 2 $neighbors = $this->Article->find('neighbors', array('field' => 'id', 'value' => 3)); 3 }
本例中 $params 数组包含两个元素:field 和 value。所有的 find 中的其它元素仍然可用(例如:如果模型可包含,可以在 $params 指定 ‘包含’)。调用 find('neighbors') 的结果格式如下:
1 Array 2 ( 3 [prev] => Array 4 ( 5 [ModelName] => Array 6 ( 7 [id] => 2 8 [field1] => value1 9 [field2] => value2 10 ... 11 ) 12 [AssociatedModelName] => Array 13 ( 14 [id] => 151 15 [field1] => value1 16 [field2] => value2 17 ... 18 ) 19 ) 20 [next] => Array 21 ( 22 [ModelName] => Array 23 ( 24 [id] => 4 25 [field1] => value1 26 [field2] => value2 27 ... 28 ) 29 [AssociatedModelName] => Array 30 ( 31 [id] => 122 32 [field1] => value1 33 [field2] => value2 34 ... 35 ) 36 ) 37 )
注解
注意,结果总是只包含两个根元素: prev 和 next。此功能不兑现模型默认的递归变量。递归设置必须以参数形式传递给每个需要的调用。
find 方法很灵活,能够接受自定义查找,这是通过在模型变量中定义自己的类型并在模型类中实现特定的函数完成的。
模型的 find 类型是 find 选项的快捷方式。例如,如下两种查找是相同的:
1 $this->User->find('first');
1 $this->User->find('all', array('limit' => 1));
以下是内核中预定义的类型:
那么其它的类型呢?以在数据库中查找所有的发布文章为例。每一个改变是在模型中的 Model::$findMethods 变量中添加类型:
1 class Article extends AppModel { 2 public $findMethods = array('available' => true); 3 }
这是在通知 CakePHP 接受值 available 作为 find 函数的第一个参数。 第二步是实现 _findAvailable 函数。 这是一个约定,如果想实现一个叫做 myFancySearch 的查找就需要实现一个叫做 _findMyFancySearch 方法。
1 class Article extends AppModel { 2 public $findMethods = array('available' => true); 3 4 protected function _findAvailable($state, $query, $results = array()) { 5 if ($state == 'before') { 6 $query['conditions']['Article.published'] = true; 7 return $query; 8 } 9 return $results; 10 } 11 }
下面是完整的示例(控制器代码):
1 class ArticlesController extends AppController { 2 3 // Will find all published articles and order them by the created column 4 public function index() { 5 $articles = $this->Article->find('available', array( 6 'order' => array('created' => 'desc') 7 )); 8 } 9 10 }
上面展示的代码中特定的 _find[Type] 方法接收3个参数。第一个意指查询执行在什么处于状态时执行,可以是before 或 after。 这是因为此函数是这样一种回调函数:有能力在完成前编辑查询,或者在获取结果后对结果进行编辑。
通常第一件事是检查 find 函数的查询状态。 before 状态是编辑查询、绑定新的关联、应用更多的行为、解释传递给find 的第二个参数的那些特殊键的时候。此状态需要返回 $query 参数(修改或不修改)。
after 状态是检查结果、注入新数据、计算并以另一种格式返回它,或者在最近返回的数据上做任何你爱做的事。此状态需要返回 $result 数组(修改或不修改)。
可以创建任意多你喜欢的自定义查找,这也是在应用程序中跨越模型征用代码的好办法。
还可以通过如下类型的自定义对查找进行分页:
1 class ArticlesController extends AppController { 2 3 // 将对全部发表的文章进行分页 4 public function index() { 5 $this->paginate = array('available'); 6 $articles = $this->paginate(); 7 $this->set(compact('articles')); 8 } 9 10 }
像上面这样设置控制器中的 $this->paginate 属性将导致 find 的 type 变成 available,并且还允许你继续修改查找的结果。
如果分页计数出现错误,可能需要向 AppModel 添加如下代码,它可以纠正分页计数:
1 class AppModel extends Model { 2 3 /** 4 var cpro_id = "u6885494";