检索数据
如前所述,模型层的一个角色是从多种存储中获取数据。 CakePHP 模型类带有很多功能,帮助你搜索这些数据,排序,分页并且进行过滤。你将要使用的很多功能集成于模型的 Model::find()
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’)
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’)
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’)
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 类型是 find 选项的快捷方式。例如,如下两种查找是相同的:
1 $this->User->find('first');
1 $this->User->find('all', array('limit' => 1));
以下是内核中预定义的类型:
- first
- all
- count
- list
- threaded
- neighbors
那么其它的类型呢?以在数据库中查找所有的发布文章为例。每一个改变是在模型中的 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 * 如果自定义查找的计数查询中的 'fields' 键是一个数组的话,就删除它, 5 * 因为它将彻底破坏 Model::_findCount() 调用 6 * 7 * @param string $state Either "before" or "after" 8 * @param array $query 9 * @param array $results 10 * @return int The number of records found, or false 11 * @access protected 12 * @see Model::find() 13 */ 14 protected function _findCount($state, $query, $results = array()) { 15 if ($state === 'before') { 16 if (isset($query['type']) && isset($this->findMethods[$query['type']])) { 17 $query = $this->{'_find' . ucfirst($query['type'])}('before', $query); 18 if (!empty($query['fields']) && is_array($query['fields'])) { 19 if (!preg_match('/^count/i', current($query['fields']))) { 20 unset($query['fields']); 21 } 22 } 23 } 24 } 25 return parent::_findCount($state, $query, $results); 26 } 27 28 } 29 ?>
在 2.2 版更改.
不再需要为纠正计数结果的错误而覆写 _findCount 了。 自定义查找的 'before' 状态现在将带有 $query[‘operation’] = ‘count’ 重新进行调用。返回的 $query 将用在 _findCount() 中。 如果有需要你可以通过检查 'operation' 键来辨别并返回不同的 $query:
1 protected function _findAvailable($state, $query, $results = array()) { 2 if ($state == 'before') { 3 $query['conditions']['Article.published'] = true; 4 if (!empty($query['operation']) && $query['operation'] == 'count') { 5 return $query; 6 } 7 $query['joins'] = array( 8 //array of required joins 9 ); 10 return $query; 11 } 12 return $results; 13 }
魔术查找类型
这些魔术函数是依据特定的列搜索表的快捷方式来使用的。只要向这些函数的末尾添加列名(按照驼峰命名格式), 并提供这些列的规则作为其第一个参数。
findAllBy() 函数返回类似于 find('all') 的返回格式的结果, 而 findBy 返回的格式与 find('first') 相同。
findAllBy
findAllBy<fieldName>(string $value, array $fields, array $order, int $limit, int $page, int $recursive)
findAllBy<x> 示例 | 相应的 SQL 片段 |
---|---|
$this->Product->findAllByOrderStatus('3'); | Product.order_status = 3 |
$this->Recipe->findAllByType('Cookie'); | Recipe.type = 'Cookie' |
$this->User->findAllByLastName('Anderson'); | User.last_name = 'Anderson' |
$this->Cake->findAllById(7); | Cake.id = 7 |
$this->User->findAllByEmailOrUsername('jhon'); | User.email = 'jhon' OR User.username ='jhon'; |
$this->User->findAllByUsernameAndPassword('jhon', '123'); | User.username = 'jhon' ANDUser.password = '123'; |
$this->User->findAllByLastName('psychic', array(),array('User.user_name => 'asc')); | User.last_name = 'psychic' ORDER BYUser.user_name ASC |
返回结果数组的格式类似于 find('all') 的返回值格式。
findBy
findBy<fieldName>(string $value);
findBy 魔术函数也接受一些可选参数:
findBy<fieldName>(string $value[, mixed $fields[, mixed $order]]);
findBy<x> 示例 | 相应的 SQL 片段 |
---|---|
$this->Product->findByOrderStatus('3'); | Product.order_status = 3 |
$this->Recipe->findByType('Cookie'); | Recipe.type = 'Cookie' |
$this->User->findByLastName('Anderson'); | User.last_name = 'Anderson'; |
$this->User->findByEmailOrUsername('jhon'); | User.email = 'jhon' OR User.username = 'jhon'; |
$this->User->findByUsernameAndPassword('jhon','123'); | User.username = 'jhon' AND User.password ='123'; |
$this->Cake->findById(7); | Cake.id = 7 |
findBy() 函数返回的结果类似于 find('first')。
Model::query()
query(string $query)
虽然很少有必要,但如果你不能或不想通过其它方法调用 SQL,就可以直接使用模型的 query() 方法。
如果你真想在应用程序中使用这种方法,请确保你已经阅读过 CakePHP 的 数据清洁,这有助于清理用户提供的数据,以防止注入和跨站点脚本攻击。
注解
query() 不理会 $Model->cacheQueries,因为其功能本质上与调用的模型不相关。为避免缓存调用查询,需要将第二个参数设置为 false,例如: query($query, $cachequeries = false)。
query() 在查询中使用表名作为返回数据的数组键,而不是模型名。例如:
1 $this->Picture->query("SELECT * FROM pictures LIMIT 2;");
可能返回如下数组:
1 Array 2 ( 3 [0] => Array 4 ( 5 [pictures] => Array 6 ( 7 [id] => 1304 8 [user_id] => 759 9 ) 10 ) 11 12 [1] => Array 13 ( 14 [pictures] => Array 15 ( 16 [id] => 1305 17 [user_id] => 759 18 ) 19 ) 20 )
要使用模型名作为数组键,以与 find 方法的返回结果一致,可以将查询写成:
1 $this->Picture->query("SELECT * FROM pictures AS Picture LIMIT 2;");
将返回:
1 Array 2 ( 3 [0] => Array 4 ( 5 [Picture] => Array 6 ( 7 [id] => 1304 8 [user_id] => 759 9 ) 10 ) 11 12 [1] => Array 13 ( 14 [Picture] => Array 15 ( 16 [id] => 1305 17 [user_id] => 759 18 ) 19 ) 20 )
注解
此语法及关联数组结构仅在 MySQL 中有效。在手动运行查询时,Cake 不提供任何抽象数据,所以其结果在不同的数据库中有所不同。
Model::field()
field(string $name, array $conditions = null, string $order = null)
返回值只有与 $order 指定的排序条件相匹配的第一条记录中由 $name 指定的单个列。如果没有传递条件并且模型设置了 id,将返回当前模型结果的列的值。如果没有匹配的记录,查找将返回 false。
1 $this->Post->id = 22; 2 echo $this->Post->field('name'); // echo the name for row id 22 3 4 echo $this->Post->field('name', array('created <' => date('Y-m-d H:i:s')), 'created DESC'); 5 // 显示最后创建的实例的 'name' 列。
Model::read()
read($fields, $id)
read() 是一个用于设置当前模型数据(Model::$data)的方法 – 例如在编辑过程中 – 但是不能用在从数据库中获取单条记录的其它情况中
$fields 用于传递单个列的名字,可以是字符串或者列名数组;如果为空(empty),则获取所有列。
$id 指定要读取的记录的 ID。默认是由 Model::$id 指定的当前选择的记录。 传递不同的值给 $id 将引起记录被选择。
read() 总是返回一个数组(即使仅包含了一个列名)。使用 field 来获取单个列的值。
警告
由于 read 方法覆写任何存储在模型的 data 和 id 属性中的任何信息,使用此功能是要非常小心,尤其在类似 beforeValidata 和 beforeSave 等模型回调函数中使用的时候。通常 find 方法比 read 方法提供了更强大和易用的 API。
复杂的查找条件
多数模型的 find 调用需要传递条件集。通常,CakePHP 使用数组来组合需要放在 SQL 查询的 WHERE 子句中的条件。
使用数组很整洁且易读,构建查询也非常容易。这种语法还打散了查询中的元素(列、值、操作,等等)。它使 CakePHP 有可能生成更高效的查询,确保 SQL 语法正确,正确分解查询的每个独立的部分。使用数组语法还使 CakePHP 能够确保查询避免 SQL 注入攻击。
基于数组的最基础的查询类似于:
1 $conditions = array("Post.title" => "This is a post", "Post.author_id" => 1); 2 // 带有模型的示例 3 $this->Post->find('first', array('conditions' => $conditions));
此结构非常简单:它将查找标题等于 “This is a post” 的帖子。注意,我们可以使用 “title” 作为列的名字,但是在构建查询时,最好总是指定模型名,因为它提高了代码的清晰度,有助于在你选择改变架构时防范冲突。
其它的匹配类型呢?同样简单。假设你要查找所有的 title 不是 This is a post 的帖子:
1 array("Post.title !=" => "This is a post")
注意,’!=’ 跟在列的名称之后。CakePHP 能解析任何有效的 SQL 比较操作符,包括使用 LIKE、BETWEEN、REGEX 的匹配表达式,只要你用空格分隔开列名和操作符。IN(...) 风格的匹配例外。假设你想查找 title 包含在给定的值集合之内的帖子:
1 array( 2 "Post.title" => array("First post", "Second post", "Third post") 3 )
要执行 NOT IN(...) 匹配查找 title 不在给定的值集之内的帖子:
1 array( 2 "NOT" => array("Post.title" => array("First post", "Second post", "Third post")) 3 )
为条件添加附加的过滤与向数组添加附加的键/值对一样简单:
1 array ( 2 "Post.title" => array("First post", "Second post", "Third post"), 3 "Post.created >" => date('Y-m-d', strtotime("-2 weeks")) 4 )
还可以创建对比数据库中两个列的查找:
1 array("Post.created = Post.modified")
上面的例子将返回创建时间和编辑时间相同的帖子(就是指那些从来没被编辑过的帖子)。
记住,如果你发现自己不能在一个方法中生成 WHERE 子句(例如 逻辑运算),你总能用字符串来指定它:
1 array( 2 'Model.field & 8 = 1', 3 // 其它常用条件 4 )
默认情况下,CakePHP 使用逻辑 AND 连接多个条件;也就是说,上面的代码片段仅匹配两星期前创建的并且标题与给定的集中的某一个匹配的帖子。但是我们也能很容易的找到符合任一条件的帖子:
1 array("OR" => array( 2 "Post.title" => array("First post", "Second post", "Third post"), 3 "Post.created >" => date('Y-m-d', strtotime("-2 weeks")) 4 ))
CakePHP 接受所有有效的 SQL 逻辑运算,包括 AND、OR、NOT、XOR 等等,而且不区分大小写。这些条件还能无限制嵌套。假设 Posts 和 Authors 间有 belongsTo 关系,想要找到所有包含特定关键词(”magic”)或者两星期前建立的,但仅限于由 Bob 发布的帖子:
1 array( 2 "Author.name" => "Bob", 3 "OR" => array( 4 "Post.title LIKE" => "%magic%", 5 "Post.created >" => date('Y-m-d', strtotime("-2 weeks")) 6 ) 7 )
如果需要在同一个列上设置多个条件,比如想要执行一个带有多个条款的 LIKE 搜索,可以使用类似如下的条件:
1 array('OR' => array( 2 array('Post.title LIKE' => '%one%'), 3 array('Post.title LIKE' => '%two%') 4 ))
Cake 不能检查 null 列。在本例中,查询将返回所有 title 不为 null 的记录:
1 array("NOT" => array( 2 "Post.title" => null 3 ) 4 )
要处理 BETWEEN 查询,可以使用如下条件:
1 array('Post.read_count BETWEEN ? AND ?' => array(1,10))
注解
CakePHP 将引用的数字值依赖于 DB 的列类型。
GROUP BY?:
1 array( 2 'fields' => array( 3 'Product.type', 4 'MIN(Product.price) as price' 5 ), 6 'group' => 'Product.type' 7 )
所返回的值格式如下:
1 Array 2 ( 3 [0] => Array 4 ( 5 [Product] => Array 6 ( 7 [type] => Clothing 8 ) 9 [0] => Array 10 ( 11 [price] => 32 12 ) 13 ) 14 [1] => Array 15 ...
下面是执行 DISTINCT 查询的简单示例。可以按类似格式使用其它操作符,例如 MIN()、MAX(),等等:
1 array( 2 'fields' => array('DISTINCT (User.name) AS my_column_name'), 3 'order' = >array('User.id DESC') 4 )
通过嵌套多个条件数组,可以构建非常复杂的条件:
1 array( 2 'OR' => array( 3 array('Company.name' => 'Future Holdings'), 4 array('Company.city' => 'CA') 5 ), 6 'AND' => array( 7 array( 8 'OR' => array( 9 array('Company.status' => 'active'), 10 'NOT' => array( 11 array('Company.status' => array('inactive', 'suspended')) 12 ) 13 ) 14 ) 15 ) 16 )
其对应的 SQL 查询为:
1 SELECT `Company`.`id`, `Company`.`name`, 2 `Company`.`description`, `Company`.`location`, 3 `Company`.`created`, `Company`.`status`, `Company`.`size` 4 5 FROM 6 `companies` AS `Company` 7 WHERE 8 ((`Company`.`name` = 'Future Holdings') 9 OR 10 (`Company`.`name` = 'Steel Mega Works')) 11 AND 12 ((`Company`.`status` = 'active') 13 OR (NOT (`Company`.`status` IN ('inactive', 'suspended'))))
子查询
下面的示例假定我们有一个带有 “id”,”name” 和 “status” 列的 “users” 表。 status 可以是 “A”、”B”、”C”。并且我们想使用子查询获取所有 status 不同于 “B” 的用户。
为了达到此目的,我们将获取模型数据源,向其发出请求以建立查询,就像我们正在调用 find 方法,只不过返回的是一条 SQL 语句。然后,我们生成一个表达式并将其添加到条件数组中:
1 $conditionsSubQuery['"User2"."status"'] = 'B'; 2 3 $db = $this->User->getDataSource(); 4 $subQuery = $db->buildStatement( 5 array( 6 'fields' => array('"User2"."id"'), 7 'table' => $db->fullTableName($this->User), 8 'alias' => 'User2', 9 'limit' => null, 10 'offset' => null, 11 'joins' => array(), 12 'conditions' => $conditionsSubQuery, 13 'order' => null, 14 'group' => null 15 ), 16 $this->User 17 ); 18 $subQuery = ' "User"."id" NOT IN (' . $subQuery . ') '; 19 $subQueryExpression = $db->expression($subQuery); 20 21 $conditions[] = $subQueryExpression; 22 23 $this->User->find('all', compact('conditions'));
生成的 SQL 查询为:
1 SELECT 2 "User"."id" AS "User__id", 3 "User"."name" AS "User__name", 4 "User"."status" AS "User__status" 5 FROM 6 "users" AS "User" 7 WHERE 8 "User"."id" NOT IN ( 9 SELECT 10 "User2"."id" 11 FROM 12 "users" AS "User2" 13 WHERE 14 "User2"."status" = 'B' 15 )
另外,如果你需要传递上面的原始 SQL 查询的一部分,带有原始 SQL 的数据源**表达式**为查询的任意部分工作。
预处理语句
如果需要对查询有更多控制,可以使用预处理语句。它允许你直接与数据库驱动对话,并且传递任何你需要的自定义查询:
1 $db = $this->getDataSource(); 2 $db->fetchAll( 3 'SELECT * from users where username = ? AND password = ?', 4 array('jhon', '12345') 5 ); 6 $db->fetchAll( 7 'SELECT * from users where username = :username AND password = :password', 8 array('username' => 'jhon','password' => '12345') 9 );