MongoModel.class.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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. /**
  12. * MongoModel模型类
  13. * 实现了ODM和ActiveRecords模式
  14. * @category Extend
  15. * @package Extend
  16. * @subpackage Model
  17. * @author liu21st <liu21st@gmail.com>
  18. */
  19. class MongoModel extends Model{
  20. // 主键类型
  21. const TYPE_OBJECT = 1;
  22. const TYPE_INT = 2;
  23. const TYPE_STRING = 3;
  24. // 主键名称
  25. protected $pk = '_id';
  26. // _id 类型 1 Object 采用MongoId对象 2 Int 整形 支持自动增长 3 String 字符串Hash
  27. protected $_idType = self::TYPE_OBJECT;
  28. // 主键是否自动增长 支持Int型主键
  29. protected $_autoInc = false;
  30. // Mongo默认关闭字段检测 可以动态追加字段
  31. protected $autoCheckFields = false;
  32. // 链操作方法列表
  33. protected $methods = array('table','order','auto','filter','validate');
  34. /**
  35. * 利用__call方法实现一些特殊的Model方法
  36. * @access public
  37. * @param string $method 方法名称
  38. * @param array $args 调用参数
  39. * @return mixed
  40. */
  41. public function __call($method,$args) {
  42. if(in_array(strtolower($method),$this->methods,true)) {
  43. // 连贯操作的实现
  44. $this->options[strtolower($method)] = $args[0];
  45. return $this;
  46. }elseif(strtolower(substr($method,0,5))=='getby') {
  47. // 根据某个字段获取记录
  48. $field = parse_name(substr($method,5));
  49. $where[$field] =$args[0];
  50. return $this->where($where)->find();
  51. }elseif(strtolower(substr($method,0,10))=='getfieldby') {
  52. // 根据某个字段获取记录的某个值
  53. $name = parse_name(substr($method,10));
  54. $where[$name] =$args[0];
  55. return $this->where($where)->getField($args[1]);
  56. }else{
  57. throw_exception(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
  58. return;
  59. }
  60. }
  61. /**
  62. * 获取字段信息并缓存 主键和自增信息直接配置
  63. * @access public
  64. * @return void
  65. */
  66. public function flush() {
  67. // 缓存不存在则查询数据表信息
  68. $fields = $this->db->getFields();
  69. if(!$fields) { // 暂时没有数据无法获取字段信息 下次查询
  70. return false;
  71. }
  72. $this->fields = array_keys($fields);
  73. $this->fields['_pk'] = $this->pk;
  74. $this->fields['_autoinc'] = $this->_autoInc;
  75. foreach ($fields as $key=>$val){
  76. // 记录字段类型
  77. $type[$key] = $val['type'];
  78. }
  79. // 记录字段类型信息
  80. if(C('DB_FIELDTYPE_CHECK')) $this->fields['_type'] = $type;
  81. // 2008-3-7 增加缓存开关控制
  82. if(C('DB_FIELDS_CACHE')){
  83. // 永久缓存数据表信息
  84. $db = $this->dbName?$this->dbName:C('DB_NAME');
  85. F('_fields/'.$db.'.'.$this->name,$this->fields);
  86. }
  87. }
  88. // 写入数据前的回调方法 包括新增和更新
  89. protected function _before_write(&$data) {
  90. $pk = $this->getPk();
  91. // 根据主键类型处理主键数据
  92. if(isset($data[$pk]) && $this->_idType == self::TYPE_OBJECT) {
  93. $data[$pk] = new MongoId($data[$pk]);
  94. }
  95. }
  96. /**
  97. * count统计 配合where连贯操作
  98. * @access public
  99. * @return integer
  100. */
  101. public function count(){
  102. // 分析表达式
  103. $options = $this->_parseOptions();
  104. return $this->db->count($options);
  105. }
  106. /**
  107. * 获取下一ID 用于自动增长型
  108. * @access public
  109. * @param string $pk 字段名 默认为主键
  110. * @return mixed
  111. */
  112. public function getMongoNextId($pk=''){
  113. if(empty($pk)) {
  114. $pk = $this->getPk();
  115. }
  116. return $this->db->mongo_next_id($pk);
  117. }
  118. // 插入数据前的回调方法
  119. protected function _before_insert(&$data,$options) {
  120. // 写入数据到数据库
  121. if($this->_autoInc && $this->_idType== self::TYPE_INT) { // 主键自动增长
  122. $pk = $this->getPk();
  123. if(!isset($data[$pk])) {
  124. $data[$pk] = $this->db->mongo_next_id($pk);
  125. }
  126. }
  127. }
  128. public function clear(){
  129. return $this->db->clear();
  130. }
  131. // 查询成功后的回调方法
  132. protected function _after_select(&$resultSet,$options) {
  133. array_walk($resultSet,array($this,'checkMongoId'));
  134. }
  135. /**
  136. * 获取MongoId
  137. * @access protected
  138. * @param array $result 返回数据
  139. * @return array
  140. */
  141. protected function checkMongoId(&$result){
  142. if(is_object($result['_id'])) {
  143. $result['_id'] = $result['_id']->__toString();
  144. }
  145. return $result;
  146. }
  147. // 表达式过滤回调方法
  148. protected function _options_filter(&$options) {
  149. $id = $this->getPk();
  150. if(isset($options['where'][$id]) && is_scalar($options['where'][$id]) && $this->_idType== self::TYPE_OBJECT) {
  151. $options['where'][$id] = new MongoId($options['where'][$id]);
  152. }
  153. }
  154. /**
  155. * 查询数据
  156. * @access public
  157. * @param mixed $options 表达式参数
  158. * @return mixed
  159. */
  160. public function find($options=array()) {
  161. if( is_numeric($options) || is_string($options)) {
  162. $id = $this->getPk();
  163. $where[$id] = $options;
  164. $options = array();
  165. $options['where'] = $where;
  166. }
  167. // 分析表达式
  168. $options = $this->_parseOptions($options);
  169. $result = $this->db->find($options);
  170. if(false === $result) {
  171. return false;
  172. }
  173. if(empty($result)) {// 查询结果为空
  174. return null;
  175. }else{
  176. $this->checkMongoId($result);
  177. }
  178. $this->data = $result;
  179. $this->_after_find($this->data,$options);
  180. return $this->data;
  181. }
  182. /**
  183. * 字段值增长
  184. * @access public
  185. * @param string $field 字段名
  186. * @param integer $step 增长值
  187. * @return boolean
  188. */
  189. public function setInc($field,$step=1) {
  190. return $this->setField($field,array('inc',$step));
  191. }
  192. /**
  193. * 字段值减少
  194. * @access public
  195. * @param string $field 字段名
  196. * @param integer $step 减少值
  197. * @return boolean
  198. */
  199. public function setDec($field,$step=1) {
  200. return $this->setField($field,array('inc','-'.$step));
  201. }
  202. /**
  203. * 获取一条记录的某个字段值
  204. * @access public
  205. * @param string $field 字段名
  206. * @param string $spea 字段数据间隔符号
  207. * @return mixed
  208. */
  209. public function getField($field,$sepa=null) {
  210. $options['field'] = $field;
  211. $options = $this->_parseOptions($options);
  212. if(strpos($field,',')) { // 多字段
  213. if(is_numeric($sepa)) {// 限定数量
  214. $options['limit'] = $sepa;
  215. $sepa = null;// 重置为null 返回数组
  216. }
  217. $resultSet = $this->db->select($options);
  218. if(!empty($resultSet)) {
  219. $_field = explode(',', $field);
  220. $field = array_keys($resultSet[0]);
  221. $key = array_shift($field);
  222. $key2 = array_shift($field);
  223. $cols = array();
  224. $count = count($_field);
  225. foreach ($resultSet as $result){
  226. $name = $result[$key];
  227. if(2==$count) {
  228. $cols[$name] = $result[$key2];
  229. }else{
  230. $cols[$name] = is_null($sepa)?$result:implode($sepa,$result);
  231. }
  232. }
  233. return $cols;
  234. }
  235. }else{
  236. // 返回数据个数
  237. if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据
  238. $options['limit'] = is_numeric($sepa)?$sepa:1;
  239. } // 查找一条记录
  240. $result = $this->db->find($options);
  241. if(!empty($result)) {
  242. if(1==$options['limit']) return reset($result[0]);
  243. foreach ($result as $val){
  244. $array[] = $val[$field];
  245. }
  246. return $array;
  247. }
  248. }
  249. return null;
  250. }
  251. /**
  252. * 执行Mongo指令
  253. * @access public
  254. * @param array $command 指令
  255. * @return mixed
  256. */
  257. public function command($command) {
  258. return $this->db->command($command);
  259. }
  260. /**
  261. * 执行MongoCode
  262. * @access public
  263. * @param string $code MongoCode
  264. * @param array $args 参数
  265. * @return mixed
  266. */
  267. public function mongoCode($code,$args=array()) {
  268. return $this->db->execute($code,$args);
  269. }
  270. // 数据库切换后回调方法
  271. protected function _after_db() {
  272. // 切换Collection
  273. $this->db->switchCollection($this->getTableName(),$this->dbName?$this->dbName:C('db_name'));
  274. }
  275. /**
  276. * 得到完整的数据表名 Mongo表名不带dbName
  277. * @access public
  278. * @return string
  279. */
  280. public function getTableName() {
  281. if(empty($this->trueTableName)) {
  282. $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : '';
  283. if(!empty($this->tableName)) {
  284. $tableName .= $this->tableName;
  285. }else{
  286. $tableName .= parse_name($this->name);
  287. }
  288. $this->trueTableName = strtolower($tableName);
  289. }
  290. return $this->trueTableName;
  291. }
  292. }