Version 3 #1
86
RoboFile.php
86
RoboFile.php
@ -1,4 +1,7 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
use Robo\Tasks;
|
||||
|
||||
if ( ! function_exists('glob_recursive'))
|
||||
{
|
||||
// Does not support flag GLOB_BRACE
|
||||
@ -20,7 +23,7 @@ if ( ! function_exists('glob_recursive'))
|
||||
*
|
||||
* @see http://robo.li/
|
||||
*/
|
||||
class RoboFile extends \Robo\Tasks {
|
||||
class RoboFile extends Tasks {
|
||||
|
||||
/**
|
||||
* Directories used by analysis tools
|
||||
@ -51,20 +54,20 @@ class RoboFile extends \Robo\Tasks {
|
||||
/**
|
||||
* Do static analysis tasks
|
||||
*/
|
||||
public function analyze()
|
||||
public function analyze(): void
|
||||
{
|
||||
$this->prepare();
|
||||
$this->lint();
|
||||
$this->phploc(TRUE);
|
||||
$this->phpcs(TRUE);
|
||||
$this->dependencyReport();
|
||||
$this->phpmd(TRUE);
|
||||
$this->phpcpdReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run all tests, generate coverage, generate docs, generate code statistics
|
||||
*/
|
||||
public function build()
|
||||
public function build(): void
|
||||
{
|
||||
$this->analyze();
|
||||
$this->coverage();
|
||||
@ -74,11 +77,11 @@ class RoboFile extends \Robo\Tasks {
|
||||
/**
|
||||
* Cleanup temporary files
|
||||
*/
|
||||
public function clean()
|
||||
public function clean(): void
|
||||
{
|
||||
// So the task doesn't complain,
|
||||
// make any 'missing' dirs to cleanup
|
||||
array_map(function ($dir) {
|
||||
array_map(static function ($dir) {
|
||||
if ( ! is_dir($dir))
|
||||
{
|
||||
`mkdir -p {$dir}`;
|
||||
@ -92,19 +95,15 @@ class RoboFile extends \Robo\Tasks {
|
||||
/**
|
||||
* Run unit tests and generate coverage reports
|
||||
*/
|
||||
public function coverage()
|
||||
public function coverage(): void
|
||||
{
|
||||
$this->taskPhpUnit()
|
||||
->configFile('build/phpunit.xml')
|
||||
->run();
|
||||
// $this->_run(['phpdbg -qrr -- vendor/bin/phpunit -c build']);
|
||||
|
||||
$this->_run(['phpdbg -qrr -- vendor/bin/phpunit -c build']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate documentation with phpdox
|
||||
*/
|
||||
public function docs()
|
||||
public function docs(): void
|
||||
{
|
||||
$this->_run(['vendor/bin/phpdox']);
|
||||
}
|
||||
@ -112,11 +111,11 @@ class RoboFile extends \Robo\Tasks {
|
||||
/**
|
||||
* Verify that source files are valid
|
||||
*/
|
||||
public function lint()
|
||||
public function lint(): void
|
||||
{
|
||||
$files = $this->getAllSourceFiles();
|
||||
|
||||
$chunks = array_chunk($files, (int)`getconf _NPROCESSORS_ONLN`);
|
||||
$chunks = array_chunk($files, (int)shell_exec('getconf _NPROCESSORS_ONLN'));
|
||||
|
||||
foreach($chunks as $chunk)
|
||||
{
|
||||
@ -129,7 +128,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
*
|
||||
* @param bool $report - if true, generates reports instead of direct output
|
||||
*/
|
||||
public function phpcs($report = FALSE)
|
||||
public function phpcs($report = FALSE): void
|
||||
{
|
||||
$dir = __DIR__;
|
||||
|
||||
@ -149,12 +148,36 @@ class RoboFile extends \Robo\Tasks {
|
||||
$this->_run($cmd_parts);
|
||||
}
|
||||
|
||||
public function phpmd($report = FALSE): void
|
||||
{
|
||||
$report_cmd_parts = [
|
||||
'vendor/bin/phpmd',
|
||||
'./src',
|
||||
'xml',
|
||||
'cleancode,codesize,controversial,design,naming,unusedcode',
|
||||
'--exclude ParallelAPIRequest',
|
||||
'--reportfile ./build/logs/phpmd.xml'
|
||||
];
|
||||
|
||||
$normal_cmd_parts = [
|
||||
'vendor/bin/phpmd',
|
||||
'./src',
|
||||
'ansi',
|
||||
'cleancode,codesize,controversial,design,naming,unusedcode',
|
||||
'--exclude ParallelAPIRequest'
|
||||
];
|
||||
|
||||
$cmd_parts = ($report) ? $report_cmd_parts : $normal_cmd_parts;
|
||||
|
||||
$this->_run($cmd_parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the phploc tool
|
||||
*
|
||||
* @param bool $report - if true, generates reports instead of direct output
|
||||
*/
|
||||
public function phploc($report = FALSE)
|
||||
public function phploc($report = FALSE): void
|
||||
{
|
||||
// Command for generating reports
|
||||
$report_cmd_parts = [
|
||||
@ -182,7 +205,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
/**
|
||||
* Create temporary directories
|
||||
*/
|
||||
public function prepare()
|
||||
public function prepare(): void
|
||||
{
|
||||
array_map([$this, '_mkdir'], $this->taskDirs);
|
||||
}
|
||||
@ -190,7 +213,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
/**
|
||||
* Lint php files and run unit tests
|
||||
*/
|
||||
public function test()
|
||||
public function test(): void
|
||||
{
|
||||
$this->lint();
|
||||
$this->taskPhpUnit()
|
||||
@ -202,7 +225,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
/**
|
||||
* Watches for file updates, and automatically runs appropriate actions
|
||||
*/
|
||||
public function watch()
|
||||
public function watch(): void
|
||||
{
|
||||
$this->taskWatch()
|
||||
->monitor('composer.json', function() {
|
||||
@ -217,27 +240,12 @@ class RoboFile extends \Robo\Tasks {
|
||||
->run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create pdepend reports
|
||||
*/
|
||||
protected function dependencyReport()
|
||||
{
|
||||
$cmd_parts = [
|
||||
'vendor/bin/pdepend',
|
||||
'--jdepend-xml=build/logs/jdepend.xml',
|
||||
'--jdepend-chart=build/pdepend/dependencies.svg',
|
||||
'--overview-pyramid=build/pdepend/overview-pyramid.svg',
|
||||
'src'
|
||||
];
|
||||
$this->_run($cmd_parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the total list of source files, including tests
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAllSourceFiles()
|
||||
protected function getAllSourceFiles(): array
|
||||
{
|
||||
$files = array_merge(
|
||||
glob_recursive('build/*.php'),
|
||||
@ -256,7 +264,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
*
|
||||
* @param array $chunk
|
||||
*/
|
||||
protected function parallelLint(array $chunk)
|
||||
protected function parallelLint(array $chunk): void
|
||||
{
|
||||
$task = $this->taskParallelExec()
|
||||
->timeout(5)
|
||||
@ -273,7 +281,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
/**
|
||||
* Generate copy paste detector report
|
||||
*/
|
||||
protected function phpcpdReport()
|
||||
protected function phpcpdReport(): void
|
||||
{
|
||||
$cmd_parts = [
|
||||
'vendor/bin/phpcpd',
|
||||
@ -290,7 +298,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
* @param array $cmd_parts - command arguments
|
||||
* @param string $join_on - what to join the command arguments with
|
||||
*/
|
||||
protected function _run(array $cmd_parts, $join_on = ' ')
|
||||
protected function _run(array $cmd_parts, $join_on = ' '): void
|
||||
{
|
||||
$this->taskExec(implode($join_on, $cmd_parts))->run();
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* SQL Query Builder / Database Abstraction Layer
|
||||
*
|
||||
* PHP version 7.2
|
||||
* PHP version 7.3
|
||||
*
|
||||
* @package Query
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
|
@ -1,8 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit
|
||||
beStrictAboutOutputDuringTests="false"
|
||||
colors="true"
|
||||
stopOnFailure="false"
|
||||
bootstrap="./../tests/bootstrap.php">
|
||||
bootstrap="./../tests/bootstrap.php"
|
||||
verbose="true"
|
||||
>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">./../src/</directory>
|
||||
|
125
composer.json
125
composer.json
@ -1,60 +1,71 @@
|
||||
{
|
||||
"name":"aviat/query",
|
||||
"type":"library",
|
||||
"description":"Database Query Builder and Abstraction layer",
|
||||
"keywords":[
|
||||
"database",
|
||||
"query builder",
|
||||
"codeigniter",
|
||||
"mysql",
|
||||
"sqlite",
|
||||
"postgres",
|
||||
"pdo"
|
||||
],
|
||||
"homepage":"https://git.timshomepage.net/aviat/Query",
|
||||
"license":"MIT",
|
||||
"authors": [{
|
||||
"name": "Timothy J. Warren",
|
||||
"email": "tim@timshomepage.net",
|
||||
"homepage": "https://timshomepage.net",
|
||||
"role": "Developer"
|
||||
}],
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"ext-pdo": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"consolidation/robo": "^2.0.0",
|
||||
"monolog/monolog": "^2.0.1",
|
||||
"pdepend/pdepend": "^2.5",
|
||||
"phploc/phploc": "^5.0",
|
||||
"phpstan/phpstan": "^0.12.2",
|
||||
"phpunit/phpunit": "^8.5",
|
||||
"sebastian/phpcpd": "^4.1",
|
||||
"simpletest/simpletest": "^1.1",
|
||||
"squizlabs/php_codesniffer": "^3.0.0",
|
||||
"theseer/phpdox": "^0.12.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Query\\": "src"
|
||||
},
|
||||
"files": ["src/common.php"]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Query\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "robo build",
|
||||
"clean": "robo clean",
|
||||
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
|
||||
"phpstan": "phpstan analyse -l 3 -c phpstan.neon src tests",
|
||||
"test": "phpunit -c build --no-coverage"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"coverage": "Generate test coverage report",
|
||||
"test": "Run unit tests"
|
||||
"name": "aviat/query",
|
||||
"type": "library",
|
||||
"description": "Database Query Builder and Abstraction layer",
|
||||
"keywords": [
|
||||
"database",
|
||||
"query builder",
|
||||
"codeigniter",
|
||||
"mysql",
|
||||
"sqlite",
|
||||
"postgres",
|
||||
"pdo"
|
||||
],
|
||||
"homepage": "https://git.timshomepage.net/aviat/Query",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Timothy J. Warren",
|
||||
"email": "tim@timshomepage.net",
|
||||
"homepage": "https://timshomepage.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"lock": false,
|
||||
"platform": {
|
||||
"php": "7.3"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.3",
|
||||
"ext-pdo": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"consolidation/robo": "^2.0.0",
|
||||
"monolog/monolog": "^2.0.1",
|
||||
"phploc/phploc": "^5.0",
|
||||
"phpmd/phpmd": "^2.8",
|
||||
"phpstan/phpstan": "^0.12.2",
|
||||
"phpunit/phpunit": "^8.5",
|
||||
"sebastian/phpcpd": "^4.1",
|
||||
"simpletest/simpletest": "^1.1",
|
||||
"squizlabs/php_codesniffer": "^3.0.0",
|
||||
"theseer/phpdox": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Query\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/common.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Query\\Tests\\": "tests"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "robo build",
|
||||
"clean": "robo clean",
|
||||
"coverage": "phpdbg -qrr -- vendor/bin/phpunit -c build",
|
||||
"pcov": "vendor/bin/phpunit -c build",
|
||||
"phpstan": "phpstan analyse -l 3 -c phpstan.neon src tests",
|
||||
"test": "phpunit -c build --no-coverage"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"coverage": "Generate test coverage report",
|
||||
"test": "Run unit tests"
|
||||
}
|
||||
}
|
||||
|
12
phpdox.xml
12
phpdox.xml
@ -78,24 +78,20 @@
|
||||
</source>
|
||||
|
||||
<!-- git vcs information -->
|
||||
<source type="git">
|
||||
<!-- <source type="git">
|
||||
<git binary="/usr/bin/git" />
|
||||
<history enabled="true" limit="15" cache="${phpDox.project.workdir}/gitlog.xml" />
|
||||
</source>
|
||||
</source> -->
|
||||
|
||||
<!-- PHP Code Sniffer findings -->
|
||||
<!--
|
||||
<source type="phpcs">
|
||||
<source type="checkstyle">
|
||||
<file name="phpcs.xml" />
|
||||
</source>
|
||||
-->
|
||||
|
||||
<!-- PHPMessDetector -->
|
||||
<!--
|
||||
<source type="pmd">
|
||||
<file name="pmd.xml" />
|
||||
<file name="phpmd.xml" />
|
||||
</source>
|
||||
-->
|
||||
|
||||
<!-- PHPUnit Coverage XML -->
|
||||
<!--
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
namespace Query;
|
||||
|
||||
use function is_array;
|
||||
use function regexInArray;
|
||||
|
||||
use BadMethodCallException;
|
||||
@ -46,6 +47,7 @@ use Query\Drivers\DriverInterface;
|
||||
* @method getTriggers(): array | null
|
||||
* @method getTypes(): array | null
|
||||
* @method getUtil(): \Query\Drivers\AbstractUtil
|
||||
* @method getVersion(): string
|
||||
* @method getViews(): array | null
|
||||
* @method inTransaction(): bool
|
||||
* @method lastInsertId(string $name = NULL): string
|
||||
@ -78,10 +80,16 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
|
||||
/**
|
||||
* Whether to do only an explain on the query
|
||||
* @var boolean
|
||||
* @var bool
|
||||
*/
|
||||
protected $explain = FALSE;
|
||||
|
||||
/**
|
||||
* Whether to return data from a modification query
|
||||
* @var bool
|
||||
*/
|
||||
protected $returning = FALSE;
|
||||
|
||||
/**
|
||||
* The current database driver
|
||||
* @var DriverInterface
|
||||
@ -142,7 +150,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
{
|
||||
if (method_exists($this->driver, $name))
|
||||
{
|
||||
return \call_user_func_array([$this->driver, $name], $params);
|
||||
return $this->driver->$name(...$params);
|
||||
}
|
||||
|
||||
throw new BadMethodCallException('Method does not exist');
|
||||
@ -180,11 +188,11 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
unset($fieldsArray);
|
||||
|
||||
// Join the strings back together
|
||||
for($i = 0, $c = count($safeArray); $i < $c; $i++)
|
||||
foreach ($safeArray as $i => $iValue)
|
||||
{
|
||||
if (\is_array($safeArray[$i]))
|
||||
if (is_array($iValue))
|
||||
{
|
||||
$safeArray[$i] = implode(' AS ', $safeArray[$i]);
|
||||
$safeArray[$i] = implode(' AS ', $iValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -250,12 +258,21 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo implement
|
||||
* Add a 'returning' clause to an insert,update, or delete query
|
||||
*
|
||||
* @param string $fields
|
||||
* @return $this
|
||||
*/
|
||||
public function returning(string $fields = '*'): QueryBuilderInterface
|
||||
public function returning(string $fields = ''): QueryBuilderInterface
|
||||
{
|
||||
$this->returning = TRUE;
|
||||
|
||||
// Re-use the string select field for generating the returning type clause
|
||||
if ($fields !== '')
|
||||
{
|
||||
return $this->select($fields);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -762,7 +779,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
$this->from($table);
|
||||
}
|
||||
|
||||
$result = $this->_run('get', $table, NULL, NULL, $reset);
|
||||
$result = $this->_run(QueryType::SELECT, $table, NULL, NULL, $reset);
|
||||
$rows = $result->fetchAll();
|
||||
|
||||
return (int) count($rows);
|
||||
@ -782,7 +799,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
$this->set($data);
|
||||
}
|
||||
|
||||
return $this->_run('insert', $table);
|
||||
return $this->_run(QueryType::INSERT, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -797,6 +814,8 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
// Get the generated values and sql string
|
||||
[$sql, $data] = $this->driver->insertBatch($table, $data);
|
||||
|
||||
|
||||
|
||||
return $sql !== NULL
|
||||
? $this->_run('', $table, $sql, $data)
|
||||
: NULL;
|
||||
@ -816,7 +835,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
$this->set($data);
|
||||
}
|
||||
|
||||
return $this->_run('update', $table);
|
||||
return $this->_run(QueryType::UPDATE, $table);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -857,7 +876,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
$this->where($where);
|
||||
}
|
||||
|
||||
return $this->_run('delete', $table);
|
||||
return $this->_run(QueryType::DELETE, $table);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
@ -879,7 +898,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
$this->from($table);
|
||||
}
|
||||
|
||||
return $this->_getCompile('select', $table, $reset);
|
||||
return $this->_getCompile(QueryType::SELECT, $table, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -891,7 +910,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
*/
|
||||
public function getCompiledInsert(string $table, bool $reset=TRUE): string
|
||||
{
|
||||
return $this->_getCompile('insert', $table, $reset);
|
||||
return $this->_getCompile(QueryType::INSERT, $table, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -903,7 +922,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
*/
|
||||
public function getCompiledUpdate(string $table='', bool $reset=TRUE): string
|
||||
{
|
||||
return $this->_getCompile('update', $table, $reset);
|
||||
return $this->_getCompile(QueryType::UPDATE, $table, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -915,7 +934,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
*/
|
||||
public function getCompiledDelete(string $table='', bool $reset=TRUE): string
|
||||
{
|
||||
return $this->_getCompile('delete', $table, $reset);
|
||||
return $this->_getCompile(QueryType::DELETE, $table, $reset);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
@ -931,6 +950,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
{
|
||||
$this->state = new State();
|
||||
$this->explain = FALSE;
|
||||
$this->returning = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1204,7 +1224,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
*/
|
||||
protected function _appendQuery(array $values, string $sql, int $totalTime): void
|
||||
{
|
||||
$evals = \is_array($values) ? $values : [];
|
||||
$evals = is_array($values) ? $values : [];
|
||||
$esql = str_replace('?', '%s', $sql);
|
||||
|
||||
// Quote string values
|
||||
@ -1240,29 +1260,29 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
* @param string $table
|
||||
* @return string
|
||||
*/
|
||||
protected function _compileType(string $type='', string $table=''): string
|
||||
protected function _compileType(string $type=QueryType::SELECT, string $table=''): string
|
||||
{
|
||||
$setArrayKeys = $this->state->getSetArrayKeys();
|
||||
switch($type)
|
||||
{
|
||||
case 'insert':
|
||||
case QueryType::INSERT:
|
||||
$paramCount = count($setArrayKeys);
|
||||
$params = array_fill(0, $paramCount, '?');
|
||||
$sql = "INSERT INTO {$table} ("
|
||||
. implode(',', $setArrayKeys)
|
||||
. ")\nVALUES (".implode(',', $params).')';
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
case QueryType::UPDATE:
|
||||
$setString = $this->state->getSetString();
|
||||
$sql = "UPDATE {$table}\nSET {$setString}";
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
case QueryType::DELETE:
|
||||
$sql = "DELETE FROM {$table}";
|
||||
break;
|
||||
break;
|
||||
|
||||
// Get queries
|
||||
case QueryType::SELECT:
|
||||
default:
|
||||
$fromString = $this->state->getFromString();
|
||||
$selectString = $this->state->getSelectString();
|
||||
@ -1275,7 +1295,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
// Replace the star with the selected fields
|
||||
$sql = str_replace('*', $selectString, $sql);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
return $sql;
|
||||
@ -1305,7 +1325,7 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
{
|
||||
$func = 'get' . ucFirst($clause);
|
||||
$param = $this->state->$func();
|
||||
if (\is_array($param))
|
||||
if (is_array($param))
|
||||
{
|
||||
foreach($param as $q)
|
||||
{
|
||||
@ -1325,6 +1345,9 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
$sql = $this->driver->getSql()->limit($sql, $limit, $this->state->getOffset());
|
||||
}
|
||||
|
||||
// Set the returning clause, if applicable
|
||||
$sql = $this->_compileReturning($sql, $type);
|
||||
|
||||
// See if the query plan, rather than the
|
||||
// query data should be returned
|
||||
if ($this->explain === TRUE)
|
||||
@ -1334,4 +1357,51 @@ class QueryBuilder implements QueryBuilderInterface {
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate returning clause of query
|
||||
*
|
||||
* @param string $sql
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
protected function _compileReturning(string $sql, string $type): string
|
||||
{
|
||||
if ($this->returning === FALSE)
|
||||
{
|
||||
return $sql;
|
||||
}
|
||||
|
||||
$rawSelect = $this->state->getSelectString();
|
||||
$selectString = ($rawSelect === '') ? '*' : $rawSelect;
|
||||
$returningSQL = $this->driver->returning($sql, $selectString);
|
||||
|
||||
if ($returningSQL === $sql)
|
||||
{
|
||||
// If the driver doesn't support the returning clause, it returns the original query.
|
||||
// Fake the same result with a transaction and a select query
|
||||
if ( ! $this->inTransaction())
|
||||
{
|
||||
$this->beginTransaction();
|
||||
}
|
||||
|
||||
// Generate the appropriate select query for the returning clause fallback
|
||||
switch ($type)
|
||||
{
|
||||
case QueryType::INSERT:
|
||||
// @TODO figure out a good response for insert query
|
||||
break;
|
||||
|
||||
case QueryType::UPDATE:
|
||||
// @TODO figure out a good response for update query
|
||||
break;
|
||||
|
||||
case QueryType::DELETE:
|
||||
// @TODO Figure out a good response for delete query
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $returningSQL;
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ use PDOStatement;
|
||||
* @method getTriggers(): array | null
|
||||
* @method getTypes(): array | null
|
||||
* @method getUtil(): \Query\Drivers\AbstractUtil
|
||||
* @method getVersion(): string
|
||||
* @method getViews(): array | null
|
||||
* @method inTransaction(): bool
|
||||
* @method lastInsertId(string $name = NULL): string
|
||||
@ -106,6 +107,14 @@ interface QueryBuilderInterface {
|
||||
*/
|
||||
public function selectSum(string $field, $as=FALSE): self;
|
||||
|
||||
/**
|
||||
* Add a 'returning' clause to an insert,update, or delete query
|
||||
*
|
||||
* @param string $fields
|
||||
* @return self
|
||||
*/
|
||||
public function returning(string $fields = '*'): self;
|
||||
|
||||
/**
|
||||
* Adds the 'distinct' keyword to a query
|
||||
*
|
||||
|
177
src/State.php
177
src/State.php
@ -17,6 +17,25 @@ namespace Query;
|
||||
|
||||
/**
|
||||
* Query builder state
|
||||
*
|
||||
* @method getSelectString(): string
|
||||
* @method getFromString(): string
|
||||
* @method getSetString(): string
|
||||
* @method getOrderString(): string
|
||||
* @method getGroupString(): string
|
||||
* @method getSetArrayKeys(): array
|
||||
* @method getOrderArray(): array
|
||||
* @method getGroupArray(): array
|
||||
* @method getValues(): array
|
||||
* @method getWhereValues(): array
|
||||
* @method getLimit(): int
|
||||
* @method getOffset()
|
||||
* @method getQueryMap(): array
|
||||
* @method getHavingMap(): array
|
||||
*
|
||||
* @method setSelectString(string): self
|
||||
* @method setFromString(string): self
|
||||
* @method setSetString(string): self
|
||||
*/
|
||||
class State {
|
||||
// --------------------------------------------------------------------------
|
||||
@ -124,22 +143,28 @@ class State {
|
||||
*/
|
||||
protected $havingMap = [];
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @return State
|
||||
*/
|
||||
public function setSelectString(string $str): self
|
||||
public function __call(string $name, array $arguments)
|
||||
{
|
||||
$this->selectString = $str;
|
||||
return $this;
|
||||
}
|
||||
if (strpos($name, 'get', 0) === 0)
|
||||
{
|
||||
$maybeProp = lcfirst(substr($name, 3));
|
||||
if (isset($this->$maybeProp))
|
||||
{
|
||||
return $this->$maybeProp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSelectString(): string
|
||||
{
|
||||
return $this->selectString;
|
||||
if (strpos($name, 'set', 0) === 0)
|
||||
{
|
||||
$maybeProp = lcfirst(substr($name, 3));
|
||||
if (isset($this->$maybeProp))
|
||||
{
|
||||
$this->$maybeProp = $arguments[0];
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,50 +177,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getFromString(): string
|
||||
{
|
||||
return $this->fromString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $fromString
|
||||
* @return State
|
||||
*/
|
||||
public function setFromString(string $fromString): self
|
||||
{
|
||||
$this->fromString = $fromString;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSetString(): string
|
||||
{
|
||||
return $this->setString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $setString
|
||||
* @return State
|
||||
*/
|
||||
public function setSetString(string $setString): self
|
||||
{
|
||||
$this->setString = $setString;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOrderString(): string
|
||||
{
|
||||
return $this->orderString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $orderString
|
||||
* @return State
|
||||
@ -206,14 +187,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupString(): string
|
||||
{
|
||||
return $this->groupString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $groupString
|
||||
* @return State
|
||||
@ -224,14 +197,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSetArrayKeys(): array
|
||||
{
|
||||
return $this->setArrayKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $setArrayKeys
|
||||
* @return State
|
||||
@ -252,14 +217,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getOrderArray(): array
|
||||
{
|
||||
return $this->orderArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param mixed $orderArray
|
||||
@ -271,14 +228,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupArray(): array
|
||||
{
|
||||
return $this->groupArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $groupArray
|
||||
* @return State
|
||||
@ -299,14 +248,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getValues(): array
|
||||
{
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $values
|
||||
* @return State
|
||||
@ -317,14 +258,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getWhereValues(): array
|
||||
{
|
||||
return $this->whereValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $val
|
||||
* @return State
|
||||
@ -345,14 +278,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getLimit(): ?int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
* @return State
|
||||
@ -363,14 +288,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|false
|
||||
*/
|
||||
public function getOffset()
|
||||
{
|
||||
return $this->offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|false $offset
|
||||
* @return State
|
||||
@ -381,14 +298,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getQueryMap(): array
|
||||
{
|
||||
return $this->queryMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an additional set of mapping pairs to a internal map
|
||||
*
|
||||
@ -407,14 +316,6 @@ class State {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHavingMap(): array
|
||||
{
|
||||
return $this->havingMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $item
|
||||
* @return State
|
||||
|
@ -557,6 +557,24 @@ abstract class BaseQueryBuilderTest extends TestCase {
|
||||
$this->assertIsA($query, 'PDOStatement');
|
||||
}
|
||||
|
||||
public function testInsertReturning(): void
|
||||
{
|
||||
$query = self::$db->set('id', 99)
|
||||
->set('key', 84)
|
||||
->set('val', 120)
|
||||
->returning()
|
||||
->insert('test');
|
||||
|
||||
$row = $query->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$this->assertIsA($query, 'PDOStatement');
|
||||
$this->assertEqual([
|
||||
'id' => 99,
|
||||
'key' => 84,
|
||||
'val' => 120,
|
||||
], $row);
|
||||
}
|
||||
|
||||
public function testInsertBatch(): void
|
||||
{
|
||||
$data = [
|
||||
@ -594,6 +612,33 @@ abstract class BaseQueryBuilderTest extends TestCase {
|
||||
$this->assertIsA($query, 'PDOStatement');
|
||||
}
|
||||
|
||||
public function testUpdateReturning(): void
|
||||
{
|
||||
// Make sure the id exists so there isn't an error for that reason
|
||||
self::$db->insert('test', [
|
||||
'id' => 7,
|
||||
'key' => 'kjsgarik yugasdikh',
|
||||
'val' => 'alkfiasghdfdhs',
|
||||
]);
|
||||
|
||||
$query = self::$db->where('id', 7)
|
||||
->set([
|
||||
'id' => 7,
|
||||
'key' => 'gogle',
|
||||
'val' => 'non-word'
|
||||
])
|
||||
->returning('key')
|
||||
->update('test');
|
||||
|
||||
$this->assertIsA($query, 'PDOStatement');
|
||||
$row = $query->fetch(PDO::FETCH_ASSOC);
|
||||
$this->assertEqual([
|
||||
'key' => 'gogle',
|
||||
], $row, json_encode($query->errorInfo()));
|
||||
|
||||
// $this->assertNotEqual(FALSE, $row, $query->errorInfo());
|
||||
}
|
||||
|
||||
public function testUpdateBatchNull(): void
|
||||
{
|
||||
$query = self::$db->updateBatch('test', [], '');
|
||||
@ -652,6 +697,13 @@ abstract class BaseQueryBuilderTest extends TestCase {
|
||||
$this->assertIsA($query, 'PDOStatement');
|
||||
}
|
||||
|
||||
public function testDeleteReturning():void
|
||||
{
|
||||
$query = self::$db->returning()->delete('test', ['id' => 99]);
|
||||
|
||||
$this->assertIsA($query, 'PDOStatement');
|
||||
}
|
||||
|
||||
public function testDeleteWithMultipleWhereValues(): void
|
||||
{
|
||||
$query = self::$db->delete('test', [
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* @package Query
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2012 - 2020 Timothy J. Warren
|
||||
* @copyright 2012 - 2019 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link https://git.timshomepage.net/aviat/Query
|
||||
* @version 3.0.0
|
||||
@ -72,4 +72,14 @@ class MySQLQueryBuilderTest extends BaseQueryBuilderTest {
|
||||
$this->assertTrue(count(array_keys($res[0])) > 1);
|
||||
$this->assertTrue(array_key_exists('table', $res[0]));
|
||||
}
|
||||
|
||||
public function testInsertReturning(): void
|
||||
{
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
public function testUpdateReturning(): void
|
||||
{
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
* @package Query
|
||||
* @author Timothy J. Warren <tim@timshomepage.net>
|
||||
* @copyright 2012 - 2020 Timothy J. Warren
|
||||
* @copyright 2012 - 2019 Timothy J. Warren
|
||||
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
||||
* @link https://git.timshomepage.net/aviat/Query
|
||||
* @version 3.0.0
|
||||
@ -113,4 +113,14 @@ use Query\Tests\BaseQueryBuilderTest;
|
||||
$this->assertTrue(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
public function testInsertReturning(): void
|
||||
{
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
|
||||
public function testUpdateReturning(): void
|
||||
{
|
||||
$this->markTestSkipped();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user