上一次我说了一下怎么从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执行的时候肯定会报错,由于暂时我们没有写这些函数的具体实现,所以暂时就这样吧。
好了,也差不多就这样吧,下次再继续了!!