123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- <?php
- // +----------------------------------------------------------------------
- // | ThinkPHP [ WE CAN DO IT JUST THINK ]
- // +----------------------------------------------------------------------
- // | Copyright (c) 2010 http://topthink.com All rights reserved.
- // +----------------------------------------------------------------------
- // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
- // +----------------------------------------------------------------------
- // | Author: liu21st <liu21st@gmail.com>
- // +----------------------------------------------------------------------
- namespace Think\Model;
- use Think\Model;
- /**
- * MongoModel模型类
- * 实现了ODM和ActiveRecords模式
- */
- class MongoModel extends Model{
- // 主键类型
- const TYPE_OBJECT = 1;
- const TYPE_INT = 2;
- const TYPE_STRING = 3;
- // 主键名称
- protected $pk = '_id';
- // _id 类型 1 Object 采用MongoId对象 2 Int 整形 支持自动增长 3 String 字符串Hash
- protected $_idType = self::TYPE_OBJECT;
- // 主键是否自增
- protected $_autoinc = true;
- // Mongo默认关闭字段检测 可以动态追加字段
- protected $autoCheckFields = false;
- // 链操作方法列表
- protected $methods = array('table','order','auto','filter','validate');
- /**
- * 利用__call方法实现一些特殊的Model方法
- * @access public
- * @param string $method 方法名称
- * @param array $args 调用参数
- * @return mixed
- */
- public function __call($method,$args) {
- if(in_array(strtolower($method),$this->methods,true)) {
- // 连贯操作的实现
- $this->options[strtolower($method)] = $args[0];
- return $this;
- }elseif(strtolower(substr($method,0,5))=='getby') {
- // 根据某个字段获取记录
- $field = parse_name(substr($method,5));
- $where[$field] =$args[0];
- return $this->where($where)->find();
- }elseif(strtolower(substr($method,0,10))=='getfieldby') {
- // 根据某个字段获取记录的某个值
- $name = parse_name(substr($method,10));
- $where[$name] =$args[0];
- return $this->where($where)->getField($args[1]);
- }else{
- E(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_'));
- return;
- }
- }
- /**
- * 获取字段信息并缓存 主键和自增信息直接配置
- * @access public
- * @return void
- */
- public function flush() {
- // 缓存不存在则查询数据表信息
- $fields = $this->db->getFields();
- if(!$fields) { // 暂时没有数据无法获取字段信息 下次查询
- return false;
- }
- $this->fields = array_keys($fields);
- foreach ($fields as $key=>$val){
- // 记录字段类型
- $type[$key] = $val['type'];
- }
- // 记录字段类型信息
- if(C('DB_FIELDTYPE_CHECK')) $this->fields['_type'] = $type;
- // 2008-3-7 增加缓存开关控制
- if(C('DB_FIELDS_CACHE')){
- // 永久缓存数据表信息
- $db = $this->dbName?$this->dbName:C('DB_NAME');
- F('_fields/'.$db.'.'.$this->name,$this->fields);
- }
- }
- // 写入数据前的回调方法 包括新增和更新
- protected function _before_write(&$data) {
- $pk = $this->getPk();
- // 根据主键类型处理主键数据
- if(isset($data[$pk]) && $this->_idType == self::TYPE_OBJECT) {
- $data[$pk] = new \MongoId($data[$pk]);
- }
- }
- /**
- * count统计 配合where连贯操作
- * @access public
- * @return integer
- */
- public function count(){
- // 分析表达式
- $options = $this->_parseOptions();
- return $this->db->count($options);
- }
- /**
- * 获取唯一值
- * @access public
- * @return array | false
- */
- public function distinct($field, $where=array() ){
- // 分析表达式
- $this->options = $this->_parseOptions();
- $this->options['where'] = array_merge((array)$this->options['where'], $where);
- $command = array(
- "distinct" => $this->options['table'],
- "key" => $field,
- "query" => $this->options['where']
- );
- $result = $this->command($command);
- return isset($result['values']) ? $result['values'] : false;
- }
- /**
- * 获取下一ID 用于自动增长型
- * @access public
- * @param string $pk 字段名 默认为主键
- * @return mixed
- */
- public function getMongoNextId($pk=''){
- if(empty($pk)) {
- $pk = $this->getPk();
- }
- return $this->db->getMongoNextId($pk);
- }
- /**
- * 新增数据
- * @access public
- * @param mixed $data 数据
- * @param array $options 表达式
- * @param boolean $replace 是否replace
- * @return mixed
- */
- public function add($data='',$options=array(),$replace=false) {
- if(empty($data)) {
- // 没有传递数据,获取当前数据对象的值
- if(!empty($this->data)) {
- $data = $this->data;
- // 重置数据
- $this->data = array();
- }else{
- $this->error = L('_DATA_TYPE_INVALID_');
- return false;
- }
- }
- // 分析表达式
- $options = $this->_parseOptions($options);
- // 数据处理
- $data = $this->_facade($data);
- if(false === $this->_before_insert($data,$options)) {
- return false;
- }
- // 写入数据到数据库
- $result = $this->db->insert($data,$options,$replace);
- if(false !== $result ) {
- $this->_after_insert($data,$options);
- if(isset($data[$this->getPk()])){
- return $data[$this->getPk()];
- }
- }
- return $result;
- }
- // 插入数据前的回调方法
- protected function _before_insert(&$data,$options) {
- // 写入数据到数据库
- if($this->_autoinc && $this->_idType== self::TYPE_INT) { // 主键自动增长
- $pk = $this->getPk();
- if(!isset($data[$pk])) {
- $data[$pk] = $this->db->getMongoNextId($pk);
- }
- }
- }
- public function clear(){
- return $this->db->clear();
- }
- // 查询成功后的回调方法
- protected function _after_select(&$resultSet,$options) {
- array_walk($resultSet,array($this,'checkMongoId'));
- }
- /**
- * 获取MongoId
- * @access protected
- * @param array $result 返回数据
- * @return array
- */
- protected function checkMongoId(&$result){
- if(is_object($result['_id'])) {
- $result['_id'] = $result['_id']->__toString();
- }
- return $result;
- }
- // 表达式过滤回调方法
- protected function _options_filter(&$options) {
- $id = $this->getPk();
- if(isset($options['where'][$id]) && is_scalar($options['where'][$id]) && $this->_idType== self::TYPE_OBJECT) {
- $options['where'][$id] = new \MongoId($options['where'][$id]);
- }
- }
- /**
- * 查询数据
- * @access public
- * @param mixed $options 表达式参数
- * @return mixed
- */
- public function find($options=array()) {
- if( is_numeric($options) || is_string($options)) {
- $id = $this->getPk();
- $where[$id] = $options;
- $options = array();
- $options['where'] = $where;
- }
- // 分析表达式
- $options = $this->_parseOptions($options);
- $result = $this->db->find($options);
- if(false === $result) {
- return false;
- }
- if(empty($result)) {// 查询结果为空
- return null;
- }else{
- $this->checkMongoId($result);
- }
- $this->data = $result;
- $this->_after_find($this->data,$options);
- return $this->data;
- }
- /**
- * 字段值增长
- * @access public
- * @param string $field 字段名
- * @param integer $step 增长值
- * @return boolean
- */
- public function setInc($field,$step=1) {
- return $this->setField($field,array('inc',$step));
- }
- /**
- * 字段值减少
- * @access public
- * @param string $field 字段名
- * @param integer $step 减少值
- * @return boolean
- */
- public function setDec($field,$step=1) {
- return $this->setField($field,array('inc','-'.$step));
- }
- /**
- * 获取一条记录的某个字段值
- * @access public
- * @param string $field 字段名
- * @param string $spea 字段数据间隔符号
- * @return mixed
- */
- public function getField($field,$sepa=null) {
- $options['field'] = $field;
- $options = $this->_parseOptions($options);
- if(strpos($field,',')) { // 多字段
- if(is_numeric($sepa)) {// 限定数量
- $options['limit'] = $sepa;
- $sepa = null;// 重置为null 返回数组
- }
- $resultSet = $this->db->select($options);
- if(!empty($resultSet)) {
- $_field = explode(',', $field);
- $field = array_keys($resultSet[0]);
- $key = array_shift($field);
- $key2 = array_shift($field);
- $cols = array();
- $count = count($_field);
- foreach ($resultSet as $result){
- $name = $result[$key];
- if(2==$count) {
- $cols[$name] = $result[$key2];
- }else{
- $cols[$name] = is_null($sepa)?$result:implode($sepa,$result);
- }
- }
- return $cols;
- }
- }else{
- // 返回数据个数
- if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据
- $options['limit'] = is_numeric($sepa)?$sepa:1;
- } // 查找符合的记录
- $result = $this->db->select($options);
- if(!empty($result)) {
- if(1==$options['limit']) {
- $result = reset($result);
- return $result[$field];
- }
- foreach ($result as $val){
- $array[] = $val[$field];
- }
- return $array;
- }
- }
- return null;
- }
- /**
- * 执行Mongo指令
- * @access public
- * @param array $command 指令
- * @return mixed
- */
- public function command($command, $options=array()) {
- $options = $this->_parseOptions($options);
- return $this->db->command($command, $options);
- }
- /**
- * 执行MongoCode
- * @access public
- * @param string $code MongoCode
- * @param array $args 参数
- * @return mixed
- */
- public function mongoCode($code,$args=array()) {
- return $this->db->execute($code,$args);
- }
- // 数据库切换后回调方法
- protected function _after_db() {
- // 切换Collection
- $this->db->switchCollection($this->getTableName(),$this->dbName?$this->dbName:C('db_name'));
- }
- /**
- * 得到完整的数据表名 Mongo表名不带dbName
- * @access public
- * @return string
- */
- public function getTableName() {
- if(empty($this->trueTableName)) {
- $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : '';
- if(!empty($this->tableName)) {
- $tableName .= $this->tableName;
- }else{
- $tableName .= parse_name($this->name);
- }
- $this->trueTableName = strtolower($tableName);
- }
- return $this->trueTableName;
- }
- /**
- * 分组查询
- * @access public
- * @return string
- */
- public function group($key, $init, $reduce, $option=array()) {
- $option = $this->_parseOptions($option);
- //合并查询条件
- if(isset($option['where']))
- $option['condition'] = array_merge((array)$option['condition'], $option['where']);
- return $this->db->group($key, $init, $reduce, $option);
- }
- /**
- * 返回Mongo运行错误信息
- * @access public
- * @return json
- */
- public function getLastError(){
- return $this->db->command(array('getLastError'=>1));
- }
- /**
- * 返回指定集合的统计信息,包括数据大小、已分配的存储空间和索引的大小
- * @access public
- * @return json
- */
- public function status(){
- $option = $this->_parseOptions();
- return $this->db->command(array('collStats'=>$option['table']));
- }
-
- /**
- * 取得当前数据库的对象
- * @access public
- * @return object
- */
- public function getDB(){
- return $this->db->getDB();
- }
-
- /**
- * 取得集合对象,可以进行创建索引等查询
- * @access public
- * @return object
- */
- public function getCollection(){
- return $this->db->getCollection();
- }
- }
|