MongoModel.class.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2010 http://topthink.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <liu21st@gmail.com>
  10. // +----------------------------------------------------------------------
  11. namespace Think\Model;
  12. use Think\Model;
  13. /**
  14. * MongoModel模型类
  15. * 实现了ODM和ActiveRecords模式
  16. */
  17. class MongoModel extends Model{
  18. // 主键类型
  19. const TYPE_OBJECT = 1;
  20. const TYPE_INT = 2;
  21. const TYPE_STRING = 3;
  22. // 主键名称
  23. protected $pk = '_id';
  24. // _id 类型 1 Object 采用MongoId对象 2 Int 整形 支持自动增长 3 String 字符串Hash
  25. protected $_idType = self::TYPE_OBJECT;
  26. // 主键是否自增
  27. protected $_autoinc = true;
  28. // Mongo默认关闭字段检测 可以动态追加字段
  29. protected $autoCheckFields = false;
  30. // 链操作方法列表
  31. protected $methods = array('table','order','auto','filter','validate');
  32. /**
  33. * 利用__call方法实现一些特殊的Model方法
  34. * @access public
  35. * @param string $method 方法名称
  36. * @param array $args 调用参数
  37. * @return mixed
  38. */
  39. public function __call($method,$args) {
  40. if(in_array(strtolower($method),$this->methods,true)) {
  41. // 连贯操作的实现
  42. $this->options[strtolower($method)] = $args[0];
  43. return $this;
  44. }elseif(strtolower(substr($method,0,5))=='getby') {
  45. // 根据某个字段获取记录
  46. $field = parse_name(substr($method,5));
  47. $where[$field] =$args[0];
  48. return $this->where($where)->find();
  49. }elseif(strtolower(substr($method,0,10))=='getfieldby') {
  50. // 根据某个字段获取记录的某个值
  51. $name = parse_name(substr($method,10));
  52. $where[$name] =$args[0];
  53. return $this->where($where)->getField($args[1]);
  54. }else{
  55. E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
  56. return;
  57. }
  58. }
  59. /**
  60. * 获取字段信息并缓存 主键和自增信息直接配置
  61. * @access public
  62. * @return void
  63. */
  64. public function flush() {
  65. // 缓存不存在则查询数据表信息
  66. $fields = $this->db->getFields();
  67. if(!$fields) { // 暂时没有数据无法获取字段信息 下次查询
  68. return false;
  69. }
  70. $this->fields = array_keys($fields);
  71. foreach ($fields as $key=>$val){
  72. // 记录字段类型
  73. $type[$key] = $val['type'];
  74. }
  75. // 记录字段类型信息
  76. if(C('DB_FIELDTYPE_CHECK')) $this->fields['_type'] = $type;
  77. // 2008-3-7 增加缓存开关控制
  78. if(C('DB_FIELDS_CACHE')){
  79. // 永久缓存数据表信息
  80. $db = $this->dbName?$this->dbName:C('DB_NAME');
  81. F('_fields/'.$db.'.'.$this->name,$this->fields);
  82. }
  83. }
  84. // 写入数据前的回调方法 包括新增和更新
  85. protected function _before_write(&$data) {
  86. $pk = $this->getPk();
  87. // 根据主键类型处理主键数据
  88. if(isset($data[$pk]) && $this->_idType == self::TYPE_OBJECT) {
  89. $data[$pk] = new \MongoId($data[$pk]);
  90. }
  91. }
  92. /**
  93. * count统计 配合where连贯操作
  94. * @access public
  95. * @return integer
  96. */
  97. public function count(){
  98. // 分析表达式
  99. $options = $this->_parseOptions();
  100. return $this->db->count($options);
  101. }
  102. /**
  103. * 获取唯一值
  104. * @access public
  105. * @return array | false
  106. */
  107. public function distinct($field, $where=array() ){
  108. // 分析表达式
  109. $this->options = $this->_parseOptions();
  110. $this->options['where'] = array_merge((array)$this->options['where'], $where);
  111. $command = array(
  112. "distinct" => $this->options['table'],
  113. "key" => $field,
  114. "query" => $this->options['where']
  115. );
  116. $result = $this->command($command);
  117. return isset($result['values']) ? $result['values'] : false;
  118. }
  119. /**
  120. * 获取下一ID 用于自动增长型
  121. * @access public
  122. * @param string $pk 字段名 默认为主键
  123. * @return mixed
  124. */
  125. public function getMongoNextId($pk=''){
  126. if(empty($pk)) {
  127. $pk = $this->getPk();
  128. }
  129. return $this->db->getMongoNextId($pk);
  130. }
  131. /**
  132. * 新增数据
  133. * @access public
  134. * @param mixed $data 数据
  135. * @param array $options 表达式
  136. * @param boolean $replace 是否replace
  137. * @return mixed
  138. */
  139. public function add($data='',$options=array(),$replace=false) {
  140. if(empty($data)) {
  141. // 没有传递数据,获取当前数据对象的值
  142. if(!empty($this->data)) {
  143. $data = $this->data;
  144. // 重置数据
  145. $this->data = array();
  146. }else{
  147. $this->error = L('_DATA_TYPE_INVALID_');
  148. return false;
  149. }
  150. }
  151. // 分析表达式
  152. $options = $this->_parseOptions($options);
  153. // 数据处理
  154. $data = $this->_facade($data);
  155. if(false === $this->_before_insert($data,$options)) {
  156. return false;
  157. }
  158. // 写入数据到数据库
  159. $result = $this->db->insert($data,$options,$replace);
  160. if(false !== $result ) {
  161. $this->_after_insert($data,$options);
  162. if(isset($data[$this->getPk()])){
  163. return $data[$this->getPk()];
  164. }
  165. }
  166. return $result;
  167. }
  168. // 插入数据前的回调方法
  169. protected function _before_insert(&$data,$options) {
  170. // 写入数据到数据库
  171. if($this->_autoinc && $this->_idType== self::TYPE_INT) { // 主键自动增长
  172. $pk = $this->getPk();
  173. if(!isset($data[$pk])) {
  174. $data[$pk] = $this->db->getMongoNextId($pk);
  175. }
  176. }
  177. }
  178. public function clear(){
  179. return $this->db->clear();
  180. }
  181. // 查询成功后的回调方法
  182. protected function _after_select(&$resultSet,$options) {
  183. array_walk($resultSet,array($this,'checkMongoId'));
  184. }
  185. /**
  186. * 获取MongoId
  187. * @access protected
  188. * @param array $result 返回数据
  189. * @return array
  190. */
  191. protected function checkMongoId(&$result){
  192. if(is_object($result['_id'])) {
  193. $result['_id'] = $result['_id']->__toString();
  194. }
  195. return $result;
  196. }
  197. // 表达式过滤回调方法
  198. protected function _options_filter(&$options) {
  199. $id = $this->getPk();
  200. if(isset($options['where'][$id]) && is_scalar($options['where'][$id]) && $this->_idType== self::TYPE_OBJECT) {
  201. $options['where'][$id] = new \MongoId($options['where'][$id]);
  202. }
  203. }
  204. /**
  205. * 查询数据
  206. * @access public
  207. * @param mixed $options 表达式参数
  208. * @return mixed
  209. */
  210. public function find($options=array()) {
  211. if( is_numeric($options) || is_string($options)) {
  212. $id = $this->getPk();
  213. $where[$id] = $options;
  214. $options = array();
  215. $options['where'] = $where;
  216. }
  217. // 分析表达式
  218. $options = $this->_parseOptions($options);
  219. $result = $this->db->find($options);
  220. if(false === $result) {
  221. return false;
  222. }
  223. if(empty($result)) {// 查询结果为空
  224. return null;
  225. }else{
  226. $this->checkMongoId($result);
  227. }
  228. $this->data = $result;
  229. $this->_after_find($this->data,$options);
  230. return $this->data;
  231. }
  232. /**
  233. * 字段值增长
  234. * @access public
  235. * @param string $field 字段名
  236. * @param integer $step 增长值
  237. * @return boolean
  238. */
  239. public function setInc($field,$step=1) {
  240. return $this->setField($field,array('inc',$step));
  241. }
  242. /**
  243. * 字段值减少
  244. * @access public
  245. * @param string $field 字段名
  246. * @param integer $step 减少值
  247. * @return boolean
  248. */
  249. public function setDec($field,$step=1) {
  250. return $this->setField($field,array('inc','-'.$step));
  251. }
  252. /**
  253. * 获取一条记录的某个字段值
  254. * @access public
  255. * @param string $field 字段名
  256. * @param string $spea 字段数据间隔符号
  257. * @return mixed
  258. */
  259. public function getField($field,$sepa=null) {
  260. $options['field'] = $field;
  261. $options = $this->_parseOptions($options);
  262. if(strpos($field,',')) { // 多字段
  263. if(is_numeric($sepa)) {// 限定数量
  264. $options['limit'] = $sepa;
  265. $sepa = null;// 重置为null 返回数组
  266. }
  267. $resultSet = $this->db->select($options);
  268. if(!empty($resultSet)) {
  269. $_field = explode(',', $field);
  270. $field = array_keys($resultSet[0]);
  271. $key = array_shift($field);
  272. $key2 = array_shift($field);
  273. $cols = array();
  274. $count = count($_field);
  275. foreach ($resultSet as $result){
  276. $name = $result[$key];
  277. if(2==$count) {
  278. $cols[$name] = $result[$key2];
  279. }else{
  280. $cols[$name] = is_null($sepa)?$result:implode($sepa,$result);
  281. }
  282. }
  283. return $cols;
  284. }
  285. }else{
  286. // 返回数据个数
  287. if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据
  288. $options['limit'] = is_numeric($sepa)?$sepa:1;
  289. } // 查找符合的记录
  290. $result = $this->db->select($options);
  291. if(!empty($result)) {
  292. if(1==$options['limit']) {
  293. $result = reset($result);
  294. return $result[$field];
  295. }
  296. foreach ($result as $val){
  297. $array[] = $val[$field];
  298. }
  299. return $array;
  300. }
  301. }
  302. return null;
  303. }
  304. /**
  305. * 执行Mongo指令
  306. * @access public
  307. * @param array $command 指令
  308. * @return mixed
  309. */
  310. public function command($command, $options=array()) {
  311. $options = $this->_parseOptions($options);
  312. return $this->db->command($command, $options);
  313. }
  314. /**
  315. * 执行MongoCode
  316. * @access public
  317. * @param string $code MongoCode
  318. * @param array $args 参数
  319. * @return mixed
  320. */
  321. public function mongoCode($code,$args=array()) {
  322. return $this->db->execute($code,$args);
  323. }
  324. // 数据库切换后回调方法
  325. protected function _after_db() {
  326. // 切换Collection
  327. $this->db->switchCollection($this->getTableName(),$this->dbName?$this->dbName:C('db_name'));
  328. }
  329. /**
  330. * 得到完整的数据表名 Mongo表名不带dbName
  331. * @access public
  332. * @return string
  333. */
  334. public function getTableName() {
  335. if(empty($this->trueTableName)) {
  336. $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : '';
  337. if(!empty($this->tableName)) {
  338. $tableName .= $this->tableName;
  339. }else{
  340. $tableName .= parse_name($this->name);
  341. }
  342. $this->trueTableName = strtolower($tableName);
  343. }
  344. return $this->trueTableName;
  345. }
  346. /**
  347. * 分组查询
  348. * @access public
  349. * @return string
  350. */
  351. public function group($key, $init, $reduce, $option=array()) {
  352. $option = $this->_parseOptions($option);
  353. //合并查询条件
  354. if(isset($option['where']))
  355. $option['condition'] = array_merge((array)$option['condition'], $option['where']);
  356. return $this->db->group($key, $init, $reduce, $option);
  357. }
  358. /**
  359. * 返回Mongo运行错误信息
  360. * @access public
  361. * @return json
  362. */
  363. public function getLastError(){
  364. return $this->db->command(array('getLastError'=>1));
  365. }
  366. /**
  367. * 返回指定集合的统计信息,包括数据大小、已分配的存储空间和索引的大小
  368. * @access public
  369. * @return json
  370. */
  371. public function status(){
  372. $option = $this->_parseOptions();
  373. return $this->db->command(array('collStats'=>$option['table']));
  374. }
  375. /**
  376. * 取得当前数据库的对象
  377. * @access public
  378. * @return object
  379. */
  380. public function getDB(){
  381. return $this->db->getDB();
  382. }
  383. /**
  384. * 取得集合对象,可以进行创建索引等查询
  385. * @access public
  386. * @return object
  387. */
  388. public function getCollection(){
  389. return $this->db->getCollection();
  390. }
  391. }