关联查询是关系型数据库的典型查询语句。根据两个或多个表中列之间的关系,从这些表中查询数据。SQL标准使用JOIN和ON关键字来实现关系查询。Join子句join子句的构造并不难,注意事项就是关联查询的注意事项:写语法和关联条件,使用table.field方式,防止字段被重命名。在基类中新建一个join()方法://$table要关联的表//$one是一个表的字段作为关联条件//$two是另一个表的字段作为关联条件//$type关联模式inner,left,rightpublicfunctionjoin($table,$one,$two,$type='INNER'){//判断模式是否有效if(!in_array($type,['INNER','LEFT','RIGHT'])){thrownew\InvalidArgumentException("错误连接模式");}//构建连接子句字符串$this->_join_str.=''.$type.'加入'.self::_wrapRow($table)。'ON'.self::_wrapRow($one)。'='.self::_wrapRow($two);返回$this;}leftJoin()和rightJoin()方法:publicfunctionleftJoin($table,$one,$two){return$this->join($table,$one,$two,'LEFT');}publicfunctionrightJoin($table,$one,$two){return$this->join($table,$one,$two,'RIGHT');}注意:Sqlite不支持右连接,所以rightJoin()方法在Sqlite驱动程序类中无效。BuildSELECTstudent.name,class.nameFROMstudentINNERJOINclassONstudent.class_id=class.id;:$results=$driver->table('student')->select('student.name','class.name')->join('class','student.class_id','class.id')->get();表前缀为什么要有表前缀以前在一个数据库中放很多数据表的时候,需要表前缀来区分功能。虽然现在这种情况很少见,但是对于查询构建者来说,还是有必要提供一个方便的方法来设置表前缀,尤其是当你没有权限修改表名的时候。自动添加表前缀的方法对于有表前缀的表,我们不希望每次都写一个前缀,这样会导致应用层更改前缀后进行修改。所以我们将表前缀作为配置参数传递给查询构建器,并在查询构建器的底部自动添加前缀。表前缀配置,假设表前缀为'test_'://以mysql为例$config=['host'=>'localhost','port'=>'3306','user'=>'username','password'=>'password','dbname'=>'dbname','charset'=>'utf8','prefix'=>'test_','timezone'=>'+8:00','collection'=>'utf8_general_ci','strict'=>false,//'unix_socket'=>'/var/run/mysqld/mysqld.sock',];$db=新的Mysql($config);addprefixautomaticallymethod:protectedfunction_wrapTable($table){//构造函数传入的配置中是否有前缀参数?$prefix=array_key_exists('prefix',$this->_config)?$this->_config['前缀']:'';//拼接前缀return$prefix.$table;}modifytable()method:publicfunctiontable($table){//自动添加前缀$this->_table=self::_wrapRow($this->_wrapTable($table));return$this;}join子句也涉及表,所以修改join()方法:publicfunctionjoin($table,$one,$two,$type='INNER'){if(!in_array($type,['内','左','RIGHT'])){thrownew\InvalidArgumentException("错误连接模式");}//添加表前缀$table=$this->_wrapTable($table);$this->_join_str.=''.$type.'加入'.self::_wrapRow($table)。'ON'.self::_wrapRow($one).'='.self::_wrapRow($two);return$this;}table.field添加表前缀添加表前缀后,我们会发现一个问题:使用table()和join()方法传入的表可以自动添加前缀,但是表中的表。字段格式不能自动加前缀,如上join('class','student.class_id','class.id'),我们不能写join('class','test_student.class_id','test_class.id')每次(这种情况下和全部手动添加前缀没什么区别),你必须想办法自动添加前缀来观察table.field模式,它出现在哪里是不确定的,它可能出现在某个列或者任何子句,因此不必在固定位置添加前缀Likely。那我们反过来想一想。如果SQL已经构造但没有执行,我们已经知道这种格式用在什么地方了,我们可以一一替换。那么你怎么知道这种格式用在什么地方呢?使用正则表达式,我们使用正则表达式来查找table.field的表部分,只是给表加一个表前缀(这里不考虑跨库查询三点的情况)。向基类添加了_wrapPrepareSql()方法://Replacetable.fieldwithprefixtable.fieldprotectedfunction_wrapPrepareSql(){$quote=static::$_quote_symbol;$prefix_pattern='/'.$quote.'([a-zA-Z0-9_]+)'.$quote.'(\.)'.$quote.'([a-zA-Z0-9_]+)'.$引用。'/';$prefix_replace=self::_quote($this->_wrapTable('$1')).'$2'.self::_quote('$3');$this->_prepare_sql=preg_replace($prefix_pattern,$prefix_replace,$this->_prepare_sql);}修改_execute()方法:protectedfunction_execute(){try{//table.field方式添加表前缀$this->_wrapPrepareSql();$this->_pdoSt=$this->_pdo->prepare($this->_prepare_sql);$this->_bindParams();$this->_pdoSt->execute();$this->_reset();}catch(PDOException$e){if($this->_isTimeout($e)){$this->_closeConnection();$this->_connect();try{//table.field模式添加表前缀$this->_wrapPrepareSql();$this->_pdoSt=$this->_pdo->prepare($this->_prepare_sql);$this->_bindParams();$this->_pdoSt->execute();$this->_reset();}catch(PDOException$e){抛出$e;}}else{抛出$e;}}}最后我们进行了一次完整的测试:require_oncedirname(dirname(__FILE__))。'/vendor/autoload.php';useDrivers\Mysql;$config=['host'=>'localhost','port'=>'3306','user'=>'username','password'=>'password','dbname'=>'database','prefix'=>'test_','charset'=>'utf8','timezone'=>'+8:00','collection'=>'utf8_general_ci','strict'=>false,];$driver=newMysql($config);$results=$driver->table('student')->select('student.name','class.name')->join('班级','student.class_id','class.id')->get();var_dump($results);试试吧!复杂语句的构建已经走到了现在的地步,查询相关的SQL构建方法也基本有了。让我们执行一些复杂的SQL构造。注意:这只是我的测试环境的一个例子。你可以根据自己的想法建表。选择*FROMt_userWHEREusername='Jackieaa'OR(NOTEXISTS(SELECT*FROMt_userWHEREusername='Jackieaa')AND(username='JackieConroy'ORusername='JammieHaag'))ANDg_idIN(SELECTidFROMt_user_group)ORDERBYidDESCLIMIT1OFFSET0:$results=$driver->table('user')->where('username','Jackieaa')->orWhereBrackets(function($query){$query->whereNotExists(function($query){$query->table('user')->where('username','Jackieaa');})->WhereBrackets(function($query){$query->where('username','JackieConroy')->orWhere('username','JammieHaag');});})->whereInSub('g_id',function($query){$query->table('user_group')->select('id');})->orderBy('id','DESC')->limit(0,1)->get();构造语言SELECTt_user.username,t_user_group.groupnameFROMt_userLEFTJOINt_user_groupONt_user.g_id=t_user_group.idWHEREusername='Jackieaa'OR(NOTEXISTS(SELECT*FROMt_userWHEREusername='Jackieaa')ANDusername='JackieConroy'):$results=$driver->table('user')->select('user.username','user_group.groupname')->leftJoin('user_group','user.g_id','user_group.id')->where('user.username','Jackieaa')->orWhereBrackets(function($query){$query->whereNotExists(function($query){$query->table('user')->where('username','Jackieaa');})->where('user.username','JackieConroy');})->get();更多例子参考WorkerF元测试-PDODQL
