有理想的人,生活总是火热的。 --斯大林
由于项目需要,我必须得用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官方,如果有幸看到这篇博文,希望大神们给次力!
本文由 陌上花开 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Jul 1, 2016 at 04:06 am