前几天我讲到在SqlParser中完成SQL的解析,我已经讲了distinct,field,table,group,order等的简单实现,那么怎么实现where呢,没有where,这个SQL一直还是不能运行的。
对于where,我们可以想一下,允许用户输入的参数如:
$this->where('uid > 1'),$this->where(array('uid' => array('gt' , 1)))
这几种形式,首先第一种,就是直接将这段SQL通过字符串传递过来,这种在where函数中只需要拼接一个WHERE子串即可。
对于传递的参数是数组的情况就比较复杂了,首先,对于最外层的array,它的形式应该是如下的:
array(条件,条件,条件....)
对于具体的某一个条件,它也是一个数组,如:
字段=> array(比较符,数值,连接符)
对于最简单的形式,如$this->where(array('uid' => array('gt',1)))它实际上被解析之后的SQL为:WHERE uid > 1,如果条件为多个:
$this->where(array('uid' => array('gt' , 1)),'password' => array('neq' , 'test')),它代表的意思是:WHERE uid > 1 AND password != 'test',我们没有写条件与条件之间的连接符,应该框架自己会默认有一个连接符,如AND。
由于对于符号:eq,neq,gt,gte等,我们需要将它解析成为如:=,!=,>,>=等,所以我们首先要定义一个函数来处理这个:
我们称这个函数为:_parseCompExp,这个函数的实现为:
private function _parseCompExp($exp) { if(is_string($exp)) { return str_replace(array_keys($this->_compExp),array_values($this->_compExp),$exp); }elseif(is_array($exp)) { $tmpArr = array(); foreach($exp as $val) { $tmpArr[] = str_replace(array_keys($this->_compExp),array_values($this->_compExp),$val); } return $tmpArr; } else { //不能解析,直接返回 return $exp; } }
为了扩展,我们不会将需要解析的参数写死,而是直接做成一个private的变量,如:
private $_compExp = array( 'neq' => '!=', 'gte' => '>=', 'lte' => '<=', 'notlike' => 'NOT LIKE', 'gt' => '>', 'lt' => '<', 'eq' => '=', 'like' => 'LIKE' );这个变量指定了每一个字符串代表的实际意义。
具体的where的实现是怎么样的呢,我刚才写了一个实现,虽然看着很恶心:
private function _where($where) { if(empty($where)) { return ''; } if(is_string($where)) { return ' WHERE ' . $where; }elseif(is_array($where)) { $str = ' WHERE '; $arrLen = count($where); $count = 0; foreach($where as $key => $val) { $needJoinStr = true; $joinStr = ' AND '; if(preg_match('/^lt|lte|gt|gte|eq|neq|like|notlike$/i',$val[0])) { $command = $this->_parseCompExp($val[0]); $str .= (' ' . $key . ' ' . $command . ' ' . $val[1]); } elseif('in' === strtolower($val[0])) { if(is_array($val[1])) { $str .= (' ( ' . $key . ' IN ' . implode(',',$val[1]) . ')'); } else { $str .= (' ( ' . $key . ' IN ' . $val[1] . ' ) '); } } elseif('between' === strtolower($val[0])) { $data = explode(',',$val[1]); $str .= (' ' . $key . ' BETWEEN ' . $data[0] . ' AND ' . $data[1] . ' '); } else {} if($count + 1 < $arrLen) { if(isset($val[2])) { $str .= (' ' . $val[2] . ' '); } else { $str .= ' AND '; } } $count ++; } return $str; } else {} }由于这种比较符有这样几种:
第一种:>,=,<等,这种后面跟的数值只有一个,如 > 1,这种的解析是比较简单的,只要通过parseCompExp函数转换一下,然后字符串拼接即可。
第二种:IN,IN后面跟的参数个数不一定,所以需要判定后面传递的是数组还是字符串,如果是数组,那么需要通过,来explode成为字符串,如果是字符串,那就直接拼接即可。
第三种,Between,后面跟的参数肯定是两个, 由于我为了简单,所以指定了第二个参数为数值,第三个参数为连接符,所以只有在第二个参数中设置数组来解决这个问题,如: 'uid' => array('between',array(2,4))它代表的意思就是:uid between 2 and 4。
对于其他情况,暂不解析。
再后面就是判定一下是否需要连接符,如果需要连接符,并且设置了数组的第三个元素,那么直接拼接,否则拼接AND。
现在我们可以在模型中使用了,使用如下:
<?php class TestModel extends ModelBase { public function test() { $this->distinct()->where()->field(array( 'id' => 'uid','user' ))->table(array( 'user' => 'TEST','limits' ))->group()->order(array( 'test','test2' => 'asc' )) ->where(array( 'id' => array('gt',1) ))->select(); } }
当然,这种实现是不完善的,反正先这样吧!!!
本来一个完整的SQL不只是我们实现的这几个函数,为了简便,我就不讲后面复杂的内容了。
对于其他框架实现的如:1对多,多对1这种多表查询,你们可以自己想一下怎么实现。
后面我就粗略讲一下DbTable了,DbRelation我就不讲了,有兴趣去实现的童鞋可以自己试一下。