把一条基本的SQL语句(不包括子句)抽象成一个类,这里的SQL类由select的`fields`, from的`table`, join`table`on`condition`, where的`condtion`, order by的`field desc, field asc`, limit `offset, length` 六部分组成(可支持扩展 `group by`\`having`\union等)
代码目录如下:
/index.php demo代码
/autoload.php 加载类文件
/sour/Condition.php 条件类
/sour/HopeSQL.php SQL构造器实例
/sour/SQLCreator.php SQL构造器抽象类
/sour/StringHelper.php 字符串帮助类
/autoload.php
<?php
defined('PATH') or define('PATH', __DIR__);
function auto_loader($class)
{
$path = str_replace(
'\\',
DIRECTORY_SEPARATOR,
substr($class, strpos($class, '\\', 1)+1)
);
if(file_exists(PATH.DIRECTORY_SEPARATOR.$path.'.php')) {
require PATH.DIRECTORY_SEPARATOR.$path.'.php';
} else {
throw new Exception(sprintf('not found `%s`', $class));
}
return class_exists($class);
}
/sour/Condition.php
<?php
namespace builder\sour;
use \Exception;
define('DEFAULT_RIGHT_TYPE', 0);
define('RIGHT_TYPE_INT', 0);
define('RIGHT_TYPE_STRING', 1);
define('RIGHT_TYPE_ARRAY_INT', 2);
define('RIGHT_TYPE_ARRAY_STRING', 3);
define('RIGHT_TYPE_COLUMN', 4);
define('RIGHT_TYPE_OBJ', 5);
define('OPERATOR_EQ', '=');
define('OPERATOR_NOT_EQ', '<>');
define('OPERATOR_LT', '>');
define('OPERATOR_RT', '<');
define('OPERATOR_LE', '<=');
define('OPERATOR_RE', '>=');
define('OPERATOR_IN', 'in');
define('OPERATOR_NOT_IN', 'not in');
define('OPERATOR_NOT_LIKE', 'like');
class Condition
{
private $operator;
private $left;
private $right;
private $rightType;
public function __construct($left, $right, $operator = OPERATOR_EQ, $rightType = DEFAULT_RIGHT_TYPE)
{
$this->left = $left;
$this->right = $right;
$this->operator = $operator;
$this->rightType = $rightType;
}
public function build()
{
if(false !== $this->rightType) {
switch ($this->rightType) {
case RIGHT_TYPE_INT:
$this->right = intval($this->right);
break;
case RIGHT_TYPE_STRING:
$this->right = '\''.addslashes($this->right).'\'';
break;
case RIGHT_TYPE_ARRAY_INT:
$this->right = implode(', ', $this->right);
break;
case RIGHT_TYPE_ARRAY_STRING:
$this->right = '\''.implode('\', \'', $this->right).'\'';
break;
case RIGHT_TYPE_OBJ:
return [
'field' => $this->right,
'operator' => $this->operator,
'obj' => $this->right
];
break;
case RIGHT_TYPE_COLUMN:
break;
default:
throw new Exception('error right type');
}
}
switch ($this->operator) {
case OPERATOR_EQ:
case OPERATOR_NOT_EQ:
case OPERATOR_LT:
case OPERATOR_RT:
case OPERATOR_LE:
case OPERATOR_RE:
case OPERATOR_NOT_LIKE:
case RIGHT_TYPE_COLUMN:
$condition = implode(' ', [
$this->left,
$this->operator,
$this->right
]);
break;
case OPERATOR_IN:
case OPERATOR_NOT_IN:
$condition = implode(' ', [
$this->left,
$this->operator,
'('.$this->right.')'
]);
break;
default:
$condition = '1 = 1';
}
return $condition;
}
}
/sour/StringHelper.php
<?php
namespace builder\sour;
class StringHelper
{
public static function joinField($alias, $field)
{
return $alias.'.'.$field;
}
}
/sour/SQLCreator.php
<?php
namespace builder\sour;
define('SQL_FIELD', 'field');
define('SQL_TABLE', 'table');
define('SQL_WHERE', 'where');
define('SQL_LIMIT', 'limit');
define('SQL_ORDER_BY', 'order_by');
define('SQL_GROUP_BY', 'group_by');
define('SQL_HAVING', 'having');
abstract class SQLCreator
{
public $table; //表名
public $alias; //表别名
public $params; //参数
public $lastSQL;
public function __construct($table, $alias, $params)
{
$this->table = $table;
$this->alias = $alias;
$this->params = $params;
}
public abstract function build(); //根据规则生成SQL的方法
public abstract function query($SQL); //原生查询方法
public abstract function execute($SQL); //原生执行方法
public abstract function explain();
}
/sour/HopeSQL.php
<?php
namespace builder\sour;
define('SQL_DEFAULT_ALIAS', '');
define('SQL_DEFAULT_PARAMS', []);
define('JOIN_CONTENT_NULL', null);
define('WHERE_OR', 1);
define('WHERE_AND', 2);
define('CONDITION_ABS_CLASS', 'builder\sour\Condition');
define('DEFAULT_ORDER_BY', 'ASC');
use \design\builder\sour\Condition;
use \Exception;
class HopeSQL extends SQLCreator
{
private $joinIndex = 0;
public function __construct($table, $alias = SQL_DEFAULT_ALIAS, $params = SQL_DEFAULT_PARAMS)
{
if(!$table) {
throw new Exception('table name can not empty!');
}
if(!$alias) {
$alias = $table;
}
parent::__construct($table, $alias, $params);
}
public function select($params)
{
foreach ($params as $param) {
$this->params['fields'][$param] = $param;
}
return $this;
}
public function join($alias, $content = JOIN_CONTENT_NULL)
{
$this->joinIndex++;
$this->params['join'][$this->joinIndex][$alias] = $content;
return $this;
}
public function on(array $conditions, $class = CONDITION_ABS_CLASS) {
foreach ($conditions as $condition) {
if(!($condition instanceof $class)) {
throw new Exception(sprintf('condition must is `%s`', $class));
}
$this->params['on'][$this->joinIndex][] = $condition;
}
return $this;
}
public function whereAnd($conditions, $class = CONDITION_ABS_CLASS)
{
foreach ($conditions as $condition) {
if(!($condition instanceof $class)) {
throw new Exception(sprintf('condition must is `%s`', $class));
}
$this->params['where'][] = [WHERE_AND => $condition];
}
return $this;
}
public function whereOr($conditions, $class = CONDITION_ABS_CLASS)
{
foreach ($conditions as $condition) {
if(!($condition instanceof $class)) {
throw new Exception(sprintf('condition must is `%s`', $class));
}
$this->params['where'][] = [WHERE_OR => $condition];
}
return $this;
}
public function whereAll(array $conditions, $class = CONDITION_ABS_CLASS)
{
foreach ($conditions as $condition) {
$key = key($condition);
$condition = current($condition);
if(!($condition instanceof $class)) {
throw new Exception(sprintf('condition must is `%s`', $class));
}
$this->params['where'][] = [$key => $condition];
}
return $this;
}
public function orderBy($field, $desc = DEFAULT_ORDER_BY)
{
$this->params['order_by'][] = $field.' '.$desc;
return $this;
}
public function limit($offset, $length)
{
$this->params['offset'] = $offset;
$this->params['length'] = $length;
return $this;
}
public function get($name)
{
if(isset($this->params['fields'][$name])) {
return StringHelper::joinField($this->alias, $this->params['fields'][$name]);
}
throw new Exception();
}
public function getAlias()
{
return $this->alias;
}
public function build()
{
$fields = '*';
if(isset($this->params['fields']) && !empty($this->params['fields'])) {
$fields = [];
foreach ($this->params['fields'] as $field) {
$fields[] = StringHelper::joinField($this->alias, $field);
}
$fields = implode(', ', $fields);
}
$join = '';
if(isset($this->params['join']) && !empty($this->params['join'])) {
$joins = [];
foreach ($this->params['join'] as $index => $content) {
$item = 'join ';
$key = key($content);
$content = current($content);
if($content instanceof $this) {
$item .= '('.$content->build().')'.' as '.$key;
} else {
$item .= $key.' as '.$key;
}
if(!empty($this->params['on'][$index])) {
$item .= ' on 1 = 1';
foreach ($this->params['on'][$index] as $condition) {
$varCondition = $condition->build();
if(is_array($varCondition)) {
if($varCondition['obj'] instanceof $this) {
$item .= ' AND '.$varCondition['field'].$varCondition['operator'].'('.$varCondition['obj']->build().')';
} else {
throw new Exception(sprintf('don\'t export class `%s`', get_class($varCondition['obj'])));
}
} else {
$item .= ' AND '.$varCondition;
}
}
}
$joins[] = $item;
}
$join .= implode(' ', $joins);
}
$where = 'where 1 = 1 ';
if(isset($this->params['where']) && !empty($this->params['where'])) {
$conditions = [];
foreach ($this->params['where'] as $condition) {
$key = key($condition);
$condition = current($condition);
if ($key === WHERE_AND) {
if ($condition instanceof $this) {
$conditions[] .= 'AND ' . $condition['field'] . $condition['operator'] . '(' . $condition['obj']->build() . ')';
} else {
$conditions[] .= 'AND ' . $condition->build();
}
} elseif ($key === WHERE_OR) {
if ($condition instanceof $this) {
$conditions[] .= 'OR ' . $condition['field'] . $condition['operator'] . '(' . $condition['obj']->build() . ')';
} else {
$conditions[] .= 'OR ' . $condition->build();
}
}
}
$where .= implode(' ', $conditions);
}
$order_by = '';
if(isset($this->params['order_by']) && !empty($this->params['order_by'])) {
$order_by = 'order by '.implode(', ', $this->params['order_by']);
}
$limit = '';
if(isset($this->params['offset'])) {
$limit .= 'limit '.$this->params['offset'];
if(isset($this->params['length'])) {
$limit .= ', '.$this->params['length'];
}
}
$this->lastSQL = sprintf(
'select %s from %s as %s %s %s %s %s',
$fields,
$this->table,
$this->alias,
$join,
$where,
$order_by,
$limit
);
return $this->lastSQL;
//todo order by
//todo limit
}
public function query($SQL)
{
// TODO: Implement query() method.
}
public function execute($SQL)
{
// TODO: Implement execute() method.
}
public function explain()
{
$this->query('explain '.$this->build());
}
}
/index.php
<?php
namespace design;
use \builder\sour as BS;
require 'autoload.php';
spl_autoload_register('auto_loader');
$objSQL = new BS\HopeSQL('user', 'us');
$SQL = $objSQL->select([
'id',
'name',
'age',
'email'
])->join(
'password'
)->on([
new BS\Condition($objSQL->get('id'), 'password.id', OPERATOR_EQ, RIGHT_TYPE_COLUMN)
])->join(
'email'
)->on([
new BS\Condition($objSQL->get('email'), 'email.id', OPERATOR_EQ, RIGHT_TYPE_COLUMN)
])->whereAnd([
new BS\Condition($objSQL->get('id'), '1'),
new BS\Condition($objSQL->get('name'), 'jack', OPERATOR_NOT_EQ, RIGHT_TYPE_STRING)
])->orderBy(
$objSQL->get('id'),
DEFAULT_ORDER_BY
)->orderBy(
$objSQL->get('name'),
DEFAULT_ORDER_BY
)->limit(
0,
10
)->build();
echo $SQL . PHP_EOL;
使用结果:
select us.id, us.name, us.age, us.email from user as us join password as password on 1 = 1 AND us.id = password.id join email as email on 1 = 1 AND us.email = email.id where 1 = 1 AND us.id = 1 AND us.name <> 'jack' order by us.id ASC, us.name ASC limit 0, 10
呵呵~ 菜鸟之码,还望见笑