diff --git a/src/Rxn/Orm/Builder.php b/src/Rxn/Orm/Builder.php index 221969c..58d1cc7 100644 --- a/src/Rxn/Orm/Builder.php +++ b/src/Rxn/Orm/Builder.php @@ -2,6 +2,7 @@ namespace Rxn\Orm; +use Rxn\Orm\Builder\Command; use Rxn\Orm\Builder\QueryParser; abstract class Builder @@ -21,17 +22,6 @@ abstract class Builder */ public $table_aliases = []; - /** - * @param string $reference - * - * @return string - */ - protected function cleanReference(string $reference): string - { - $filtered_reference = $this->filterReference($reference); - return $this->escapeReference($filtered_reference); - } - protected function cleanValue(string $value): string { return "'$value'"; @@ -47,67 +37,18 @@ function changeKey(&$array, $old_key, $new_key) return array_combine($keys, $array); } - /** - * @param string $operand - * - * @return string - */ - protected function filterReference(string $operand): string - { - $operand = preg_replace('#[\`\s]#', '', $operand); - preg_match('#[\p{L}\_\.\-\`0-9]+#', $operand, $matches); - if (isset($matches[0])) { - return $matches[0]; - } - return ''; - } - - /** - * @param string $operand - * - * @return string - */ - protected function escapeReference(string $operand): string - { - $exploded_operand = explode('.', $operand); - if (count($exploded_operand) === 2) { - return "`{$exploded_operand[0]}`.`{$exploded_operand[1]}`"; - } - return "`$operand`"; - } - - - protected function isAssociative(array $array) - { - if ([] === $array) { - return false; - } - ksort($array); - return array_keys($array) !== range(0, count($array) - 1); - } - protected function addCommandWithModifiers($command, $modifiers, $key) { $this->commands[$command][$key] = $modifiers; } - protected function addCommand($command, $value) - { - $this->commands[$command][] = $value; - } - - protected function loadCommands(Builder $builder) - { - $this->commands = array_merge_recursive((array)$this->commands, (array)$builder->commands); - } - - protected function loadGroupCommands(Builder $builder, $type) { - $this->commands[$type][] = $builder->commands; + protected function loadGroupCommands(Command $command, $type) { + $this->commands[$type][] = $command; } - protected function loadBindings(Builder $builder) + protected function loadBindings(Command $command) { - $this->bindings = array_merge((array)$this->bindings, (array)$builder->bindings); + $this->bindings = array_merge((array)$this->bindings, (array)$command->bindings); } protected function loadTableAliases(Builder $builder) @@ -120,38 +61,6 @@ protected function getCommands() return $this->commands; } - protected function addBindings($key_values) - { - if (empty($key_values)) { - return null; - } - foreach ($key_values as $value) { - $this->addBinding($value); - } - } - - protected function addBinding($value) - { - $this->bindings[] = $value; - } - - protected function getOperandBindings($operand): array - { - if (is_array($operand)) { - $bindings = []; - $parsed_array = []; - if (empty($bindings)) { - foreach ($operand as $value) { - $parsed_array[] = '?'; - $bindings[] = $value; - } - return ['(' . implode(",", $parsed_array) . ')', $bindings]; - } - } - - return ['?', [$operand]]; - } - public function build() { $parser = new QueryParser($this); $this->rawSql = $parser->getSql(); diff --git a/src/Rxn/Orm/Builder/Column.php b/src/Rxn/Orm/Builder/Column.php new file mode 100644 index 0000000..9330d5c --- /dev/null +++ b/src/Rxn/Orm/Builder/Column.php @@ -0,0 +1,22 @@ +column = $column; + $this->alias = $alias; + } +} diff --git a/src/Rxn/Orm/Builder/Command.php b/src/Rxn/Orm/Builder/Command.php new file mode 100644 index 0000000..75a4ec1 --- /dev/null +++ b/src/Rxn/Orm/Builder/Command.php @@ -0,0 +1,152 @@ +filterReference($reference); + //return $this->escapeReference($filtered_reference); + return $filtered_reference; + } + + protected function addColumn($column, $alias = null) + { + list($column, $table, $database) = $this->listColumnTableDatabase($column); + + $key = null; + if (!empty($column)) { + $key = $column; + if (!empty($alias)) { + $key = $alias; + } + } + + $table = $this->addTable($table, null, $database); + + $table->columns[$key] = new Column($column, $alias); + } + + protected function addTable($table, $alias = null, $database = null) + { + $this->tables[$table] = new Table($table, $alias, $database); + return $this->tables[$table]; + } + + protected function listColumnTableDatabase($reference) + { + preg_match('#\`?(\p{L}+)\`?\.\`?(\p{L}+)\`?#', $reference, $matches); + if (isset($matches[1]) && isset($matches[2]) && !isset($matches[3])) { + $table = $matches[1]; + $column = $matches[2]; + return [$column, $table, null]; + } elseif (isset($matches[3])) { + $database = $matches[1]; + $table = $matches[2]; + $column = $matches[3]; + return [$column, $table, $database]; + } + $column = $reference; + return [$column, null, null]; + } + + /** + * @param string $operand + * + * @return string + */ + protected function escapeReference(string $operand): string + { + $exploded_operands = explode('.', $operand); + + $operands = []; + foreach ($exploded_operands as $exploded_operand) { + $operands[] = "`$exploded_operand`"; + } + return implode(".", $operands); + } + + /** + * @param string $operand + * + * @return string + */ + protected function filterReference(string $operand): string + { + $operand = preg_replace('#[\`\s]#', '', $operand); + preg_match('#[\p{L}\_\.\-\`0-9]+#', $operand, $matches); + if (isset($matches[0])) { + return $matches[0]; + } + return ''; + } + + /** + * @param $operand + * + * @return array + */ + protected function getOperandBindings($operand): array + { + if (is_array($operand)) { + $bindings = []; + $parsed_array = []; + if (empty($bindings)) { + foreach ($operand as $value) { + $parsed_array[] = '?'; + $bindings[] = $value; + } + return ['(' . implode(",", $parsed_array) . ')', $bindings]; + } + } + + return ['?', [$operand]]; + } + + protected function addBindings($key_values) + { + if (empty($key_values)) { + return null; + } + foreach ($key_values as $value) { + $this->addBinding($value); + } + } + + protected function addBinding($value) + { + $this->bindings[] = $value; + } +} diff --git a/src/Rxn/Orm/Builder/Query.php b/src/Rxn/Orm/Builder/Query.php index 61b2396..15579d8 100644 --- a/src/Rxn/Orm/Builder/Query.php +++ b/src/Rxn/Orm/Builder/Query.php @@ -10,6 +10,12 @@ class Query extends Builder { + + /** + * @var Command[] + */ + public $commands; + /** * @param array $columns * @param bool $distinct @@ -18,9 +24,7 @@ class Query extends Builder */ public function select(array $columns = ['*'], $distinct = false): Query { - $select = new Select(); - $select->set($columns, $distinct); - $this->loadCommands($select); + $this->commands[] = (new Select())->select($columns, $distinct); return $this; } @@ -32,10 +36,7 @@ public function select(array $columns = ['*'], $distinct = false): Query */ public function from(string $table, string $alias = null): Query { - $from = new From(); - $from->set($table, $alias); - $this->loadCommands($from); - $this->loadTableAliases($from); + $this->commands[] = (new From())->from($table, $alias); return $this; } @@ -50,20 +51,17 @@ public function from(string $table, string $alias = null): Query */ public function joinCustom(string $table, callable $callable, string $alias = null, string $type = 'inner'): Query { - $join = new Join(); - $join->set($table, $callable, $alias, $type); - $this->loadCommands($join); - $this->loadBindings($join); - $this->loadTableAliases($join); + $this->commands[] = (new Join())->join($table, $callable, $alias, $type); return $this; } /** - * @param string $table - * @param string $first_operand - * @param string $operator - * @param string|array $second_operand - * @param string $alias + * @param string $table + * @param string $operand_1 + * @param string $operator + * @param string|array $operand_2 + * @param string $alias + * @param callable|null $callback * * @return Query * @throws \Exception @@ -71,71 +69,76 @@ public function joinCustom(string $table, callable $callable, string $alias = nu */ public function join( string $table, - string $first_operand, + string $operand_1, string $operator, - $second_operand, - string $alias = null + $operand_2, + string $alias = null, + callable $callback = null ): Query { - return $this->innerJoin($table, $first_operand, $operator, $second_operand, $alias); + return $this->innerJoin($table, $operand_1, $operator, $operand_2, $alias, $callback); } /** - * @param string $table - * @param string $first_operand - * @param string $operator - * @param string|array $second_operand - * @param string|null $alias + * @param string $table + * @param string $operand_1 + * @param string $operator + * @param string|array $operand_2 + * @param string|null $alias + * @param callable|null $callback * * @return Query * @throws \Exception */ public function innerJoin( string $table, - string $first_operand, + string $operand_1, string $operator, - $second_operand, - string $alias = null + $operand_2, + string $alias = null, + callable $callback = null ): Query { return $this->joinCustom($table, - function (Join $join) use ($first_operand, $operator, $second_operand, $alias) { - if (!empty($alias)) { - $join->as($alias); + function (Join $join) use ($operand_1, $operator, $operand_2, $alias, $callback) { + $join->on($operand_1, $operator, $operand_2); + if (!is_null($callback)) { + call_user_func($callback, $join); } - $join->on($first_operand, $operator, $second_operand); }, $alias, 'inner'); } /** - * @param string $table - * @param string $first_operand - * @param string $operator - * @param string|array $second_operand - * @param string $alias + * @param string $table + * @param string $operand_1 + * @param string $operator + * @param string|array $operand_2 + * @param string $alias + * @param callable|null $callback * * @return Query * @throws \Exception */ public function leftJoin( string $table, - string $first_operand, + string $operand_1, string $operator, - $second_operand, - string $alias + $operand_2, + string $alias, + callable $callback = null ): Query { return $this->joinCustom($table, - function (Join $join) use ($first_operand, $operator, $second_operand, $alias) { - if (!empty($alias)) { - $join->as($alias); + function (Join $join) use ($operand_1, $operator, $operand_2, $alias, $callback) { + $join->on($operand_1, $operator, $operand_2); + if (!is_null($callback)) { + call_user_func($callback, $join); } - $join->on($first_operand, $operator, $second_operand); }, $alias, 'left'); } /** * @param string $table - * @param string $first_operand + * @param string $operand_1 * @param string $operator - * @param string|array $second_operand + * @param string|array $operand_2 * @param string $alias * * @return Query @@ -143,18 +146,14 @@ function (Join $join) use ($first_operand, $operator, $second_operand, $alias) { */ public function rightJoin( string $table, - string $first_operand, + string $operand_1, string $operator, - $second_operand, + $operand_2, string $alias ): Query { - return $this->joinCustom($table, - function (Join $join) use ($first_operand, $operator, $second_operand, $alias) { - if (!empty($alias)) { - $join->as($alias); - } - $join->on($first_operand, $operator, $second_operand); - }, $alias, 'right'); + return $this->joinCustom($table, function (Join $join) use ($operand_1, $operator, $operand_2, $alias) { + $join->on($operand_1, $operator, $operand_2); + }, $alias, 'right'); } /** @@ -187,59 +186,30 @@ public function whereId($id, string $id_key = 'id', callable $callback = null, s } public function where( - string $first_operand, + string $operand_1, string $operator, - string $second_operand, + string $operand_2, callable $callback = null, string $type = 'where' ): Query { - $where = new Where(); - $where->set($first_operand, $operator, $second_operand, $callback, $type); - if (!is_null($callback)) { - $group = $where::WHERE_COMMANDS[$type]; - $this->loadGroupCommands($where, $group); - } else { - $this->loadCommands($where); - } - $this->loadBindings($where); + $this->commands[] = (new Where())->where($operand_1, $operator, $operand_2, $callback, $type); return $this; } - /** - * @param string $operand - * @param array $values - * @param callable|null $callback - * @param string $type - * @param bool $not - * - * @return Query - */ public function whereIn( string $operand, - array $values, + array $operands, callable $callback = null, string $type = 'where', $not = false ) { - $where = new Where(); - $where->setIn($operand, $values, $callback, $type, $not); - $this->loadCommands($where); - $this->loadBindings($where); + $this->commands[] = (new Where())->in($operand, $operands, $callback, $type, $not); return $this; } - /** - * @param string $operand - * @param string|array $values - * @param callable|null $callback - * @param string $type - * - * @return Query - * @throws \Exception - */ - public function whereNotIn(string $operand, array $values, callable $callback = null, string $type = 'where') + public function whereNotIn(string $operand, array $operands, callable $callback = null, string $type = 'where') { - return $this->whereIn($operand, $values, $callback, $type, true); + return $this->whereIn($operand, $operands, $callback, $type, true); } /** @@ -252,10 +222,7 @@ public function whereNotIn(string $operand, array $values, callable $callback = */ public function whereIsNull(string $operand, callable $callback = null, $type = 'where', $not = false) { - $where = new Where(); - $where->setNull($operand, $callback, $type, $not); - $this->loadCommands($where); - $this->loadBindings($where); + $this->commands[] = (new Where())->isNull($operand, $callback, $type, $not); return $this; } @@ -272,37 +239,29 @@ public function whereIsNotNull(string $operand, callable $callback = null, $type } /** - * @param string $first_operand + * @param string $operand_1 * @param string $operator - * @param string $second_operand + * @param string $operand_2 * @param callable|null $callback * * @return Query */ - public function and ( - string $first_operand, - string $operator, - string $second_operand, - callable $callback = null - ): Query { - return $this->andWhere($first_operand, $operator, $second_operand, $callback); + public function and (string $operand_1, string $operator, string $operand_2, callable $callback = null): Query + { + return $this->andWhere($operand_1, $operator, $operand_2, $callback); } /** - * @param string $first_operand + * @param string $operand_1 * @param string $operator - * @param string $second_operand + * @param string $operand_2 * @param callable|null $callback * * @return Query */ - public function andWhere( - string $first_operand, - string $operator, - string $second_operand, - callable $callback = null - ): Query { - return $this->where($first_operand, $operator, $second_operand, $callback, 'and'); + public function andWhere(string $operand_1, string $operator, string $operand_2, callable $callback = null): Query + { + return $this->where($operand_1, $operator, $operand_2, $callback, 'and'); } /** @@ -353,29 +312,29 @@ public function andWhereIsNotNull(string $operand, callable $callback = null) } /** - * @param string $first_operand + * @param string $operand_1 * @param string $operator - * @param $second_operand + * @param $operand_2 * @param callable|null $callback * * @return Query */ - public function or (string $first_operand, string $operator, $second_operand, callable $callback = null): Query + public function or (string $operand_1, string $operator, $operand_2, callable $callback = null): Query { - return $this->orWhere($first_operand, $operator, $second_operand, $callback); + return $this->orWhere($operand_1, $operator, $operand_2, $callback); } /** - * @param string $first_operand + * @param string $operand_1 * @param string $operator - * @param $second_operand + * @param $operand_2 * @param callable|null $callback * * @return Query */ - public function orWhere(string $first_operand, string $operator, $second_operand, callable $callback = null): Query + public function orWhere(string $operand_1, string $operator, $operand_2, callable $callback = null): Query { - return $this->where($first_operand, $operator, $second_operand, $callback, 'or'); + return $this->where($operand_1, $operator, $operand_2, $callback, 'or'); } /** diff --git a/src/Rxn/Orm/Builder/Query/From.php b/src/Rxn/Orm/Builder/Query/From.php index ef4dd11..3319d54 100644 --- a/src/Rxn/Orm/Builder/Query/From.php +++ b/src/Rxn/Orm/Builder/Query/From.php @@ -2,19 +2,15 @@ namespace Rxn\Orm\Builder\Query; -use Rxn\Orm\Builder\Query; +use Rxn\Orm\Builder\Command; -class From extends Query { +class From extends Command +{ - public function set(string $table, string $alias = null) { - $escaped_table = $this->escapeReference($table); - if (empty($alias)) { - $value = $escaped_table; - } else { - $escaped_alias = $this->escapeReference($alias); - $value = "$escaped_table AS $escaped_alias"; - $this->table_aliases[$table] = $alias; - } - $this->addCommand('FROM', $value); + public function from(string $table, string $alias = null, $database = null) + { + $this->command = 'FROM'; + $this->addTable($table, $alias, $database); + return $this; } } diff --git a/src/Rxn/Orm/Builder/Query/Join.php b/src/Rxn/Orm/Builder/Query/Join.php index ecdfae4..55ed1c1 100644 --- a/src/Rxn/Orm/Builder/Query/Join.php +++ b/src/Rxn/Orm/Builder/Query/Join.php @@ -2,9 +2,11 @@ namespace Rxn\Orm\Builder\Query; -use Rxn\Orm\Builder; +use Rxn\Orm\Builder\Command; +use Rxn\Orm\Builder\Query\Join\On; +use Rxn\Orm\Builder\Table; -class Join extends Builder +class Join extends Command { const JOIN_COMMANDS = [ 'inner' => 'INNER JOIN', @@ -13,62 +15,73 @@ class Join extends Builder ]; /** - * @var string + * @var */ - public $table; + public $command; /** - * @var string + * @var Table[] */ - public $alias; + public $tables; /** - * @var + * @var On[]|Where[] */ - public $modifiers; + public $commands; /** - * @var + * @param string $table + * @param callable $callable + * @param string|null $alias + * @param string $type + * + * @return $this + * @throws \Exception */ - public $bindings; - - public function set(string $table, callable $callable, string $alias = null, string $type = 'inner') { + public function join(string $table, callable $callable, string $alias = null, string $type = 'inner') + { if (!array_key_exists($type, self::JOIN_COMMANDS)) { throw new \Exception(""); } - $this->table = $table; - $this->addAlias($alias); - $command = self::JOIN_COMMANDS[$type]; + $this->command = self::JOIN_COMMANDS[$type]; + $this->addTable($table, $alias); call_user_func($callable, $this); - $this->addBindings($this->bindings); - $this->addCommandWithModifiers($command, $this->modifiers, $table); - + return $this; } + public function on(string $operand_1, string $operator, $operand_2) + { + $command = new On($operand_1, $operator, $operand_2); + $this->commands[] = $command; + return $this; + } - - public function as(string $alias) { - $this->alias = $alias; - $clean_alias = $this->cleanReference($alias); - if (!in_array($clean_alias, (array)$this->modifiers['AS'])) { - $this->modifiers['AS'][] = $clean_alias; - $this->table_aliases[$this->table] = $alias; - } + public function where( + string $operand_1, + string $operator, + string $operand_2, + string $type = 'and', + callable $callback = null + ): Command { + $this->commands[] = (new Where())->where($operand_1, $operator, $operand_2, $callback, $type); return $this; } - public function on(string $first, string $condition, $second) { - $first = $this->cleanReference($first); - $second = $this->cleanReference($second); - $value = "$first $condition $second"; - $this->modifiers['ON'][] = $value; + public function whereIn( + string $operand, + array $operands, + string $type = 'and', + $not = false, + callable $callback = null + ) { + $this->commands[] = (new Where())->in($operand, $operands, $callback, $type, $not); return $this; } - private function addAlias($alias) { - if (!empty($alias)) { - $this->as($alias); - } + public function whereIsNull(string $operand, $type = 'and', $not = false, callable $callback = null) + { + $this->commands[] = (new Where())->isNull($operand, $callback, $type, $not); + return $this; } protected function addCommand($command, $value) diff --git a/src/Rxn/Orm/Builder/Query/Join/On.php b/src/Rxn/Orm/Builder/Query/Join/On.php new file mode 100644 index 0000000..9241f99 --- /dev/null +++ b/src/Rxn/Orm/Builder/Query/Join/On.php @@ -0,0 +1,23 @@ +operand_1 = $operand_1; + $this->operator = $operator; + $this->operand_2 = $operand_2; + $this->addColumn($operand_1); + $this->addColumn($operand_2); + } +} diff --git a/src/Rxn/Orm/Builder/Query/Select.php b/src/Rxn/Orm/Builder/Query/Select.php index 3eb14be..568f14d 100644 --- a/src/Rxn/Orm/Builder/Query/Select.php +++ b/src/Rxn/Orm/Builder/Query/Select.php @@ -2,15 +2,22 @@ namespace Rxn\Orm\Builder\Query; -use Rxn\Orm\Builder\Query; +use Rxn\Orm\Builder\Command; -class Select extends Query +class Select extends Command { + /** + * @var string + */ + public $command; + /** * @param array $columns * @param bool $distinct + * + * @return $this */ - public function set(array $columns = ['*'], $distinct = false) + public function select(array $columns = ['*'], $distinct = false) { if ($columns === ['*'] || empty($columns) @@ -21,6 +28,7 @@ public function set(array $columns = ['*'], $distinct = false) } else { $this->selectNumerical($columns, $distinct); } + return $this; } /** @@ -28,8 +36,8 @@ public function set(array $columns = ['*'], $distinct = false) */ public function selectAll($distinct = false) { - $command = ($distinct) ? 'SELECT DISTINCT' : 'SELECT'; - $this->addCommand($command, "*"); + $this->command = ($distinct) ? 'SELECT DISTINCT' : 'SELECT'; + $this->addColumn('*'); } /** @@ -38,16 +46,10 @@ public function selectAll($distinct = false) */ public function selectAssociative(array $columns, $distinct = false) { - $command = ($distinct) ? 'SELECT DISTINCT' : 'SELECT'; - foreach ($columns as $reference => $alias) { - $reference = $this->cleanReference($reference); - if (empty($alias)) { - $value = $reference; - } else { - $alias = $this->cleanReference($alias); - $value = "$reference AS $alias"; - } - $this->addCommand($command, $value); + $this->command = ($distinct) ? 'SELECT DISTINCT' : 'SELECT'; + foreach ($columns as $column => $alias) { + $column = $this->cleanReference($column); + $this->addColumn($column, $alias); } } diff --git a/src/Rxn/Orm/Builder/Query/Where.php b/src/Rxn/Orm/Builder/Query/Where.php index 6cf1e34..7d4e672 100644 --- a/src/Rxn/Orm/Builder/Query/Where.php +++ b/src/Rxn/Orm/Builder/Query/Where.php @@ -2,14 +2,14 @@ namespace Rxn\Orm\Builder\Query; -use Rxn\Orm\Builder\Query; +use Rxn\Orm\Builder\Command; -class Where extends Query +class Where extends Command { const WHERE_COMMANDS = [ 'where' => 'WHERE', - 'and' => 'WHERE', + 'and' => 'AND', 'or' => 'OR', ]; @@ -17,8 +17,6 @@ class Where extends Query '=', '!=', '<>', - 'IN', - 'NOT IN', 'LIKE', 'NOT LIKE', 'BETWEEN', @@ -30,85 +28,191 @@ class Where extends Query '>=', ]; - public function set( - string $first_operand, + protected $command = 'WHERE'; + + /** + * @var string + */ + protected $operand_1; + + /** + * @var string + */ + protected $operator; + + /** + * @var string|array + */ + protected $operand_2; + + /** + * @var Command[] + */ + protected $commands; + + /** + * @param string $operand_1 + * @param string $operator + * @param string $operand_2 + * @param callable|null $callback + * @param string $type + * + * @return $this + * @throws \Exception + */ + public function where( + string $operand_1, string $operator, - string $second_operand, + string $operand_2, callable $callback = null, string $type = 'where' - ) { + ): Where { $this->validateType($type); - if ($this->isReference($first_operand) - && $this->isReference($second_operand) - ) { - $first_operand = $this->cleanReference($first_operand); - $second_operand = $this->cleanReference($second_operand); - } elseif ($this->isReference($first_operand) - && !$this->isReference($second_operand) - ) { - $first_operand = $this->cleanReference($first_operand); - list($second_operand, $bindings) = $this->getOperandBindings($second_operand); + $this->validateOperator($operator); + + $this->command = self::WHERE_COMMANDS[$type]; + + if ($this->isReference($operand_1)) { + $this->operand_1 = $this->cleanReference($operand_1); + } else { + list($this->operand_1, $bindings) = $this->getOperandBindings($operand_1); $this->addBindings($bindings); - } elseif (!$this->isReference($first_operand) - && $this->isReference($second_operand) - ) { - list($first_operand, $bindings) = $this->getOperandBindings($first_operand); + } + + $this->operator = $operator; + + if ($this->isReference($operand_2)) { + $this->operand_2 = $this->cleanReference($operand_2); + } else { + list($this->operand_2, $bindings) = $this->getOperandBindings($operand_2); $this->addBindings($bindings); - $second_operand = $this->cleanReference($second_operand); } - $value = "$first_operand $operator $second_operand"; + if (!is_null($callback)) { - $command = self::WHERE_COMMANDS['where']; - $this->addCommand($command, $value); call_user_func($callback, $this); - } else { - $command = self::WHERE_COMMANDS[$type]; - $this->addCommand($command, $value); } + return $this; } - public function setIn( + public function in( string $operand, - array $values, + array $operands, callable $callback = null, - string $type = 'where', + $type = 'where', $not = false - ) { + ): Where { $this->validateType($type); - $operand = $this->cleanReference($operand); - $operator = ($not) ? 'NOT IN' : 'IN'; - list($value_list, $bindings) = $this->getOperandBindings($values); - $value = "$operand $operator $value_list"; - $command = self::WHERE_COMMANDS[$type]; - $this->addCommand($command, $value); - $this->addBindings($bindings); + $this->command = self::WHERE_COMMANDS[$type]; + $this->operand_1 = $this->cleanReference($operand); + $this->operator = ($not) ? 'NOT IN' : 'IN'; + + $cleaned_operands = []; + foreach ($operands as $key => $unclean_operand) { + if ($this->isReference($unclean_operand)) { + $cleaned_operands[$key] = $this->cleanReference($unclean_operand); + } else { + $cleaned_operands[$key] = $unclean_operand; + } + } + $this->operand_2 = $cleaned_operands; + if (!is_null($callback)) { call_user_func($callback, $this); } + return $this; } - public function setNull(string $operand, callable $callback = null, string $type = 'where', $not = false) + public function isNull(string $operand, callable $callback = null, $type = 'where', $not = false): Where { $this->validateType($type); - $operand = $this->cleanReference($operand); - $operator = ($not) ? 'IS NOT NULL' : 'IS NULL'; - $value = "$operand $operator"; - $command = self::WHERE_COMMANDS[$type]; - $this->addCommand($command, $value); + $this->command = self::WHERE_COMMANDS[$type]; + $this->operand_1 = $this->cleanReference($operand); + $this->operator = ($not) ? 'IS NOT NULL' : 'IS NULL'; + if (!is_null($callback)) { call_user_func($callback, $this); } + return $this; + } + + public function and (string $operand_1, string $operator, string $operand_2, callable $callback = null): Where + { + $this->commands[] = (new Where())->where($operand_1, $operator, $operand_2, $callback, 'and'); + return $this; + } + + public function andIn(string $operand, array $operands, callable $callback = null): Where + { + $this->commands[] = (new Where())->in($operand, $operands, $callback, 'and', false); + return $this; + } + + public function andNotIn(string $operand, array $operands, callable $callback = null): Where + { + $this->commands[] = (new Where())->in($operand, $operands, $callback, 'and', true); + return $this; + } + + public function andIsNull(string $operand, callable $callback = null): Where + { + $this->commands[] = (new Where())->isNull($operand, $callback, 'and', false); + return $this; + } + + public function andIsNotNull(string $operand, callable $callback = null): Where + { + $this->commands[] = (new Where())->isNull($operand, $callback, 'and', true); + return $this; + } + + public function or (string $operand_1, string $operator, string $operand_2, callable $callback = null): Where + { + $this->commands[] = (new Where())->where($operand_1, $operator, $operand_2, $callback, 'or'); + return $this; + } + + public function orIn(string $operand, array $operands, callable $callback = null): Where + { + $this->commands[] = (new Where())->in($operand, $operands, $callback, 'or', false); + return $this; + } + + public function orNotIn(string $operand, array $operands, callable $callback = null): Where + { + $this->commands[] = (new Where())->in($operand, $operands, $callback, 'or', true); + return $this; + } + + public function orIsNull(string $operand, callable $callback = null): Where + { + $this->commands[] = (new Where())->isNull($operand, $callback, 'or', false); + return $this; + } + + public function orIsNotNull(string $operand, callable $callback = null): Where + { + $this->commands[] = (new Where())->isNull($operand, $callback, 'or', true); + return $this; } private function validateType($type) { if (!array_key_exists($type, self::WHERE_COMMANDS)) { - throw new \Exception(""); + throw new \Exception("'$type' is not a valid WHERE command"); + } + } + + private function validateOperator($operator) { + if (!in_array($operator, self::WHERE_OPERATORS)) { + throw new \Exception("'$operator' is not a valid WHERE operator"); } } - private function isReference($operand) + private function isReference($operand): bool { + if (!is_string($operand)) { + return false; + } $result = explode('.', $operand); if (isset($result[1])) { return true; diff --git a/src/Rxn/Orm/Builder/QueryParser.php b/src/Rxn/Orm/Builder/QueryParser.php deleted file mode 100644 index 8c27426..0000000 --- a/src/Rxn/Orm/Builder/QueryParser.php +++ /dev/null @@ -1,149 +0,0 @@ -builder = $builder; - } - - public function getSql() - { - $sql = ''; - $sql .= $this->select(); - $sql .= $this->from(); - $sql .= $this->innerJoin(); - $sql .= $this->leftJoin(); - $sql .= $this->rightJoin(); - $sql .= $this->where(); - $sql .= $this->or(); - return $sql; - } - - private function select() - { - if (!array_key_exists('SELECT', $this->builder->commands)) { - return ''; - } - - $select = $this->builder->commands['SELECT']; - if ($select == ['*']) { - return "SELECT * \r\n"; - } - return "SELECT \r\n " . implode(",\r\n ", $select) . " \r\n"; - } - - private function from() - { - if (!array_key_exists('FROM', $this->builder->commands)) { - return ''; - } - - $from = $this->builder->commands['FROM']; - return "FROM " . implode(",\r\n ", $from) . " \r\n"; - } - - private function innerJoin() - { - if (!array_key_exists('INNER JOIN', $this->builder->commands)) { - return ''; - } - return $this->join('INNER JOIN'); - } - - private function leftJoin() - { - if (!array_key_exists('LEFT JOIN', $this->builder->commands)) { - return ''; - } - return $this->join('LEFT JOIN'); - } - - private function rightJoin() - { - if (!array_key_exists('RIGHT JOIN', $this->builder->commands)) { - return ''; - } - return $this->join('RIGHT JOIN'); - } - - private function where() { - if (!array_key_exists('WHERE', $this->builder->commands)) { - return ''; - } - - $where = $this->builder->commands['WHERE']; - - $sql = ''; - $used_initial_where = false; - foreach ($where as $key => $value) { - if (is_numeric($key) && is_array($value)) { - // in a group - foreach ($value as $grouped_command => $commands) { - switch ($grouped_command) { - case 'WHERE': - $grouped_command = ($used_initial_where) ? 'AND' : 'WHERE'; - $used_initial_where = true; - $commands_imploded = implode(" \r\n AND ",$commands); - $sql .= "$grouped_command (\r\n $commands_imploded \r\n)\r\n"; - } - } - } - } - return $sql; - } - - private function or() { - if (!array_key_exists('WHERE', $this->builder->commands)) { - return ''; - } - - $where = $this->builder->commands['WHERE']; - - $sql = ''; - $used_initial_where = false; - foreach ($where as $key => $value) { - if (is_numeric($key) && is_array($value)) { - // in a group - foreach ($value as $grouped_command => $commands) { - switch ($grouped_command) { - case 'WHERE': - $grouped_command = ($used_initial_where) ? 'AND' : 'WHERE'; - $used_initial_where = true; - $commands_imploded = implode(" \r\n AND ",$commands); - $sql .= "$grouped_command (\r\n $commands_imploded \r\n)\r\n"; - } - } - } - } - return $sql; - } - - private function join($command) { - $inner_join = $this->builder->commands[$command]; - - $sql = ''; - foreach ($inner_join as $table => $conditions) { - $sql .= "$command `$table` "; - foreach ($conditions as $condition => $expressions) { - $sql .= "$condition "; - foreach ($expressions as $expression) { - $sql .= "$expression "; - } - } - $sql .= "\r\n"; - } - return $sql; - } - - -} diff --git a/src/Rxn/Orm/Builder/Table.php b/src/Rxn/Orm/Builder/Table.php new file mode 100644 index 0000000..fbc6fca --- /dev/null +++ b/src/Rxn/Orm/Builder/Table.php @@ -0,0 +1,33 @@ +table = $table; + $this->alias = $alias; + $this->database = $database; + } +} diff --git a/src/Rxn/Orm/Tests/Builder/Query/JoinTest.php b/src/Rxn/Orm/Tests/Builder/Query/JoinTest.php index 844fdd0..3d80d93 100644 --- a/src/Rxn/Orm/Tests/Builder/Query/JoinTest.php +++ b/src/Rxn/Orm/Tests/Builder/Query/JoinTest.php @@ -4,6 +4,7 @@ use PHPUnit\Framework\TestCase; use Rxn\Orm\Builder\Query; +use Rxn\Orm\Builder\Query\Join; use Rxn\Orm\Builder\Query\Where; final class JoinTest extends TestCase @@ -12,21 +13,25 @@ public function testJoin() { $query = new Query(); $query->select(['users.id' => 'user_id']) - ->from('users') - ->join('orders', 'orders.user_id', '=', 'users.id', 'o') + ->from('users', 'o') + ->leftJoin('orders', 'orders.user_id', '=', 'users.id', 'o', function (Join $join) { + $join->whereIsNull('users.test2','and',true, function (Where $where) { + $where->orIsNotNull('users.test3'); + }); + }) ->join('invoices', 'invoices.id', '=', 'orders.invoice_id', 'i') ->where('users.first_name', '=', 'David', function (Where $where) { $where->and('users.last_name', '=', 'Wyly'); + $where->andIn('users.type', [1, 2, 3]); }) - ->where('users.first_name', '=', 'Lance', function (Where $where) { + ->and('users.first_name', '=', 'Lance', function (Where $where) { $where->and('users.last_name', '=', 'Badger'); }) ->or('users.first_name2', '=', 'Joseph', function (Where $where) { $where->and('users.last_name2', '=', 'Andrews', function (Where $where) { $where->or('users.last_name2', '=', 'Andrews, III'); }); - }) - ->build(); + }); $this->assertEquals('`users`.`id` AS `user_id`', $query->commands['SELECT'][0]);