上一次我说了一下怎么从ModelBase通过Sql parse将SQL拆分,也就是解析的过程,今天我详细的说一下。
首先,我们实现函数的链式操作,即如:$this->where()->table()->select()
我们知道,如果要实现这种函数链,需要return $this,所以我们要保证在调用select方法之前的所有函数都要返回$this,也就是这个对象,最简单的方法就是在每个方法的最后执行return $this,但是这样实在是一个麻烦的事情,我们都知道PHP有一系列的魔术方法可以实现非常强大的功能,今天我们就要用到其中的一个魔术方法:__call,这儿方法是调用了对象中一个不存在的方法时调用的,也就是系统在抛出错误之前进行最后一次挽救,如果有__call,那么就将决定权交给__call,否则直接抛出错误。
所以我们可以在ModelBase中定义一个__call方法,这个方法的内容如下:
public function __call($method,$args) {
if(in_array($method,array(
'where','field','distinct','table','order','group'
))) {
$this->_options[$method] = isset($args[0]) ? $args[0] : null;
return $this;
} else {
parent::__call($method,$args);
}
}
由于我们在Base中也定义了__call,所以如果我们在这个类中的__call中无法处理的话,我们需要将控制权交给Base。
由__call方法控制的方法是where,field,distinct,table,order,group,当然,这个不一定是全部的,我暂时就写这么几个,你们读了上次的文章应该知道这几个函数是处理什么内容了吧。
这个__call方法执行的也很简单,就是在$this->_options数组里面将调用的方法的参数存储起来,这样用来做什么呢,这是为了后面Sql parse调用的。
我们把处理SQL的这个类称为SqlParser,那么SqlParser现在最少有以下几个方法:
private function _where($where){}
private function _field($field) {}
private function _distinct($distinct) {}
private function _table($table) {}
private function _order($order) {}
private function _group($group) {}
可能你们会问为什么我要把这些方法设置为private呢,因为直接和ModelBase打交道的不是这些方法,假设我们把和ModelBase交互的这个方法称为execute,那么我们在ModelBase中只需要调用:
$this->_sqlParser->execute($this->_options);
这个调用过程应该在哪儿调用呢,应该在select函数执行的时候。
public function select() {
$this->_sqlParser->execute($this->_options);
$this->_db->prepare($this->_sqlParser->getSql());
$this->_db->execute($this->_sqlParser->getParams());
}
这个就是ModelBase的select方法的代码,它实际上调用了sqlParser的实例的execute方法,然后在通过SqlParser的getSql和getParams方法获取SQL和参数分别交给驱动类的prepare和execute执行。
那么现在ModelBase的代码就变成如下了:
<?php
class ModelBase extends Base {
protected $_db = null;
private $_sqlParser = null;
private $_options = array(
'where' => '',
'field' => '',
'distinct' => '',
'table' => '',
'order' => '',
'group' => ''
);
public function __construct() {
$this->_db = ConnectionManager::getConnection();
$this->_sqlParser = new SqlParser();
}
public function execute($sql,Array $arr) {
$this->_db->prepare($sql);
$this->_db->execute($arr);
}
public function getAll() {
return $this->_db->getAllByAssocArray();
}
public function __call($method,$args) {
if(in_array($method,array(
'where','field','distinct','table','order','group'
))) {
$this->_options[$method] = isset($args[0]) ? $args[0] : null;
return $this;
} else {
parent::__call($method,$args);
}
}
public function select() {
$this->_sqlParser->execute($this->_options);
$this->_db->prepare($this->_sqlParser->getSql());
$this->_db->execute($this->_sqlParser->getParams());
}
}
那么SqlParser的代码是怎么样的呢,是这样的:
<?php
class SqlParser extends Base {
private static $_templateSql = 'SELECT :distinct :field FROM :table WHERE :where :order :group';
private $_sql = '';
private $_params = array();
public function execute(Array $options) {
$this->_sql = str_replace(array(
':distinct',':field',':table',':where',':group',':order'
),array(
$this->_distinct($options['distinct']),
$this->_field($options['field']),
$this->_table($options['table']),
$this->_where($options['where']),
$this->_group($options['group']),
$this->_order($options['order'])
),self::$_templateSql);
}
public function getSql() {
return $this->_sql;
}
public function getParams() {
return $this->_params;
}
private function _distinct($distinct) {
return 'distinct';
}
private function _field($field) {
return 'field';
}
private function _table($table) {
return 'table';
}
private function _where($where) {
return 'where';
}
private function _group($group) {
return 'group';
}
private function _order($order) {
return 'order';
}
}
在execute方法中,只需要按照SQL的模板(在变量templateSql中定义的)替换就行,比如对于templateSql中的:distinct(这个变量是用来占位的),当调用了$this->_distinct之后就会将templateSql中的:distinct替换成为相应的字符串。
现在为了方便查看,对这几个比较重要的方法,我们都返回了一个字符串,这种后面SQL执行的时候肯定会报错,由于暂时我们没有写这些函数的具体实现,所以暂时就这样吧。
好了,也差不多就这样吧,下次再继续了!!