【原】TP中MongoDB的使用
in 备忘 with 0 comment

【原】TP中MongoDB的使用

in 备忘 with 0 comment

有理想的人,生活总是火热的。 --斯大林


由于项目需要,我必须得用MongoDB,很凑巧的是,ThinkPHP也支持Mongo,所以我义无反顾的直接上TP,但是当我真正用起来的时候发现并不是那么尽如人意,没办法,都开始用了,这时候换框架显然是不现实的,那就改吧!

手术前的思考

其实,TP自带的mongo驱动对于普通的增删改查是没有问题的,所以做网站已经足够了,那么它和Mysql的区别在哪里呢?影响比较大的有2个:建表和数据类型。

开始修改

TP自带了一个Mongo的实现类,在这个类里面,作者已经对mongo进行了适配让用户使用mongo的时候和使用Mysql基本上一模一样,所以各种束手束脚,更加做了很多兼容工作,然而,数据类型的问题并没有解决,所以我直接跳过官方的MongoModel我直接写了个BaseModel继承了Model,根据自己的需要做了些处理,代码不多,但是够用了。

<?php
namespace Home\Model;
use Think\Model;
/**
 * MongoModel模型类
 * 实现了ODM和ActiveRecords模式
 */
class BaseModel extends Model{

    // 主键名称
    protected $pk        =   '_id';
    protected $fieldRule =   [];
    protected $autoCheckFields = false;

    /**
     * 格式化数据
     * @param $data
     * @param $options
     * @return bool
     */
    protected function _before_insert(&$data,$options) {
        $oldData = $data;
        $data = [];
        foreach( $this->fieldRule as $key => $value ){
            if( isset($oldData[$key]) ){
                $type = gettype($value);
                switch ($type){
                    case 'integer':
                        $data[$key] = intval($oldData[$key]);
                        break;
                    case 'double':
                        $data[$key] = floatval($oldData[$key]);
                        break;
                    case 'string':
                        $data[$key] = strval($oldData[$key]);
                        break;
                }
            }else{
                $data[$key] = $value;
            }
        }
        return true;
    }

    /**
     * 格式化数据
     * @param $data
     * @param $options
     * @return bool
     */
    protected function _before_update(&$data,$options) {
        $oldData = $data;
        $data = [];
        foreach( $this->fieldRule as $key => $value ){
            if( isset($oldData[$key]) ){
                $type = gettype($value);
                switch ($type){
                    case 'integer':
                        $data[$key] = intval($oldData[$key]);
                        break;
                    case 'double':
                        $data[$key] = floatval($oldData[$key]);
                        break;
                    case 'string':
                        $data[$key] = strval($oldData[$key]);
                        break;
                }
            }
        }
        return true;
    }

    /**
     * 选择数据库
     */
    protected function _after_db() {
        $this->db->switchCollection($this->getTableName(),$this->dbName?$this->dbName:C('db_name'));
    }

    /**
     * 格式化_id
     * @param $options
     */
    protected function _options_filter(&$options) {
        $id = $this->getPk();
        if( !is_array($options['where'][$id]) ){
            if(isset($options['where'][$id]) && !empty($options['where'][$id]) && !($options['where'][$id] instanceof \MongoId)) {
                $options['where'][$id] = new \MongoId($options['where'][$id]);
            }
        }else{
            foreach( $options['where'][$id][1] as &$value ){
                if(isset($value) && !empty($value) && !($value instanceof \MongoId)) {
                    $value = new \MongoId($value);
                }
            }
        }


    }

    /**
     * 格式化结果_id
     * @param $resultSet
     * @param $options
     */
    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;
    }

    /**
     * 查询单条数据
     * @access public
     * @param mixed $options 表达式参数
     * @return mixed
     */
    public function find($options=array()) {
        $options =  $this->_parseOptions($options);
        $result = $this->db->find($options);

        if( false === $result ){
            return false;
        }elseif( empty( $result ) ){
            return null;
        }else{
            $this->checkMongoId( $result );
        }

        $this->data = $result;
        $this->_after_find( $this->data, $options );
        return $this->data;
    }

    /**
     * count统计 配合where连贯操作
     * @access public
     * @return integer
     */
    public function count(){
        // 分析表达式
        $options =  $this->_parseOptions();
        return $this->db->count($options);
    }

    /**
     * 得到完整的数据表名 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 .= $this->name;
            }
            $this->trueTableName = lcfirst($tableName);
        }
        return $this->trueTableName;
    }

    public function clear(){
        // 分析表达式
        $options =  $this->_parseOptions();
        return $this->db->clear($options);
    }

}

细心的朋友可能看到了我其实也是做了一部分兼容工作,只是多了数据类型转换的工作,上面的是BaseModel,本来Mongo是不需要建表的,但是要想规定字段类型,那就必须得要“建表”,既然数据库没提供功能,那么我只能用PHP实现喽。

<?php
/**
 * 权限组包含和用户之间的关系表
 * Author: 赵翔 <756958008@qq.com>
 * Date: 16/1/16
 */
namespace Home\Model;

class AuthGroupAccessModel extends BaseModel{

    protected $fieldRule = [];

    protected function _initialize(){
        $this->fieldRule = [
            'uid' => '',           //用户ID
            'groupId' => '',       //用户组ID
        ];
    }

}

所有的数据库操作必须使用D方法,实例化我想要的目标类,在这个模型中,我就完成了建表的过程,我规定了field的类型甚至我也可以设置字段默认值啦!

最后说两句,我个人认为“迁就”是一个很可怕的事情,我个人认为在一个项目中同时用这Mysql和Mongo的概率并不高,如果真的出现了,我们也可以写一个中间件去实现,没必要非要做到“平滑迁移”,当然能做到最好,不能就算了,我们要保证最根本的功能不能丢了。ThinkPHP官方,如果有幸看到这篇博文,希望大神们给次力!

Comments are closed.