Добавлен цикл проверки сайтов

This commit is contained in:
Alexander 2024-08-12 15:14:49 +03:00
parent 0b56cd37b5
commit 6b7199a326
115 changed files with 15964 additions and 88 deletions

View File

@ -1,9 +1,15 @@
<?php <?php
namespace App; namespace App;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use App\Library\ContingentParser; use App\Library\ContingentParser;
use App\Library\Database; use App\Library\Database;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
$dbconfig = [ $dbconfig = [
"host" => "10.90.1.201", "host" => "10.90.1.201",
@ -24,25 +30,61 @@ $dbNiimko = new Database(
$dbconfig['user'], $dbconfig['user'],
$dbconfig['password'] $dbconfig['password']
); );
$builder = new GenericBuilder();
$params = ['vuz', 'n', 'n'];
$query = $builder->select()
->setTable('s_vuzes')
->setColumns(['org_id' => 'kod', 'site'])
->where('AND')
->equals('ootype', 'vuz')
->equals('deleted', 'n')
->equals('fake', 'n')
->end();
$sql = $builder->write($query);
$sites = $dbNiimko->executeQuery($sql, $params);
$client = new Client(); // print_r($sites);
$response = $client->get('https://marsu.ru/sveden/education/eduChislen.php'); // $sites = [ ['site' => "http://marsu.ru"], ['site' => "http://voenmeh.ru"], ['site' => "http://angtu.ru"] ];
$html = $response->getBody()->getContents(); $i = 0;
$parser = new ContingentParser($html, '//tr[@itemprop="eduChislen"]//'); foreach ($sites as $site) {
try {
$client = new Client();
$contingent = $parser->getDataTable(); $route = "{$site['site']}/sveden/education/";
$specializations = $dbNiimko->selectWhere('s_specs', ['id', 'kod'], ['oopkodes' => 'gos3p']); $route = str_replace("http","https", $route);
$route = str_replace("www.","", $route);
print(++$i.". Current url: $route\n");
foreach ($contingent as $key => $con) { $response = $client->get($route);
$needle = $con['spec_code']; print("StatusCode: ".$response->getStatusCode() . "\n");
foreach ($specializations as $spec) {
if ($needle == $spec['kod']) { $html = $response->getBody()->getContents();
$contingent[$key] += ['spec_id' => $spec['id']]; $parser = new ContingentParser($html, '//tr[@itemprop="eduChislen"]//');
} $contingent = $parser->getDataTable();
print_r($contingent);
} catch (ClientException $e) {
$response = $e->getCode();
} catch (RequestException $e) {
$response = $e->getCode();
} catch (ConnectException $e) {
$response = $e->getCode();
} catch (ServerException $e) {
$response = $e->getCode();
} }
} }
// $specializations = $dbNiimko->selectWhere('s_specs', ['id', 'kod'], ['oopkodes' => 'gos3p']);
// foreach ($contingent as $key => $con) {
// $needle = $con['spec_code'];
// foreach ($specializations as $spec) {
// if ($needle == $spec['kod']) {
// $contingent[$key] += ['spec_id' => $spec['id']];
// }
// }
// }
// Чтобы не дублировались в базе
// $dbOpendata->insert('sveden_education_contingent', $data); // $dbOpendata->insert('sveden_education_contingent', $data);
// $dbOpendata = $db->select('sveden_education_contingent'); // $dbOpendata->update('sveden_education_contingent', $specializations);
echo "<pre>"; // $data = $dbOpendata->select('sveden_education_contingent');
print_r($contingent);
echo "</pre>";

View File

@ -20,7 +20,7 @@ class ContingentParser
"numberBMF" => "th", "numberBMF" => "th",
"numberP" => "th", "numberP" => "th",
"numberPF" => "th", "numberPF" => "th",
"numberAll" => "th" "numberAll" => ["th", "td"]
]; ];
public function __construct(string $html, string $template) public function __construct(string $html, string $template)
@ -36,7 +36,14 @@ class ContingentParser
{ {
$data = array(); $data = array();
foreach (self::FIELDS as $field => $tag) { foreach (self::FIELDS as $field => $tag) {
if (!is_array($tag)) {
$data[$field] = $this->xpath->query($this->template . $tag . "[@itemprop=\"$field\"]"); $data[$field] = $this->xpath->query($this->template . $tag . "[@itemprop=\"$field\"]");
} else {
$x = $this->xpath->query($this->template . $tag[0] . "[@itemprop=\"$field\"]");
$y = $this->xpath->query($this->template . $tag[1] . "[@itemprop=\"$field\"]");
$data[$field] = $x > $y ? $x : $y;
}
} }
return $data; return $data;
} }
@ -46,7 +53,7 @@ class ContingentParser
$data = $this->parse(); $data = $this->parse();
$records = array(); $records = array();
for ($i = 0; $i < $data['numberAll']->length; $i++) { for ($i = 0; $i < $data['eduCode']->length; $i++) {
$contingentRow = new ContingentRow( $contingentRow = new ContingentRow(
$data['eduCode']->item($i)->textContent, $data['eduCode']->item($i)->textContent,
$data['eduName']->item($i)->textContent, $data['eduName']->item($i)->textContent,

View File

@ -1,9 +1,8 @@
<?php <?php
namespace App\Library; namespace App\Library;
use PDO;
use PDOException; use PDOException;
use PDO;
class Database extends PDO class Database extends PDO
{ {
public function __construct(string $dsn, string $username, string $password) public function __construct(string $dsn, string $username, string $password)
@ -27,65 +26,13 @@ class Database extends PDO
echo "Подключение прервано!\n"; echo "Подключение прервано!\n";
} }
public function insert(string $table, array $data) : void public function executeQuery(string $sql, array $params) : array|bool
{ {
$keys = array_keys($data); try {
$sql = "INSERT INTO $table (";
foreach ($keys as $key) {
$sql .= "$key,";
}
$sql = substr_replace($sql, ")", -1);
$sql .= " VALUES (";
foreach ($keys as $key) {
$sql .= ":$key,";
}
$sql = substr_replace($sql,"", -1);
$stmt = $this->prepare($sql); $stmt = $this->prepare($sql);
for ($i = 0; $i < count($params); $i++) {
foreach ($data as $key => $row) { $stmt->bindParam(":v".$i+1, $params[$i]);
try {
$stmt->bindParam(":$key", $row[$key]);
$stmt->execute();
} catch (PDOException $e) {
echo "Ошибка запроса: " . $e->getMessage() . "\n";
} }
}
}
public function select(string $table) : array|null
{
$stmt = $this->prepare("SELECT * FROM $table");
try {
$stmt->execute();
$array = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
echo "Ошибка запроса: " . $e->getMessage() . "\n";
} finally {
return $array;
}
}
public function selectWhere(
string $table,
array $atributes,
array $filters
) : array|null {
// Строим запрос
$sql = "SELECT ";
foreach ($atributes as $atribute) {
$sql .= "$atribute,";
}
$sql = substr_replace($sql," ", -1);
$sql .= "FROM $table WHERE ";
foreach ($filters as $key => $filter) {
$sql .= "$key = :$key ";
}
$stmt = $this->prepare($sql);
foreach ($filters as $key => $filter) {
$stmt->bindParam(":$key", $filter);
}
// Выполняем
try {
$stmt->execute(); $stmt->execute();
$array = $stmt->fetchAll(PDO::FETCH_ASSOC); $array = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) { } catch (PDOException $e) {

View File

@ -1,6 +1,7 @@
{ {
"require": { "require": {
"guzzlehttp/guzzle": "^7.0" "guzzlehttp/guzzle": "^7.0",
"nilportugues/sql-query-builder": "^1.8"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

129
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "045658d81f6d9d3243e731dda7bf04d1", "content-hash": "9f2bc33681a074e74e50dc6856b08313",
"packages": [ "packages": [
{ {
"name": "guzzlehttp/guzzle", "name": "guzzlehttp/guzzle",
@ -331,6 +331,133 @@
], ],
"time": "2024-07-18T11:15:46+00:00" "time": "2024-07-18T11:15:46+00:00"
}, },
{
"name": "nilportugues/sql-query-builder",
"version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/nilportugues/php-sql-query-builder.git",
"reference": "cc3401099328ddfad54b2b801e38e05a50da11bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nilportugues/php-sql-query-builder/zipball/cc3401099328ddfad54b2b801e38e05a50da11bd",
"reference": "cc3401099328ddfad54b2b801e38e05a50da11bd",
"shasum": ""
},
"require": {
"nilportugues/sql-query-formatter": "~1.2",
"php": ">=5.5"
},
"require-dev": {
"fabpot/php-cs-fixer": "~1.9",
"nilportugues/php_backslasher": "~0.2",
"phpunit/phpunit": "4.*"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"NilPortugues\\Sql\\QueryBuilder\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nil Portugués Calderó",
"email": "contact@nilportugues.com",
"homepage": "http://nilportugues.com",
"role": "Lead Developer"
},
{
"name": "Woody Gilk",
"email": "woody.gilk@gmail.com",
"homepage": "http://shadowhand.me/",
"role": "Contributor"
}
],
"description": "An elegant lightweight and efficient SQL QueryInterface BuilderInterface supporting bindings and complicated query generation.",
"homepage": "http://nilportugues.com",
"keywords": [
"builder",
"mysql",
"orm",
"query",
"query builder",
"sql"
],
"support": {
"issues": "https://github.com/nilportugues/php-sql-query-builder/issues",
"source": "https://github.com/nilportugues/php-sql-query-builder/tree/1.8.0"
},
"time": "2020-04-07T22:51:47+00:00"
},
{
"name": "nilportugues/sql-query-formatter",
"version": "v1.2.2",
"source": {
"type": "git",
"url": "https://github.com/nilportugues/sql-query-formatter.git",
"reference": "a539162a13e3217827237d5809fbaf25e136dc0e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nilportugues/sql-query-formatter/zipball/a539162a13e3217827237d5809fbaf25e136dc0e",
"reference": "a539162a13e3217827237d5809fbaf25e136dc0e",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"fabpot/php-cs-fixer": "~1.9",
"nilportugues/php_backslasher": "~0.2",
"phpunit/phpunit": "4.*"
},
"type": "library",
"autoload": {
"psr-4": {
"NilPortugues\\Sql\\QueryFormatter\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nil Portugués Calderó",
"email": "contact@nilportugues.com",
"homepage": "http://nilportugues.com",
"role": "Lead Developer"
}
],
"description": "A very lightweight PHP class that reformats unreadable and computer-generated SQL query statements to human-friendly, readable text.",
"homepage": "http://nilportugues.com",
"keywords": [
"format",
"formatter",
"mysql",
"parser",
"query",
"reformat",
"sql",
"sql server",
"tokenizer"
],
"support": {
"issues": "https://github.com/nilportugues/sql-query-formatter/issues",
"source": "https://github.com/nilportugues/sql-query-formatter/tree/master"
},
"time": "2015-11-02T23:24:39+00:00"
},
{ {
"name": "psr/http-client", "name": "psr/http-client",
"version": "1.0.3", "version": "1.0.3",

View File

@ -1,6 +1,6 @@
create table sveden_education_contingent create table sveden_education_contingent
( (
id int not null primary key, id serial not null primary key,
org_id int null, org_id int null,
spec_id int null, spec_id int null,
spec_code varchar(100) null comment 'Код', spec_code varchar(100) null comment 'Код',

View File

@ -8,6 +8,8 @@ $baseDir = dirname($vendorDir);
return array( return array(
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'), 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'), 'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'NilPortugues\\Sql\\QueryFormatter\\' => array($vendorDir . '/nilportugues/sql-query-formatter/src'),
'NilPortugues\\Sql\\QueryBuilder\\' => array($vendorDir . '/nilportugues/sql-query-builder/src'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),

View File

@ -18,6 +18,11 @@ class ComposerStaticInit045658d81f6d9d3243e731dda7bf04d1
'Psr\\Http\\Message\\' => 17, 'Psr\\Http\\Message\\' => 17,
'Psr\\Http\\Client\\' => 16, 'Psr\\Http\\Client\\' => 16,
), ),
'N' =>
array (
'NilPortugues\\Sql\\QueryFormatter\\' => 32,
'NilPortugues\\Sql\\QueryBuilder\\' => 30,
),
'G' => 'G' =>
array ( array (
'GuzzleHttp\\Psr7\\' => 16, 'GuzzleHttp\\Psr7\\' => 16,
@ -41,6 +46,14 @@ class ComposerStaticInit045658d81f6d9d3243e731dda7bf04d1
array ( array (
0 => __DIR__ . '/..' . '/psr/http-client/src', 0 => __DIR__ . '/..' . '/psr/http-client/src',
), ),
'NilPortugues\\Sql\\QueryFormatter\\' =>
array (
0 => __DIR__ . '/..' . '/nilportugues/sql-query-formatter/src',
),
'NilPortugues\\Sql\\QueryBuilder\\' =>
array (
0 => __DIR__ . '/..' . '/nilportugues/sql-query-builder/src',
),
'GuzzleHttp\\Psr7\\' => 'GuzzleHttp\\Psr7\\' =>
array ( array (
0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src', 0 => __DIR__ . '/..' . '/guzzlehttp/psr7/src',

View File

@ -334,6 +334,139 @@
], ],
"install-path": "../guzzlehttp/psr7" "install-path": "../guzzlehttp/psr7"
}, },
{
"name": "nilportugues/sql-query-builder",
"version": "1.8.0",
"version_normalized": "1.8.0.0",
"source": {
"type": "git",
"url": "https://github.com/nilportugues/php-sql-query-builder.git",
"reference": "cc3401099328ddfad54b2b801e38e05a50da11bd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nilportugues/php-sql-query-builder/zipball/cc3401099328ddfad54b2b801e38e05a50da11bd",
"reference": "cc3401099328ddfad54b2b801e38e05a50da11bd",
"shasum": ""
},
"require": {
"nilportugues/sql-query-formatter": "~1.2",
"php": ">=5.5"
},
"require-dev": {
"fabpot/php-cs-fixer": "~1.9",
"nilportugues/php_backslasher": "~0.2",
"phpunit/phpunit": "4.*"
},
"time": "2020-04-07T22:51:47+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"NilPortugues\\Sql\\QueryBuilder\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nil Portugués Calderó",
"email": "contact@nilportugues.com",
"homepage": "http://nilportugues.com",
"role": "Lead Developer"
},
{
"name": "Woody Gilk",
"email": "woody.gilk@gmail.com",
"homepage": "http://shadowhand.me/",
"role": "Contributor"
}
],
"description": "An elegant lightweight and efficient SQL QueryInterface BuilderInterface supporting bindings and complicated query generation.",
"homepage": "http://nilportugues.com",
"keywords": [
"builder",
"mysql",
"orm",
"query",
"query builder",
"sql"
],
"support": {
"issues": "https://github.com/nilportugues/php-sql-query-builder/issues",
"source": "https://github.com/nilportugues/php-sql-query-builder/tree/1.8.0"
},
"install-path": "../nilportugues/sql-query-builder"
},
{
"name": "nilportugues/sql-query-formatter",
"version": "v1.2.2",
"version_normalized": "1.2.2.0",
"source": {
"type": "git",
"url": "https://github.com/nilportugues/sql-query-formatter.git",
"reference": "a539162a13e3217827237d5809fbaf25e136dc0e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nilportugues/sql-query-formatter/zipball/a539162a13e3217827237d5809fbaf25e136dc0e",
"reference": "a539162a13e3217827237d5809fbaf25e136dc0e",
"shasum": ""
},
"require": {
"php": ">=5.5"
},
"require-dev": {
"fabpot/php-cs-fixer": "~1.9",
"nilportugues/php_backslasher": "~0.2",
"phpunit/phpunit": "4.*"
},
"time": "2015-11-02T23:24:39+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"NilPortugues\\Sql\\QueryFormatter\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nil Portugués Calderó",
"email": "contact@nilportugues.com",
"homepage": "http://nilportugues.com",
"role": "Lead Developer"
}
],
"description": "A very lightweight PHP class that reformats unreadable and computer-generated SQL query statements to human-friendly, readable text.",
"homepage": "http://nilportugues.com",
"keywords": [
"format",
"formatter",
"mysql",
"parser",
"query",
"reformat",
"sql",
"sql server",
"tokenizer"
],
"support": {
"issues": "https://github.com/nilportugues/sql-query-formatter/issues",
"source": "https://github.com/nilportugues/sql-query-formatter/tree/master"
},
"install-path": "../nilportugues/sql-query-formatter"
},
{ {
"name": "psr/http-client", "name": "psr/http-client",
"version": "1.0.3", "version": "1.0.3",

View File

@ -1,9 +1,9 @@
<?php return array( <?php return array(
'root' => array( 'root' => array(
'name' => '__root__', 'name' => '__root__',
'pretty_version' => '1.0.0+no-version-set', 'pretty_version' => 'dev-main',
'version' => '1.0.0.0', 'version' => 'dev-main',
'reference' => null, 'reference' => '0b56cd37b55c2d4efaa3a70b1487cae267d9c700',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@ -11,9 +11,9 @@
), ),
'versions' => array( 'versions' => array(
'__root__' => array( '__root__' => array(
'pretty_version' => '1.0.0+no-version-set', 'pretty_version' => 'dev-main',
'version' => '1.0.0.0', 'version' => 'dev-main',
'reference' => null, 'reference' => '0b56cd37b55c2d4efaa3a70b1487cae267d9c700',
'type' => 'library', 'type' => 'library',
'install_path' => __DIR__ . '/../../', 'install_path' => __DIR__ . '/../../',
'aliases' => array(), 'aliases' => array(),
@ -46,6 +46,24 @@
'aliases' => array(), 'aliases' => array(),
'dev_requirement' => false, 'dev_requirement' => false,
), ),
'nilportugues/sql-query-builder' => array(
'pretty_version' => '1.8.0',
'version' => '1.8.0.0',
'reference' => 'cc3401099328ddfad54b2b801e38e05a50da11bd',
'type' => 'library',
'install_path' => __DIR__ . '/../nilportugues/sql-query-builder',
'aliases' => array(),
'dev_requirement' => false,
),
'nilportugues/sql-query-formatter' => array(
'pretty_version' => 'v1.2.2',
'version' => '1.2.2.0',
'reference' => 'a539162a13e3217827237d5809fbaf25e136dc0e',
'type' => 'library',
'install_path' => __DIR__ . '/../nilportugues/sql-query-formatter',
'aliases' => array(),
'dev_requirement' => false,
),
'psr/http-client' => array( 'psr/http-client' => array(
'pretty_version' => '1.0.3', 'pretty_version' => '1.0.3',
'version' => '1.0.3.0', 'version' => '1.0.3.0',

View File

@ -0,0 +1 @@
repo_token: LFYrNmWM52pVO2g5dKuvf8NXd9mQLukDM

View File

@ -0,0 +1,4 @@
build/
vendor/
bin/
composer.lock

View File

@ -0,0 +1,22 @@
language: php
php:
- "5.6"
- "7.0"
- "7.1"
- "7.2"
- "7.3"
- "7.4"
before_script:
- composer install
script:
- bin/phpunit --coverage-text
matrix:
allow_failures:
- php: "hhvm"
branches:
only:
- master

View File

@ -0,0 +1,41 @@
# Changelog
## Unreleased
### Added
- Added parentQuery field to Select Object
- New test for orderBy method in Select Class
### Altered
- Changed orderBy method logic in Select Class. Now every select query has access to his parent object. You can manipulate sequence of your orderBy clause.
## 1.0.2 - TBA
### Added
- Added @shadowhand (Woody Gilk) to the **composer.json** under authors.
- Comments for queries using the **setComment** method.
- Table aliasing.
### Altered
- Changed PSR-0 loading to PSR-4.
- Changed the class loading in GenericBuilder. Now a classmap is used to load the query builder classes only when required.
- Changed test method names to camelCase format.
- Normalized the way select, insert and update behave internally.
## 1.0.1 - 2014-09-23
### Altered
- Big change in class loading to reducing memory usage.
## 1.0.0 - 2014-09-13
- First stable release
## 0.0.5-alpha - 2014-07-01
- Initial release

View File

@ -0,0 +1,33 @@
Contributing
============
First of all, **thank you** for contributing, **you are awesome**!
Here are a few rules to follow in order to ease code reviews, and discussions before
maintainers accept and merge your work.
You MUST follow the [PSR-1](http://www.php-fig.org/psr/1/) and
[PSR-2](http://www.php-fig.org/psr/2/). If you don't know about any of them, you
should really read the recommendations. Can't wait? Use the [PHP-CS-Fixer
tool](http://cs.sensiolabs.org/).
You MUST run the test suite.
You MUST write (or update) unit tests.
You SHOULD write documentation.
Please, write [commit messages that make
sense](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
and [rebase your branch](http://git-scm.com/book/en/Git-Branching-Rebasing)
before submitting your Pull Request.
One may ask you to [squash your
commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)
too. This is used to "clean" your Pull Request before merging it (we don't want
commits such as `fix tests`, `fix 2`, `fix 3`, etc.).
Also, while creating your Pull Request on GitHub, you MUST write a description
which gives the context and/or explains why you are creating it.
Thank you!

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Nil Portugués Calderó
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,1159 @@
SQL Query Builder
=================
[![Build Status](https://travis-ci.org/nilportugues/php-sql-query-builder.svg)](https://travis-ci.org/nilportugues/php-sql-query-builder) [![Coverage Status](https://img.shields.io/coveralls/nilportugues/sql-query-builder.svg)](https://coveralls.io/r/nilportugues/sql-query-builder) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nilportugues/sql-query-builder/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nilportugues/sql-query-builder/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/89ec1003-4227-43a2-8432-67a9fc2d3ba3/mini.png)](https://insight.sensiolabs.com/projects/89ec1003-4227-43a2-8432-67a9fc2d3ba3) [![Latest Stable Version](https://poser.pugx.org/nilportugues/sql-query-builder/v/stable)](https://packagist.org/packages/nilportugues/sql-query-builder) [![Total Downloads](https://poser.pugx.org/nilportugues/sql-query-builder/downloads)](https://packagist.org/packages/nilportugues/sql-query-builder) [![License](https://poser.pugx.org/nilportugues/sql-query-builder/license)](https://packagist.org/packages/nilportugues/sql-query-builder)
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://paypal.me/nilportugues)
An elegant lightweight and efficient SQL Query Builder with fluid interface SQL syntax supporting bindings and complicated query generation. **Works without establishing a connection to the database.**
<a name="index_block"></a>
* [1. Installation](#block1)
* [2. The Builder](#block2)
* [2.1. Generic Builder](#block2.1)
* [2.2. MySQL Builder](#block2.2)
* [2.3. Human Readable Output](#block2.3)
* [3. Building Queries](#block3)
* [3.1. SELECT Statement](#block3.1)
* [3.1.1. Basic SELECT statement](#block3.1.1)
* [3.1.2. Aliased SELECT statement](#block3.1.2)
* [3.1.3. SELECT with WHERE statement](#block3.1.3)
* [3.1.4. Complex WHERE conditions](#block3.1.4)
* [3.1.5. JOIN & LEFT/RIGHT/INNER/CROSS JOIN SELECT statements](#block3.1.5)
* [3.1.6. COUNT rows](#block3.1.6)
* [3.2. INSERT Statement](#block3.2)
* [3.2.1. Basic INSERT statement](#block3.2.1)
* [3.3. UPDATE Statement](#block3.3)
* [3.3.1. Basic UPDATE statement](#block3.3.1)
* [3.3.2. Elaborated UPDATE statement](#block3.3.2)
* [3.4. DELETE Statement](#block3.4)
* [3.4.1. Empty table with DELETE statement](#block3.4.1)
* [3.4.2. Basic DELETE statement](#block3.4.2)
* [3.4.3. Elaborated DELETE statement](#block3.4.3)
* [3.5. INTERSECT Statement](#block3.5)
* [3.6. MINUS Statement](#block3.6)
* [3.7. UNION Statement](#block3.7)
* [3.8. UNION ALL Statement](#block3.8)
* [4. Advanced Quering](#block4)
* [4.1. Filtering using WHERE](#block4.1)
* [4.1.1. Changing WHERE logical operator](#block4.2)
* [4.1.2. Writing complicated WHERE conditions](#block4.2)
* [4.3. Grouping with GROUP BY and HAVING](#block4.3)
* [4.3.1 Available HAVING operators](#block4.3.1)
* [4.4. Changing HAVING logical operator](#block4.4)
* [4.5. Columns as SELECT statements](#block4.5)
* [4.6. Columns being Values](#block4.6)
* [4.7. Columns using FUNCTIONS](#block4.7)
* [5. Commenting queries](#block5)
* [6. Quality Code](#block6)
* [7. Author](#block7)
* [8. License](#block8)
<a name="block1"></a>
## 1. Installation [](#index_block)
The recommended way to install the SQL Query Builder is through [Composer](http://getcomposer.org). Run the following command to install it:
```sh
php composer.phar require nilportugues/sql-query-builder
```
<a name="block2"></a>
## 2. The Builder [](#index_block)
The SQL Query Builder allows to generate complex SQL queries standard using the `SQL-2003` dialect (default) and the `MySQL` dialect, that extends the `SQL-2003` dialect.
<a name="block2.1"></a>
### 2.1. Generic Builder [](#index_block)
The Generic Query Builder is the default builder for this class and writes standard SQL-2003.
**All column aliases are escaped using the `'` sign by default.**
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()->setTable('user');
echo $builder->write($query);
```
#### Output:
```sql
SELECT user.* FROM user
```
<a name="block2.2"></a>
### 2.2. MySQL Builder [](#index_block)
The MySQL Query Builder has its own class, that inherits from the SQL-2003 builder. All columns will be wrapped with the tilde **`** sign.
**All table and column aliases are escaped using the tilde sign by default.**
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\MySqlBuilder;
$builder = new MySqlBuilder();
$query = $builder->select()->setTable('user');
echo $builder->write($query);
```
#### Output:
```sql
SELECT user.* FROM `user`
```
<a name="block2.3"></a>
#### 2.3. Human Readable Output [](#index_block)
Both Generic and MySQL Query Builder can write complex SQL queries.
Every developer out there needs at some point revising the output of a complicated query, the SQL Query Builder includes a human-friendly output method, and therefore the `writeFormatted` method is there to aid the developer when need.
Keep in mind `writeFormatted` is to be avoided at all cost in production mode as it adds unneeded overhead due to parsing and re-formatting of the generated statement.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()->setTable('user');
echo $builder->writeFormatted($query);
```
#### Output:
```sql
SELECT
user.*
FROM
user
```
More complicated examples can be found in the documentation.
<a name="block3"></a>
## 3. Building Queries [](#index_block)
<a name="block3.1"></a>
### 3.1. SELECT Statement [](#index_block)
<a name="block3.1.1"></a>
#### 3.1.1. Basic SELECT statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns(['user_id','name','email']);
echo $builder->write($query);
```
#### Output:
```sql
SELECT user.user_id, user.name, user.email FROM user
```
<a name="block3.1.2"></a>
#### 3.1.2. Aliased SELECT statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns(['userId' => 'user_id', 'username' => 'name', 'email' => 'email']);
echo $builder->write($query);
```
#### Output:
```sql
SELECT user.user_id AS 'userId', user.name AS 'username', user.email AS 'email' FROM user
```
<a name="block3.1.3"></a>
#### 3.1.3. SELECT with WHERE statement [](#index_block)
Default logical operator for filtering using `WHERE` conditions is `AND`.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns([
'userId' => 'user_id',
'username' => 'name',
'email' => 'email'
])
->where()
->greaterThan('user_id', 5)
->notLike('username', 'John')
->end();
echo $builder->writeFormatted($query);
```
#### Output:
```sql
SELECT
user.user_id AS 'userId',
user.name AS 'username',
user.email AS 'email'
FROM
user
WHERE
(user.user_id < :v1)
AND (user.username NOT LIKE :v2)
```
<a name="block3.1.4"></a>
#### 3.1.4. Complex WHERE conditions [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->where()
->equals('user_id', 1)
->equals('user_id', 2)
->subWhere("OR")
->lessThan($column, 10)
->greaterThan('user_id', 100)
->end();
echo $builder->writeFormatted($query);
```
#### Output:
```sql
SELECT
user.*
FROM
user
WHERE
(user.user_id = :v1)
AND (user.user_id = :v2)
AND (
(user.user_id < :v3)
OR (user.user_id > :v4)
)
```
<a name="block3.1.5"></a>
#### 3.1.5. JOIN & LEFT/RIGHT/INNER/CROSS JOIN SELECT statements [](#index_block)
Syntax for `JOIN`, `LEFT JOIN`, `RIGHT JOIN`, `INNER JOIN`, `CROSS JOIN` work the exactly same way.
Here's an example selecting both table and joined table columns and doing sorting using columns from both the table and the joined table.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns([
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at'
])
->orderBy('user_id', OrderBy::DESC)
->leftJoin(
'news', //join table
'user_id', //origin table field used to join
'author_id', //join column
['newsTitle' => 'title', 'body', 'created_at', 'updated_at']
)
->on()
->equals('author_id', 1); //enforcing a condition on the join column
$query
->where()
->greaterThan('user_id', 5)
->notLike('username', 'John')
->end();
$query
->orderBy('created_at', OrderBy::DESC);
echo $builder->writeFormatted($query);
```
#### Output:
```sql
SELECT
user.user_id AS 'userId',
user.name AS 'username',
user.email AS 'email',
user.created_at,
news.title AS 'newsTitle',
news.body,
news.created_at,
news.updated_at
FROM
user
LEFT JOIN
news
ON
(news.author_id = user.user_id)
AND (news.author_id = :v1)
WHERE
(user.user_id < :v2)
AND (user.username NOT LIKE :v3)
ORDER BY
user.user_id DESC,
news.created_at DESC;
```
<a name="block3.1.6"></a>
#### 3.1.6. COUNT rows [](#index_block)
Counting rows comes in 3 possible ways, using the ALL selector `*`, stating a column or stating a column and its alias.
#### 3.1.6.1. Count using ALL selector
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->count()
echo $builder->write($query);
```
#### Output:
```sql
SELECT COUNT(*) FROM user;
```
#### 3.1.6.2. Count using column as a selector
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->count('user_id')
echo $builder->write($query);
```
#### Output:
```sql
SELECT COUNT(user.user_id) FROM user;
```
#### 3.1.6.3. Count using column as a selector
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->count('user_id', 'total_users')
echo $builder->write($query);
```
#### Output:
```sql
SELECT COUNT(user.user_id) AS 'total_users' FROM user;
```
<a name="block3.2"></a>
### 3.2. INSERT Statement [](#index_block)
The `INSERT` statement is really straightforward.
<a name="block3.2.1"></a>
#### 3.2.1 Basic INSERT statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->insert()
->setTable('user')
->setValues([
'user_id' => 1,
'name' => 'Nil',
'contact' => 'contact@nilportugues.com',
]);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output
```sql
INSERT INTO user (user.user_id, user.name, user.contact) VALUES (:v1, :v2, :v3)
```
```php
[':v1' => 1, ':v2' => 'Nil', ':v3' => 'contact@nilportugues.com'];
```
<a name="block3.3"></a>
### 3.3. UPDATE Statement [](#index_block)
The `UPDATE` statement works just like expected, set the values and the conditions to match the row and you're set.
Examples provided below.
<a name="block3.3.1"></a>
#### 3.3.1 Basic UPDATE statement [](#index_block)
Important including the the `where` statement is critical, or all table rows will be replaced with the provided values if the statement is executed.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->update()
->setTable('user')
->setValues([
'user_id' => 1,
'name' => 'Nil',
'contact' => 'contact@nilportugues.com'
])
->where()
->equals('user_id', 1)
->end();
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
UPDATE
user
SET
user.user_id = :v1,
user.name = :v2,
user.contact = :v3
WHERE
(user.user_id = :v4)
```
```php
[':v1' => 1, ':v2' => 'Nil', ':v3' => 'contact@nilportugues.com', ':v4' => 1];
```
<a name="block3.3.2"></a>
#### 3.3.2. Elaborated UPDATE statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->update()
->setTable('user')
->setValues([
'name' => 'UpdatedName',
]);
$query
->where()
->like('username', '%N')
->between('user_id', 1, 2000)
->end();
$query
->orderBy('user_id', OrderBy::ASC)
->limit(1);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
UPDATE
user
SET
user.name = :v1
WHERE
(user.username LIKE :v2)
AND (user.user_id BETWEEN :v3 AND :v4)
ORDER BY
user.user_id ASC
LIMIT :v5
```
<a name="block3.4"></a>
### 3.4. DELETE Statement [](#index_block)
The `DELETE` statement is used just like `UPDATE`, but no values are set.
Examples provided below.
<a name="block3.4.1"></a>
#### 3.4.1. Empty table with DELETE statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->delete()
->setTable('user');
$sql = $builder->write($query);
```
#### Output:
```sql
DELETE FROM user
```
<a name="block3.4.2"></a>
#### 3.4.2. Basic DELETE statement [](#index_block)
Important including the the `where` statement is critical, or all table rows will be deleted with the provided values if the statement is executed.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->delete()
->setTable('user');
$query
->where()
->equals('user_id', 100)
->end();
$query
->limit(1);
$sql = $builder->write($query);
$values = $builder->getValues();
```
#### Output:
```sql
DELETE FROM user WHERE (user.user_id = :v1) LIMIT :v2
```
```php
[':v1' => 100, ':v2' => 1];
```
<a name="block3.4.2"></a>
#### 3.4.2. Elaborated DELETE statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->delete()
->setTable('user');
$query
->where()
->like('username', '%N')
->between('user_id', 1, 2000)
->end();
$query
->orderBy('user_id', OrderBy::ASC)
->limit(1);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
DELETE FROM
user
WHERE
(user.username LIKE :v1)
AND (user.user_id BETWEEN :v2 AND :v3)
ORDER BY
user.user_id ASC
LIMIT :v4
```
<a name="block3.5"></a>
### 3.5. INTERSECT Statement [](#index_block)
***
INTERSECT is not supported by MySQL.
Same results can be achieved by using INNER JOIN statement instead.
***
The `INTERSECT` statement is really straightforward.
<a name="block3.5.1"></a>
#### 3.5.1 Basic INTERSECT statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$select1 = $builder->select()->setTable('user');
$select2 = $builder->select()->setTable('user_emails');
$query = $builder->intersect()
->add($select1)
->add($select2);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output
```sql
SELECT user.* FROM user
INTERSECT
SELECT user_email.* FROM user_email
```
<a name="block3.6"></a>
### 3.6. MINUS Statement [](#index_block)
***
MINUS is not supported by MySQL.
Same results can be achieved by using a LEFT JOIN statement
in combination with an IS NULL or NOT IN condition instead.
***
The `MINUS` statement is really straightforward.
<a name="block3.6.1"></a>
#### 3.6.1 Basic MINUS statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$select1 = $builder->select()->setTable('user');
$select2 = $builder->select()->setTable('user_emails');
$query = $builder->minus($select1, $select2);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output
```sql
SELECT user.* FROM user
MINUS
SELECT user_email.* FROM user_email
```
<a name="block3.7"></a>
### 3.7. UNION Statement [](#index_block)
The `UNION` statement is really straightforward.
<a name="block3.7.1"></a>
#### 3.7.1 Basic UNION statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$select1 = $builder->select()->setTable('user');
$select2 = $builder->select()->setTable('user_emails');
$query = $builder->union()
->add($select1)
->add($select2);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output
```sql
SELECT user.* FROM user
UNION
SELECT user_email.* FROM user_email
```
<a name="block3.8"></a>
### 3.8. UNION ALL Statement [](#index_block)
The `UNION ALL` statement is really straightforward.
<a name="block3.8.1"></a>
#### 3.8.1 Basic UNION ALL statement [](#index_block)
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$select1 = $builder->select()->setTable('user');
$select2 = $builder->select()->setTable('user_emails');
$query = $builder->unionAll()
->add($select1)
->add($select2);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output
```sql
SELECT user.* FROM user
UNION ALL
SELECT user_email.* FROM user_email
```
<a name="block4"></a>
## 4. Advanced Quering [](#index_block)
<a name="block4.1"></a>
### 4.1. Filtering using WHERE [](#index_block)
The following operators are available for filtering using WHERE conditionals:
```php
public function subWhere($operator = 'OR');
public function equals($column, $value);
public function notEquals($column, $value);
public function greaterThan($column, $value);
public function greaterThanOrEqual($column, $value);
public function lessThan($column, $value);
public function lessThanOrEqual($column, $value);
public function like($column, $value);
public function notLike($column, $value);
public function match(array $columns, array $values);
public function matchBoolean(array $columns, array $values);
public function matchWithQueryExpansion(array $columns, array $values);
public function in($column, array $values);
public function notIn($column, array $values);
public function between($column, $a, $b);
public function notBetween($column, $a, $b);
public function isNull($column);
public function isNotNull($column);
public function exists(Select $select);
public function notExists(Select $select);
public function addBitClause($column, $value);
public function asLiteral($literal);
```
<a name="block4.2"></a>
### 4.2. Changing WHERE logical operator [](#index_block)
`WHERE` default's operator must be changed passing to the `where` method the logical operator `OR`.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->where('OR')
->equals('user_id', 1)
->like('name', '%N%')
->end();
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
SELECT user.* FROM user WHERE (user.user_id = :v1) OR (user.name LIKE :v2)
```
<a name="block4.3"></a>
### 4.3. Grouping with GROUP BY and HAVING [](#index_block)
Default logical operator for joining more than one `HAVING` condition is `AND`.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns([
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at'
])
->groupBy(['user_id', 'name'])
->having()
->equals('user_id', 1)
->equals('user_id', 2);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
SELECT
user.user_id AS 'userId',
user.name AS 'username',
user.email AS 'email',
user.created_at
FROM
user
GROUP BY
user.user_id, user.name
HAVING
(user.user_id = :v1)
AND (user.user_id = :v2)
```
<a name="block4.3.1"></a>
#### 4.3.1 Available HAVING operators [](#index_block)
Same operators used in the WHERE statement are available for HAVING operations.
<a name="block4.4"></a>
### 4.4. Changing HAVING logical operator [](#index_block)
`HAVING` default's operator must be changed passing to the `having` method the logical operator `OR`.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns([
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at'
])
->groupBy(['user_id', 'name'])
->having('OR')
->equals('user_id', 1)
->equals('user_id', 2);
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
SELECT
user.user_id AS 'userId',
user.name AS 'username',
user.email AS 'email',
user.created_at
FROM
user
GROUP BY
user.user_id, user.name
HAVING
(user.user_id = :v1)
OR (user.user_id = :v2)
```
<a name="block4.5"></a>
### 4.5. Columns as SELECT statements [](#index_block)
Sometimes, a column needs to be set as a column. SQL Query Builder got you covered on this one too! Check the example below.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$selectRole = $builder->select()
->setTable('role')
->setColumns(array('role_name'))
->limit(1)
->where()
->equals('role_id', 3);
$query = $builder->select()
->setTable('user')
->setColumns(array('user_id', 'username'))
->setSelectAsColumn(array('user_role' => $selectRole))
->setSelectAsColumn(array($selectRole))
->where()
->equals('user_id', 4)
->end();
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
SELECT
user.user_id,
user.username,
(
SELECT
role.role_name
FROM
role
WHERE
(role.role_id = :v1)
LIMIT :v2, :v3
) AS 'user_role',
(
SELECT
role.role_name
FROM
role
WHERE
(role.role_id = :v4)
LIMIT :v5, :v6
) AS 'role'
FROM
user
WHERE
(user.user_id = :v7)
```
<a name="block4.6"></a>
### 4.6. Columns being Values [](#index_block)
There are time where you need to force the same column structure (eg: UNIONs) even when lacking of a column or value. Forcing column with values gets you covered.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns(array('user_id', 'username'))
->setValueAsColumn('10', 'priority')
->where()
->equals('user_id', 1)
->end();
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
SELECT
user.user_id,
user.username,
:v1 AS 'priority'
FROM
user
WHERE
(user.user_id = :v2)
```
<a name="block4.7"></a>
### 4.7. Columns using FUNCTIONS [](#index_block)
Example for MAX function.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns(array('user_id', 'username'))
->setFunctionAsColumn('MAX', array('user_id'), 'max_id')
->where()
->equals('user_id', 1)
->end();
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
SELECT
user.user_id,
user.username,
MAX(user_id) AS 'max_id'
FROM
user
WHERE
(user.user_id = :v1)
```
Example for CURRENT_TIMESTAMP function.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setColumns(array('user_id', 'username'))
->setFunctionAsColumn('CURRENT_TIMESTAMP', array(), 'server_time')
->where()
->equals('user_id', 1)
->end();
$sql = $builder->writeFormatted($query);
$values = $builder->getValues();
```
#### Output:
```sql
SELECT
user.user_id,
user.username,
CURRENT_TIMESTAMP AS 'server_time'
FROM
user
WHERE
(user.user_id = :v1)
```
<a name="block5"></a>
## 5. Commenting queries [](#index_block)
The query builder allows adding comments to all query methods by using the `setComment` method.
Some useful use cases examples can be :
- Explain difficult queries or why of its existence.
- Finding slow queries from its comments.
#### Usage:
```php
<?php
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
$builder = new GenericBuilder();
$query = $builder->select()
->setTable('user')
->setComment('This is a comment');
$sql = $builder->write($query);
```
#### Output:
```sql
-- This is a comment
SELECT user.* FROM user
```
<a name="block6"></a>
## 6. Quality Code [](#index_block)
Testing has been done using PHPUnit and [Travis-CI](https://travis-ci.org). All code has been tested to be compatible from PHP 5.4 up to PHP 5.6 and [HHVM](http://hhvm.com/).
To run the test suite, you need [Composer](http://getcomposer.org):
```bash
php composer.phar install --dev
php bin/phpunit
```
<a name="block7"></a>
## 7. Author [](#index_block)
Nil Portugués Calderó
- <contact@nilportugues.com>
- [http://nilportugues.com](http://nilportugues.com)
<a name="block8"></a>
## 8. License [](#index_block)
SQL Query Builder is licensed under the MIT license.
```
Copyright (c) 2014 Nil Portugués Calderó
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
```

View File

@ -0,0 +1,53 @@
{
"name":"nilportugues/sql-query-builder",
"description":"An elegant lightweight and efficient SQL QueryInterface BuilderInterface supporting bindings and complicated query generation.",
"keywords": [ "sql", "mysql", "query", "builder", "query builder", "orm"],
"type":"library",
"license":"MIT",
"homepage":"http://nilportugues.com",
"authors":
[
{
"name":"Nil Portugués Calderó",
"email":"contact@nilportugues.com",
"homepage":"http://nilportugues.com",
"role":"Lead Developer"
},
{
"name":"Woody Gilk",
"email":"woody.gilk@gmail.com",
"homepage":"http://shadowhand.me/",
"role":"Contributor"
}
],
"autoload":{
"psr-4":{
"NilPortugues\\Sql\\QueryBuilder\\":"src/"
}
},
"autoload-dev":{
"psr-4":{
"NilPortugues\\Tests\\Sql\\QueryBuilder\\":"tests/"
}
},
"require":
{
"php": ">=5.5",
"nilportugues/sql-query-formatter": "~1.2"
},
"require-dev": {
"phpunit/phpunit": "4.*",
"fabpot/php-cs-fixer": "~1.9",
"nilportugues/php_backslasher": "~0.2"
},
"config":
{
"bin-dir": "bin"
},
"minimum-stability": "stable",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
}
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit cacheTokens="false"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
syntaxCheck="true"
bootstrap="vendor/autoload.php"
verbose="true">
<php>
<ini name="intl.default_locale" value="en_US.UTF-8" />
<ini name="intl.error_level" value="0" />
<ini name="memory_limit" value="-1" />
<ini name="max_execution_time" value="-1"/>
<ini name="date.timezone" value="Europe/Madrid" />
<ini name="error_reporting" value="E_ALL" />
</php>
<testsuites>
<testsuite name="Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./vendor/</directory>
<directory>./tests/</directory>
</exclude>
</whitelist>
</filter>
<logging>
<log type="junit" target="build/logs/junit.xml"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
<log type="coverage-html" target="build/coverage"/>
</logging>
</phpunit>

View File

@ -0,0 +1,18 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder;
/**
* Class BuilderException.
*/
final class BuilderException extends \Exception
{
}

View File

@ -0,0 +1,33 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryInterface;
/**
* Interface BuilderInterface.
*/
interface BuilderInterface
{
/**
* @param QueryInterface $query
*
* @return string
*/
public function write(QueryInterface $query);
/**
* @param QueryInterface $query
*
* @return string
*/
public function writeFormatted(QueryInterface $query);
}

View File

@ -0,0 +1,401 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory;
use NilPortugues\Sql\QueryBuilder\Manipulation\AbstractBaseQuery;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryInterface;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryFactory;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\Table;
/**
* Class Generic.
*/
class GenericBuilder implements BuilderInterface
{
/**
* The placeholder parameter bag.
*
* @var \NilPortugues\Sql\QueryBuilder\Builder\Syntax\PlaceholderWriter
*/
protected $placeholderWriter;
/**
* The Where writer.
*
* @var \NilPortugues\Sql\QueryBuilder\Builder\Syntax\WhereWriter
*/
protected $whereWriter;
/**
* The SQL formatter.
*
* @var \NilPortugues\Sql\QueryFormatter\Formatter
*/
protected $sqlFormatter;
/**
* Class namespace for the query pretty output formatter.
* Required to create the instance only if required.
*
* @var string
*/
protected $sqlFormatterClass = 'NilPortugues\Sql\QueryFormatter\Formatter';
/**
* Array holding the writers for each query part. Methods are called upon request and stored in
* the $queryWriterInstances array.
*
* @var array
*/
protected $queryWriterArray = [
'SELECT' => '\NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory::createSelectWriter',
'INSERT' => '\NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory::createInsertWriter',
'UPDATE' => '\NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory::createUpdateWriter',
'DELETE' => '\NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory::createDeleteWriter',
'INTERSECT' => '\NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory::createIntersectWriter',
'MINUS' => '\NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory::createMinusWriter',
'UNION' => '\NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory::createUnionWriter',
'UNION ALL' => '\NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory::createUnionAllWriter',
];
/**
* Array that stores instances of query writers.
*
* @var array
*/
protected $queryWriterInstances = [
'SELECT' => null,
'INSERT' => null,
'UPDATE' => null,
'DELETE' => null,
'INTERSECT' => null,
'MINUS' => null,
'UNION' => null,
'UNION ALL' => null,
];
/**
* Creates writers.
*/
public function __construct()
{
$this->placeholderWriter = WriterFactory::createPlaceholderWriter();
}
/**
* @param string $table
* @param array $columns
*
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\Select
*/
public function select($table = null, array $columns = null)
{
return $this->injectBuilder(QueryFactory::createSelect($table, $columns));
}
/**
* @param \NilPortugues\Sql\QueryBuilder\Manipulation\AbstractBaseQuery
*
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\AbstractBaseQuery
*/
protected function injectBuilder(AbstractBaseQuery $query)
{
return $query->setBuilder($this);
}
/**
* @param string $table
* @param array $values
*
*@return AbstractBaseQuery
*/
public function insert($table = null, array $values = null)
{
return $this->injectBuilder(QueryFactory::createInsert($table, $values));
}
/**
* @param string $table
* @param array $values
*
*@return AbstractBaseQuery
*/
public function update($table = null, array $values = null)
{
return $this->injectBuilder(QueryFactory::createUpdate($table, $values));
}
/**
* @param string $table
*
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\Delete
*/
public function delete($table = null)
{
return $this->injectBuilder(QueryFactory::createDelete($table));
}
/**
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\Intersect
*/
public function intersect()
{
return QueryFactory::createIntersect();
}
/**
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\Union
*/
public function union()
{
return QueryFactory::createUnion();
}
/**
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\UnionAll
*/
public function unionAll()
{
return QueryFactory::createUnionAll();
}
/**
* @param \NilPortugues\Sql\QueryBuilder\Manipulation\Select $first
* @param \NilPortugues\Sql\QueryBuilder\Manipulation\Select $second
*
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\Minus
*/
public function minus(Select $first, Select $second)
{
return QueryFactory::createMinus($first, $second);
}
/**
* @return array
*/
public function getValues()
{
return $this->placeholderWriter->get();
}
/**
* Returns a SQL string in a readable human-friendly format.
*
* @param QueryInterface $query
*
* @return string
*/
public function writeFormatted(QueryInterface $query)
{
if (null === $this->sqlFormatter) {
$this->sqlFormatter = (new \ReflectionClass($this->sqlFormatterClass))->newInstance();
}
return $this->sqlFormatter->format($this->write($query));
}
/**
* @param QueryInterface $query
* @param bool $resetPlaceholders
*
* @return string
*
* @throws \RuntimeException
*/
public function write(QueryInterface $query, $resetPlaceholders = true)
{
if ($resetPlaceholders) {
$this->placeholderWriter->reset();
}
$queryPart = $query->partName();
if (false === empty($this->queryWriterArray[$queryPart])) {
$this->createQueryObject($queryPart);
return $this->queryWriterInstances[$queryPart]->write($query);
}
throw new \RuntimeException('Query builder part not defined.');
}
/**
* @param Select $select
*
* @return string
*/
public function writeJoin(Select $select)
{
if (null === $this->whereWriter) {
$this->whereWriter = WriterFactory::createWhereWriter($this, $this->placeholderWriter);
}
$sql = ($select->getJoinType()) ? "{$select->getJoinType()} " : '';
$sql .= 'JOIN ';
$sql .= $this->writeTableWithAlias($select->getTable());
$sql .= ' ON ';
$sql .= $this->whereWriter->writeWhere($select->getJoinCondition());
return $sql;
}
/**
* @param Table $table
*
* @return string
*/
public function writeTableWithAlias(Table $table)
{
$alias = ($table->getAlias()) ? " AS {$this->writeTableAlias($table->getAlias())}" : '';
$schema = ($table->getSchema()) ? "{$table->getSchema()}." : '';
return $schema.$this->writeTableName($table).$alias;
}
/**
* @param $alias
*
* @return mixed
*/
public function writeTableAlias($alias)
{
return $alias;
}
/**
* Returns the table name.
*
* @param Table $table
*
* @return string
*/
public function writeTableName(Table $table)
{
return $table->getName();
}
/**
* @param string $alias
*
* @return string
*/
public function writeColumnAlias($alias)
{
return sprintf('"%s"', $alias);
}
/**
* @param Table $table
*
* @return string
*/
public function writeTable(Table $table)
{
$schema = ($table->getSchema()) ? "{$table->getSchema()}." : '';
return $schema.$this->writeTableName($table);
}
/**
* @param array $values
*
* @return array
*/
public function writeValues(array &$values)
{
\array_walk(
$values,
function (&$value) {
$value = $this->writePlaceholderValue($value);
}
);
return $values;
}
/**
* @param $value
*
* @return string
*/
public function writePlaceholderValue($value)
{
return $this->placeholderWriter->add($value);
}
/**
* @param $operator
*
* @return string
*/
public function writeConjunction($operator)
{
return ' '.$operator.' ';
}
/**
* @return string
*/
public function writeIsNull()
{
return ' IS NULL';
}
/**
* @return string
*/
public function writeIsNotNull()
{
return ' IS NOT NULL';
}
/**
* Returns the column name.
*
* @param Column $column
*
* @return string
*/
public function writeColumnName(Column $column)
{
$name = $column->getName();
if ($name === Column::ALL) {
return $this->writeColumnAll();
}
return $name;
}
/**
* @return string
*/
protected function writeColumnAll()
{
return '*';
}
/**
* @param string $queryPart
*/
protected function createQueryObject($queryPart)
{
if (null === $this->queryWriterInstances[$queryPart]) {
$this->queryWriterInstances[$queryPart] = \call_user_func_array(
\explode('::', $this->queryWriterArray[$queryPart]),
[$this, $this->placeholderWriter]
);
}
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\Table;
/**
* Class MySqlBuilder.
*/
class MySqlBuilder extends GenericBuilder
{
/**
* {@inheritdoc}
*
* @param Column $column
*
* @return string
*/
public function writeColumnName(Column $column)
{
if ($column->isAll()) {
return '*';
}
if (false !== strpos($column->getName(), '(')) {
return parent::writeColumnName($column);
}
return $this->wrapper(parent::writeColumnName($column));
}
/**
* {@inheritdoc}
*
* @param Table $table
*
* @return string
*/
public function writeTableName(Table $table)
{
return $this->wrapper(parent::writeTableName($table));
}
/**
* {@inheritdoc}
*
* @param $alias
*
* @return string
*/
public function writeTableAlias($alias)
{
return $this->wrapper(parent::writeTableAlias($alias));
}
/**
* {@inheritdoc}
*
* @param $alias
*
* @return string
*/
public function writeColumnAlias($alias)
{
return $this->wrapper($alias);
}
/**
* @param $string
* @param string $char
*
* @return string
*/
protected function wrapper($string, $char = '`')
{
if (0 === strlen($string)) {
return '';
}
return $char.$string.$char;
}
}

View File

@ -0,0 +1,96 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/24/14
* Time: 1:14 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\AbstractBaseQuery;
/**
* Class AbstractBaseWriter.
*/
abstract class AbstractBaseWriter
{
/**
* @var GenericBuilder
*/
protected $writer;
/**
* @var PlaceholderWriter
*/
protected $placeholderWriter;
/**
* @var ColumnWriter
*/
protected $columnWriter;
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholder
*/
public function __construct(GenericBuilder $writer, PlaceholderWriter $placeholder)
{
$this->writer = $writer;
$this->placeholderWriter = $placeholder;
$this->columnWriter = WriterFactory::createColumnWriter($writer, $placeholder);
}
/**
* @param AbstractBaseQuery $class
*
* @return string
*/
public static function writeQueryComment(AbstractBaseQuery $class)
{
$comment = '';
if ('' !== $class->getComment()) {
$comment = $class->getComment();
}
return $comment;
}
/**
* @param AbstractBaseQuery $class
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholderWriter
* @param array $parts
*/
public static function writeWhereCondition(
AbstractBaseQuery $class,
$writer, PlaceholderWriter
$placeholderWriter,
array &$parts
) {
if (!is_null($class->getWhere())) {
$whereWriter = WriterFactory::createWhereWriter($writer, $placeholderWriter);
$parts[] = "WHERE {$whereWriter->writeWhere($class->getWhere())}";
}
}
/**
* @param AbstractBaseQuery $class
* @param PlaceholderWriter $placeholderWriter
* @param array $parts
*/
public static function writeLimitCondition(
AbstractBaseQuery $class,
PlaceholderWriter $placeholderWriter,
array &$parts
) {
if (!is_null($class->getLimitStart())) {
$start = $placeholderWriter->add($class->getLimitStart());
$parts[] = "LIMIT {$start}";
}
}
}

View File

@ -0,0 +1,51 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/24/14
* Time: 12:55 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Syntax\QueryPartInterface;
/**
* Class AbstractSetWriter.
*/
abstract class AbstractSetWriter
{
/**
* @var GenericBuilder
*/
protected $writer;
/**
* @param GenericBuilder $writer
*/
public function __construct(GenericBuilder $writer)
{
$this->writer = $writer;
}
/**
* @param QueryPartInterface $setClass
* @param string $setOperation
* @param $glue
*
* @return string
*/
protected function abstractWrite(QueryPartInterface $setClass, $setOperation, $glue)
{
$selects = [];
foreach ($setClass->$setOperation() as $select) {
$selects[] = $this->writer->write($select, false);
}
return \implode("\n".$glue."\n", $selects);
}
}

View File

@ -0,0 +1,162 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/12/14
* Time: 1:28 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
/**
* Class ColumnWriter.
*/
class ColumnWriter
{
/**
* @var \NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder
*/
protected $writer;
/**
* @var PlaceholderWriter
*/
protected $placeholderWriter;
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholderWriter
*/
public function __construct(GenericBuilder $writer, PlaceholderWriter $placeholderWriter)
{
$this->writer = $writer;
$this->placeholderWriter = $placeholderWriter;
}
/**
* @param Select $select
*
* @return array
*/
public function writeSelectsAsColumns(Select $select)
{
$selectAsColumns = $select->getColumnSelects();
if (!empty($selectAsColumns)) {
$selectWriter = WriterFactory::createSelectWriter($this->writer, $this->placeholderWriter);
$selectAsColumns = $this->selectColumnToQuery($selectAsColumns, $selectWriter);
}
return $selectAsColumns;
}
/**
* @param array $selectAsColumns
* @param SelectWriter $selectWriter
*
* @return mixed
*/
protected function selectColumnToQuery(array &$selectAsColumns, SelectWriter $selectWriter)
{
\array_walk(
$selectAsColumns,
function (&$column) use (&$selectWriter) {
$keys = \array_keys($column);
$key = \array_pop($keys);
$values = \array_values($column);
$value = $values[0];
if (\is_numeric($key)) {
/* @var Column $value */
$key = $this->writer->writeTableName($value->getTable());
}
$column = $selectWriter->selectToColumn($key, $value);
}
);
return $selectAsColumns;
}
/**
* @param Select $select
*
* @return array
*/
public function writeValueAsColumns(Select $select)
{
$valueAsColumns = $select->getColumnValues();
$newColumns = [];
if (!empty($valueAsColumns)) {
foreach ($valueAsColumns as $alias => $value) {
$value = $this->writer->writePlaceholderValue($value);
$newValueColumn = array($alias => $value);
$newColumns[] = SyntaxFactory::createColumn($newValueColumn, null);
}
}
return $newColumns;
}
/**
* @param Select $select
*
* @return array
*/
public function writeFuncAsColumns(Select $select)
{
$funcAsColumns = $select->getColumnFuncs();
$newColumns = [];
if (!empty($funcAsColumns)) {
foreach ($funcAsColumns as $alias => $value) {
$funcName = $value['func'];
$funcArgs = (!empty($value['args'])) ? '('.implode(', ', $value['args']).')' : '';
$newFuncColumn = array($alias => $funcName.$funcArgs);
$newColumns[] = SyntaxFactory::createColumn($newFuncColumn, null);
}
}
return $newColumns;
}
/**
* @param Column $column
*
* @return string
*/
public function writeColumnWithAlias(Column $column)
{
if (($alias = $column->getAlias()) && !$column->isAll()) {
return $this->writeColumn($column).' AS '.$this->writer->writeColumnAlias($alias);
}
return $this->writeColumn($column);
}
/**
* @param Column $column
*
* @return string
*/
public function writeColumn(Column $column)
{
$alias = $column->getTable()->getAlias();
$table = ($alias) ? $this->writer->writeTableAlias($alias) : $this->writer->writeTable($column->getTable());
$columnString = (empty($table)) ? '' : "{$table}.";
$columnString .= $this->writer->writeColumnName($column);
return $columnString;
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/11/14
* Time: 1:50 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Delete;
/**
* Class DeleteWriter.
*/
class DeleteWriter
{
/**
* @var GenericBuilder
*/
protected $writer;
/**
* @var PlaceholderWriter
*/
protected $placeholderWriter;
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholder
*/
public function __construct(GenericBuilder $writer, PlaceholderWriter $placeholder)
{
$this->writer = $writer;
$this->placeholderWriter = $placeholder;
}
/**
* @param Delete $delete
*
* @return string
*/
public function write(Delete $delete)
{
$table = $this->writer->writeTable($delete->getTable());
$parts = array("DELETE FROM {$table}");
AbstractBaseWriter::writeWhereCondition($delete, $this->writer, $this->placeholderWriter, $parts);
AbstractBaseWriter::writeLimitCondition($delete, $this->placeholderWriter, $parts);
$comment = AbstractBaseWriter::writeQueryComment($delete);
return $comment.implode(' ', $parts);
}
}

View File

@ -0,0 +1,103 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/11/14
* Time: 1:51 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Insert;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryException;
/**
* Class InsertWriter.
*/
class InsertWriter
{
/**
* @var GenericBuilder
*/
protected $writer;
/**
* @var ColumnWriter
*/
protected $columnWriter;
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholder
*/
public function __construct(GenericBuilder $writer, PlaceholderWriter $placeholder)
{
$this->writer = $writer;
$this->columnWriter = WriterFactory::createColumnWriter($this->writer, $placeholder);
}
/**
* @param Insert $insert
*
* @throws QueryException
*
* @return string
*/
public function write(Insert $insert)
{
$columns = $insert->getColumns();
if (empty($columns)) {
throw new QueryException('No columns were defined for the current schema.');
}
$columns = $this->writeQueryColumns($columns);
$values = $this->writeQueryValues($insert->getValues());
$table = $this->writer->writeTable($insert->getTable());
$comment = AbstractBaseWriter::writeQueryComment($insert);
return $comment."INSERT INTO {$table} ($columns) VALUES ($values)";
}
/**
* @param $columns
*
* @return string
*/
protected function writeQueryColumns($columns)
{
return $this->writeCommaSeparatedValues($columns, $this->columnWriter, 'writeColumn');
}
/**
* @param $collection
* @param $writer
* @param string $method
*
* @return string
*/
protected function writeCommaSeparatedValues($collection, $writer, $method)
{
\array_walk(
$collection,
function (&$data) use ($writer, $method) {
$data = $writer->$method($data);
}
);
return \implode(', ', $collection);
}
/**
* @param $values
*
* @return string
*/
protected function writeQueryValues($values)
{
return $this->writeCommaSeparatedValues($values, $this->writer, 'writePlaceholderValue');
}
}

View File

@ -0,0 +1,29 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:15 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Manipulation\Intersect;
/**
* Class IntersectWriter.
*/
class IntersectWriter extends AbstractSetWriter
{
/**
* @param Intersect $intersect
*
* @return string
*/
public function write(Intersect $intersect)
{
return $this->abstractWrite($intersect, 'getIntersects', Intersect::INTERSECT);
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:15 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Minus;
/**
* Class MinusWriter.
*/
class MinusWriter
{
/**
* @var GenericBuilder
*/
protected $writer;
/**
* @param GenericBuilder $writer
*/
public function __construct(GenericBuilder $writer)
{
$this->writer = $writer;
}
/**
* @param Minus $minus
*
* @return string
*/
public function write(Minus $minus)
{
$first = $this->writer->write($minus->getFirst());
$second = $this->writer->write($minus->getSecond());
return $first."\n".Minus::MINUS."\n".$second;
}
}

View File

@ -0,0 +1,147 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/4/14
* Time: 12:02 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
/**
* Class PlaceholderWriter.
*/
class PlaceholderWriter
{
/**
* @var int
*/
protected $counter = 1;
/**
* @var array
*/
protected $placeholders = [];
/**
* @return array
*/
public function get()
{
return $this->placeholders;
}
/**
* @return $this
*/
public function reset()
{
$this->counter = 1;
$this->placeholders = [];
return $this;
}
/**
* @param $value
*
* @return string
*/
public function add($value)
{
$placeholderKey = ':v'.$this->counter;
$this->placeholders[$placeholderKey] = $this->setValidSqlValue($value);
++$this->counter;
return $placeholderKey;
}
/**
* @param $value
*
* @return string
*/
protected function setValidSqlValue($value)
{
$value = $this->writeNullSqlString($value);
$value = $this->writeStringAsSqlString($value);
$value = $this->writeBooleanSqlString($value);
return $value;
}
/**
* @param $value
*
* @return string
*/
protected function writeNullSqlString($value)
{
if (\is_null($value) || (\is_string($value) && empty($value))) {
$value = $this->writeNull();
}
return $value;
}
/**
* @return string
*/
protected function writeNull()
{
return 'NULL';
}
/**
* @param string $value
*
* @return string
*/
protected function writeStringAsSqlString($value)
{
if (\is_string($value)) {
$value = $this->writeString($value);
}
return $value;
}
/**
* @param string $value
*
* @return string
*/
protected function writeString($value)
{
return $value;
}
/**
* @param string $value
*
* @return string
*/
protected function writeBooleanSqlString($value)
{
if (\is_bool($value)) {
$value = $this->writeBoolean($value);
}
return $value;
}
/**
* @param bool $value
*
* @return string
*/
protected function writeBoolean($value)
{
$value = \filter_var($value, FILTER_VALIDATE_BOOLEAN);
return ($value) ? '1' : '0';
}
}

View File

@ -0,0 +1,397 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/11/14
* Time: 1:50 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
/**
* Class SelectWriter.
*/
class SelectWriter extends AbstractBaseWriter
{
/**
* @param $alias
* @param Select $select
*
* @return Column
*/
public function selectToColumn($alias, Select $select)
{
$selectAsColumn = $this->write($select);
if (!empty($selectAsColumn)) {
$selectAsColumn = '('.$selectAsColumn.')';
}
$column = array($alias => $selectAsColumn);
return SyntaxFactory::createColumn($column, null);
}
/**
* @param Select $select
*
* @return string
*/
public function write(Select $select)
{
if ($select->isJoinSelect()) {
return $this->writer->writeJoin($select);
}
return $this->writeSelectQuery($select);
}
/**
* @param Select $select
*
* @return string
*/
protected function writeSelectQuery(Select $select)
{
$parts = ['SELECT'];
if ($select->isDistinct()) {
$parts[] = 'DISTINCT';
}
$this->writeSelectColumns($select, $parts);
$this->writeSelectFrom($select, $parts);
$this->writeSelectJoins($select, $parts);
$this->writeSelectWhere($select, $parts);
$this->writeSelectGroupBy($select, $parts);
$this->writeSelectHaving($select, $parts);
$this->writeSelectOrderBy($select, $parts);
$this->writeSelectLimit($select, $parts);
return AbstractBaseWriter::writeQueryComment($select).implode(' ', \array_filter($parts));
}
/**
* @param Select $select
* @param string[] $parts
*
* @return $this
*/
public function writeSelectColumns(Select $select, array &$parts)
{
if ($select->isCount() === false) {
$columns = $this->writeColumnAlias(
$select->getAllColumns(),
$this->columnWriter->writeSelectsAsColumns($select),
$this->columnWriter->writeValueAsColumns($select),
$this->columnWriter->writeFuncAsColumns($select)
);
$parts = \array_merge($parts, [implode(', ', $columns)]);
return $this;
}
$columns = $select->getColumns();
$column = \array_pop($columns);
$columnList = $column->getName();
$parts = \array_merge($parts, [$columnList]);
return $this;
}
/**
* @param $tableColumns
* @param $selectAsColumns
* @param $valueAsColumns
* @param $funcAsColumns
*
* @return array
*/
protected function writeColumnAlias($tableColumns, $selectAsColumns, $valueAsColumns, $funcAsColumns)
{
$columns = \array_merge($tableColumns, $selectAsColumns, $valueAsColumns, $funcAsColumns);
\array_walk(
$columns,
function (&$column) {
$column = $this->columnWriter->writeColumnWithAlias($column);
}
);
return $columns;
}
/**
* @param Select $select
* @param string[] $parts
*
* @return $this
*/
public function writeSelectFrom(Select $select, array &$parts)
{
$parts = \array_merge(
$parts,
['FROM '.$this->writer->writeTableWithAlias($select->getTable())]
);
return $this;
}
/**
* @param Select $select
* @param array $parts
*
* @return $this
*/
public function writeSelectJoins(Select $select, array &$parts)
{
$parts = \array_merge(
$parts,
[$this->writeSelectAggrupation($select, $this->writer, 'getAllJoins', 'writeJoin', ' ')]
);
return $this;
}
/**
* @param Select $select
* @param $writer
* @param string $getMethod
* @param string $writeMethod
* @param string $glue
* @param string $prepend
*
* @return string
*/
protected function writeSelectAggrupation(Select $select, $writer, $getMethod, $writeMethod, $glue, $prepend = '')
{
$str = '';
$joins = $select->$getMethod();
if (!empty($joins)) {
\array_walk(
$joins,
function (&$join) use ($writer, $writeMethod) {
$join = $writer->$writeMethod($join);
}
);
$str = $prepend.implode($glue, $joins);
}
return $str;
}
/**
* @param Select $select
* @param array $parts
*
* @return $this
*/
public function writeSelectWhere(Select $select, array &$parts)
{
$str = '';
$wheres = $this->writeSelectWheres($select->getAllWheres());
$wheres = \array_filter($wheres);
if (\count($wheres) > 0) {
$str = 'WHERE ';
$separator = ' '.$this->writer->writeConjunction($select->getWhereOperator()).' ';
$str .= \implode($separator, $wheres);
}
$parts = \array_merge($parts, [$str]);
return $this;
}
/**
* @param array $wheres
*
* @return array
*/
protected function writeSelectWheres(array $wheres)
{
$whereWriter = WriterFactory::createWhereWriter($this->writer, $this->placeholderWriter);
\array_walk(
$wheres,
function (&$where) use (&$whereWriter) {
$where = $whereWriter->writeWhere($where);
}
);
return $wheres;
}
/**
* @param Select $select
* @param array $parts
*
* @return $this
*/
public function writeSelectGroupBy(Select $select, array &$parts)
{
$groupBy = $this->writeSelectAggrupation(
$select,
$this->columnWriter,
'getGroupBy',
'writeColumn',
', ',
'GROUP BY '
);
$parts = \array_merge($parts, [$groupBy]);
return $this;
}
/**
* @param Select $select
* @param array $parts
*
* @return $this
*/
public function writeSelectHaving(Select $select, array &$parts)
{
$str = '';
$havingArray = $select->getAllHavings();
if (\count($havingArray) > 0) {
$placeholder = $this->placeholderWriter;
$writer = $this->writer;
$str = 'HAVING ';
$separator = ' '.$select->getHavingOperator().' ';
$havingArray = $this->getHavingConditions($havingArray, $select, $writer, $placeholder);
$str .= \implode($separator, $havingArray);
}
$parts = \array_merge($parts, [$str]);
return $this;
}
/**
* @param array $havingArray
* @param Select $select
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholder
*
* @return mixed
*/
protected function getHavingConditions(
array &$havingArray,
Select $select,
GenericBuilder $writer,
PlaceholderWriter $placeholder
) {
\array_walk(
$havingArray,
function (&$having) use ($select, $writer, $placeholder) {
$whereWriter = WriterFactory::createWhereWriter($writer, $placeholder);
$clauses = $whereWriter->writeWhereClauses($having);
$having = \implode($this->writer->writeConjunction($select->getHavingOperator()), $clauses);
}
);
return $havingArray;
}
/**
* @param Select $select
* @param array $parts
*
* @return $this
*/
protected function writeSelectOrderBy(Select $select, array &$parts)
{
$str = '';
if (\count($select->getAllOrderBy())) {
$orderByArray = $select->getAllOrderBy();
\array_walk(
$orderByArray,
function (&$orderBy) {
$orderBy = $this->writeOrderBy($orderBy);
}
);
$str = 'ORDER BY ';
$str .= \implode(', ', $orderByArray);
}
$parts = \array_merge($parts, [$str]);
return $this;
}
/**
* @param OrderBy $orderBy
*
* @return string
*/
public function writeOrderBy(OrderBy $orderBy)
{
$column = $this->columnWriter->writeColumn($orderBy->getColumn());
return $column.' '.$orderBy->getDirection();
}
/**
* @param Select $select
* @param array $parts
*
* @return $this
*/
protected function writeSelectLimit(Select $select, array &$parts)
{
$mask = $this->getStartingLimit($select).$this->getLimitCount($select);
$limit = '';
if ($mask !== '00') {
$start = $this->placeholderWriter->add($select->getLimitStart());
$count = $this->placeholderWriter->add($select->getLimitCount());
$limit = "LIMIT {$start}, {$count}";
}
$parts = \array_merge($parts, [$limit]);
return $this;
}
/**
* @param Select $select
*
* @return string
*/
protected function getStartingLimit(Select $select)
{
return (null === $select->getLimitStart() || 0 == $select->getLimitStart()) ? '0' : '1';
}
/**
* @param Select $select
*
* @return string
*/
protected function getLimitCount(Select $select)
{
return (null === $select->getLimitCount()) ? '0' : '1';
}
}

View File

@ -0,0 +1,29 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:15 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Manipulation\UnionAll;
/**
* Class UnionAllWriter.
*/
class UnionAllWriter extends AbstractSetWriter
{
/**
* @param UnionAll $unionAll
*
* @return string
*/
public function write(UnionAll $unionAll)
{
return $this->abstractWrite($unionAll, 'getUnions', UnionAll::UNION_ALL);
}
}

View File

@ -0,0 +1,29 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:15 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Manipulation\Union;
/**
* Class UnionWriter.
*/
class UnionWriter extends AbstractSetWriter
{
/**
* @param Union $union
*
* @return string
*/
public function write(Union $union)
{
return $this->abstractWrite($union, 'getUnions', Union::UNION);
}
}

View File

@ -0,0 +1,67 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/11/14
* Time: 1:51 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryException;
use NilPortugues\Sql\QueryBuilder\Manipulation\Update;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
/**
* Class UpdateWriter.
*/
class UpdateWriter extends AbstractBaseWriter
{
/**
* @param Update $update
*
* @throws QueryException
*
* @return string
*/
public function write(Update $update)
{
$values = $update->getValues();
if (empty($values)) {
throw new QueryException('No values to update in Update query.');
}
$parts = array(
'UPDATE '.$this->writer->writeTable($update->getTable()).' SET ',
$this->writeUpdateValues($update),
);
AbstractBaseWriter::writeWhereCondition($update, $this->writer, $this->placeholderWriter, $parts);
AbstractBaseWriter::writeLimitCondition($update, $this->placeholderWriter, $parts);
$comment = AbstractBaseWriter::writeQueryComment($update);
return $comment.implode(' ', $parts);
}
/**
* @param Update $update
*
* @return string
*/
protected function writeUpdateValues(Update $update)
{
$assigns = [];
foreach ($update->getValues() as $column => $value) {
$newColumn = array($column);
$column = $this->columnWriter->writeColumn(SyntaxFactory::createColumn($newColumn, $update->getTable()));
$value = $this->writer->writePlaceholderValue($value);
$assigns[] = "$column = $value";
}
return \implode(', ', $assigns);
}
}

View File

@ -0,0 +1,400 @@
<?php
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
use NilPortugues\Sql\QueryBuilder\Syntax\Where;
/**
* Class WhereWriter.
*/
class WhereWriter extends AbstractBaseWriter
{
/**
* @var array
*/
protected $matchMode = [
'natural' => '(MATCH({{columnNames}}) AGAINST({{columnValues}}))',
'boolean' => '(MATCH({{columnNames}}) AGAINST({{columnValues}} IN BOOLEAN MODE))',
'query_expansion' => '(MATCH({{columnNames}}) AGAINST({{columnValues}} WITH QUERY EXPANSION))',
];
/**
* @param Where $where
*
* @return string
*/
public function writeWhere(Where $where)
{
$clauses = $this->writeWhereClauses($where);
$clauses = \array_filter($clauses);
if (empty($clauses)) {
return '';
}
return \implode($this->writer->writeConjunction($where->getConjunction()), $clauses);
}
/**
* @param Where $where
*
* @return array
*/
public function writeWhereClauses(Where $where)
{
$whereArray = [];
$this->writeWhereMatches($where, $whereArray);
$this->writeWhereIns($where, $whereArray);
$this->writeWhereNotIns($where, $whereArray);
$this->writeWhereBetweens($where, $whereArray);
$this->writeWhereNotBetweens($where, $whereArray);
$this->writeWhereComparisons($where, $whereArray);
$this->writeWhereIsNulls($where, $whereArray);
$this->writeWhereIsNotNulls($where, $whereArray);
$this->writeWhereBooleans($where, $whereArray);
$this->writeExists($where, $whereArray);
$this->writeNotExists($where, $whereArray);
$this->writeSubWheres($where, $whereArray);
return $whereArray;
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereMatches(Where $where, array &$whereArray)
{
$matches = [];
foreach ($where->getMatches() as $values) {
$columns = SyntaxFactory::createColumns($values['columns'], $where->getTable());
$columnNames = $this->getColumnNames($columns);
$columnValues = array(\implode(' ', $values['values']));
$columnValues = \implode(', ', $this->writer->writeValues($columnValues));
$matches[] = \str_replace(
['{{columnNames}}', '{{columnValues}}'],
[$columnNames, $columnValues],
$this->matchMode[$values['mode']]
);
}
$whereArray = \array_merge($whereArray, $matches);
}
/**
* @param $columns
*
* @return string
*/
protected function getColumnNames($columns)
{
$columnNames = [];
foreach ($columns as &$column) {
$columnNames[] = $this->columnWriter->writeColumn($column);
}
return \implode(', ', $columnNames);
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereIns(Where $where, array &$whereArray)
{
$whereArray = \array_merge(
$whereArray,
$this->writeWhereIn($where, 'getIns', 'IN')
);
}
/**
* @param Where $where
* @param string $method
* @param string $operation
*
* @return array
*/
protected function writeWhereIn(Where $where, $method, $operation)
{
$collection = [];
foreach ($where->$method() as $column => $values) {
$newColumn = array($column);
$column = SyntaxFactory::createColumn($newColumn, $where->getTable());
$column = $this->columnWriter->writeColumn($column);
$values = $this->writer->writeValues($values);
$values = \implode(', ', $values);
$collection[] = "({$column} $operation ({$values}))";
}
return $collection;
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereNotIns(Where $where, array &$whereArray)
{
$whereArray = \array_merge(
$whereArray,
$this->writeWhereIn($where, 'getNotIns', 'NOT IN')
);
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereBetweens(Where $where, array &$whereArray)
{
$between = $where->getBetweens();
\array_walk(
$between,
function (&$between) {
$between = '('
.$this->columnWriter->writeColumn($between['subject'])
.' BETWEEN '
.$this->writer->writePlaceholderValue($between['a'])
.' AND '
.$this->writer->writePlaceholderValue($between['b'])
.')';
}
);
$whereArray = \array_merge($whereArray, $between);
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereNotBetweens(Where $where, array &$whereArray)
{
$between = $where->getNotBetweens();
\array_walk(
$between,
function (&$between) {
$between = '('
.$this->columnWriter->writeColumn($between['subject'])
.' NOT BETWEEN '
.$this->writer->writePlaceholderValue($between['a'])
.' AND '
.$this->writer->writePlaceholderValue($between['b'])
.')';
}
);
$whereArray = \array_merge($whereArray, $between);
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereComparisons(Where $where, array &$whereArray)
{
$comparisons = $where->getComparisons();
\array_walk(
$comparisons,
function (&$comparison) {
if (!is_array($comparison)) {
return;
}
$str = $this->writeWherePartialCondition($comparison['subject']);
$str .= $this->writer->writeConjunction($comparison['conjunction']);
$str .= $this->writeWherePartialCondition($comparison['target']);
$comparison = "($str)";
}
);
$whereArray = \array_merge($whereArray, $comparisons);
}
/**
* @param $subject
*
* @return string
*/
protected function writeWherePartialCondition(&$subject)
{
if ($subject instanceof Column) {
$str = $this->columnWriter->writeColumn($subject);
} elseif ($subject instanceof Select) {
$selectWriter = WriterFactory::createSelectWriter($this->writer, $this->placeholderWriter);
$str = '('.$selectWriter->write($subject).')';
} else {
$str = $this->writer->writePlaceholderValue($subject);
}
return $str;
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereIsNulls(Where $where, array &$whereArray)
{
$whereArray = \array_merge(
$whereArray,
$this->writeWhereIsNullable($where, 'getNull', 'writeIsNull')
);
}
/**
* @param Where $where
* @param string $getMethod
* @param string $writeMethod
*
* @return array
*/
protected function writeWhereIsNullable(Where $where, $getMethod, $writeMethod)
{
$collection = $where->$getMethod();
\array_walk(
$collection,
function (&$collection) use ($writeMethod) {
$collection =
'('.$this->columnWriter->writeColumn($collection['subject'])
.$this->writer->$writeMethod().')';
}
);
return $collection;
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereIsNotNulls(Where $where, array &$whereArray)
{
$whereArray = \array_merge(
$whereArray,
$this->writeWhereIsNullable($where, 'getNotNull', 'writeIsNotNull')
);
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeWhereBooleans(Where $where, array &$whereArray)
{
$booleans = $where->getBooleans();
$placeholderWriter = $this->placeholderWriter;
\array_walk(
$booleans,
function (&$boolean) use (&$placeholderWriter) {
$column = $this->columnWriter->writeColumn($boolean['subject']);
$value = $this->placeholderWriter->add($boolean['value']);
$boolean = '(ISNULL('.$column.', 0) = '.$value.')';
}
);
$whereArray = \array_merge($whereArray, $booleans);
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeExists(Where $where, array &$whereArray)
{
$whereArray = \array_merge(
$whereArray,
$this->writeExistence($where, 'getExists', 'EXISTS')
);
}
/**
* @param Where $where
* @param string $method
* @param string $operation
*
* @return array
*/
protected function writeExistence(Where $where, $method, $operation)
{
$exists = [];
foreach ($where->$method() as $select) {
$exists[] = "$operation (".$this->writer->write($select, false).')';
}
return $exists;
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeNotExists(Where $where, array &$whereArray)
{
$whereArray = \array_merge(
$whereArray,
$this->writeExistence($where, 'getNotExists', 'NOT EXISTS')
);
}
/**
* @param Where $where
* @param array $whereArray
*
* @return array
*/
protected function writeSubWheres(Where $where, array &$whereArray)
{
$subWheres = $where->getSubWheres();
\array_walk(
$subWheres,
function (&$subWhere) {
$subWhere = "({$this->writeWhere($subWhere)})";
}
);
$whereArray = \array_merge($whereArray, $subWheres);
}
}

View File

@ -0,0 +1,133 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/12/14
* Time: 2:11 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
/**
* Class WriterFactory.
*/
final class WriterFactory
{
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholderWriter
*
* @return ColumnWriter
*/
public static function createColumnWriter(GenericBuilder $writer, PlaceholderWriter $placeholderWriter)
{
return new ColumnWriter($writer, $placeholderWriter);
}
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholderWriter
*
* @return WhereWriter
*/
public static function createWhereWriter(GenericBuilder $writer, PlaceholderWriter $placeholderWriter)
{
return new WhereWriter($writer, $placeholderWriter);
}
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholderWriter
*
* @return SelectWriter
*/
public static function createSelectWriter(GenericBuilder $writer, PlaceholderWriter $placeholderWriter)
{
return new SelectWriter($writer, $placeholderWriter);
}
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholderWriter
*
* @return InsertWriter
*/
public static function createInsertWriter(GenericBuilder $writer, PlaceholderWriter $placeholderWriter)
{
return new InsertWriter($writer, $placeholderWriter);
}
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholderWriter
*
* @return UpdateWriter
*/
public static function createUpdateWriter(GenericBuilder $writer, PlaceholderWriter $placeholderWriter)
{
return new UpdateWriter($writer, $placeholderWriter);
}
/**
* @param GenericBuilder $writer
* @param PlaceholderWriter $placeholderWriter
*
* @return DeleteWriter
*/
public static function createDeleteWriter(GenericBuilder $writer, PlaceholderWriter $placeholderWriter)
{
return new DeleteWriter($writer, $placeholderWriter);
}
/**
* @return PlaceholderWriter
*/
public static function createPlaceholderWriter()
{
return new PlaceholderWriter();
}
/**
* @param GenericBuilder $writer
*
* @return IntersectWriter
*/
public static function createIntersectWriter(GenericBuilder $writer)
{
return new IntersectWriter($writer);
}
/**
* @param GenericBuilder $writer
*
* @return MinusWriter
*/
public static function createMinusWriter(GenericBuilder $writer)
{
return new MinusWriter($writer);
}
/**
* @param GenericBuilder $writer
*
* @return UnionWriter
*/
public static function createUnionWriter(GenericBuilder $writer)
{
return new UnionWriter($writer);
}
/**
* @param GenericBuilder $writer
*
* @return UnionAllWriter
*/
public static function createUnionAllWriter(GenericBuilder $writer)
{
return new UnionAllWriter($writer);
}
}

View File

@ -0,0 +1,277 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
use NilPortugues\Sql\QueryBuilder\Syntax\QueryPartInterface;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
use NilPortugues\Sql\QueryBuilder\Syntax\Table;
use NilPortugues\Sql\QueryBuilder\Syntax\Where;
// Builder injects itself into query for convestion to SQL string.
use NilPortugues\Sql\QueryBuilder\Builder\BuilderInterface;
/**
* Class AbstractBaseQuery.
*/
abstract class AbstractBaseQuery implements QueryInterface, QueryPartInterface
{
/**
* @var string
*/
protected $comment = '';
/**
* @var \NilPortugues\Sql\QueryBuilder\Builder\BuilderInterface
*/
protected $builder;
/**
* @var string
*/
protected $table;
/**
* @var string
*/
protected $whereOperator = 'AND';
/**
* @var Where
*/
protected $where;
/**
* @var array
*/
protected $joins = [];
/**
* @var int
*/
protected $limitStart;
/**
* @var int
*/
protected $limitCount;
/**
* @var array
*/
protected $orderBy = [];
/**
* @return Where
*/
protected function filter()
{
if (!isset($this->where)) {
$this->where = QueryFactory::createWhere($this);
}
return $this->where;
}
/**
* Stores the builder that created this query.
*
* @param BuilderInterface $builder
*
* @return $this
*/
final public function setBuilder(BuilderInterface $builder)
{
$this->builder = $builder;
return $this;
}
/**
* @return BuilderInterface
*
* @throws \RuntimeException when builder has not been injected
*/
final public function getBuilder()
{
if (!$this->builder) {
throw new \RuntimeException('Query builder has not been injected with setBuilder');
}
return $this->builder;
}
/**
* Converts this query into an SQL string by using the injected builder.
*
* @return string
*/
public function __toString()
{
try {
return $this->getSql();
} catch (\Exception $e) {
return \sprintf('[%s] %s', \get_class($e), $e->getMessage());
}
}
/**
* Converts this query into an SQL string by using the injected builder.
* Optionally can return the SQL with formatted structure.
*
* @param bool $formatted
*
* @return string
*/
public function getSql($formatted = false)
{
if ($formatted) {
return $this->getBuilder()->writeFormatted($this);
}
return $this->getBuilder()->write($this);
}
/**
* @return string
*/
abstract public function partName();
/**
* @return Where
*/
public function getWhere()
{
return $this->where;
}
/**
* @param Where $where
*
* @return $this
*/
public function setWhere(Where $where)
{
$this->where = $where;
return $this;
}
/**
* @return Table
*/
public function getTable()
{
$newTable = array($this->table);
return \is_null($this->table) ? null : SyntaxFactory::createTable($newTable);
}
/**
* @param string $table
*
* @return $this
*/
public function setTable($table)
{
$this->table = (string) $table;
return $this;
}
/**
* @param string $whereOperator
*
* @return Where
*/
public function where($whereOperator = 'AND')
{
if (!isset($this->where)) {
$this->where = $this->filter();
}
$this->where->conjunction($whereOperator);
return $this->where;
}
/**
* @return string
*/
public function getWhereOperator()
{
if (!isset($this->where)) {
$this->where = $this->filter();
}
return $this->where->getConjunction();
}
/**
* @param string $column
* @param string $direction
* @param null $table
*
* @return $this
*/
public function orderBy($column, $direction = OrderBy::ASC, $table = null)
{
$newColumn = array($column);
$column = SyntaxFactory::createColumn($newColumn, \is_null($table) ? $this->getTable() : $table);
$this->orderBy[] = new OrderBy($column, $direction);
return $this;
}
/**
* @return int
*/
public function getLimitCount()
{
return $this->limitCount;
}
/**
* @return int
*/
public function getLimitStart()
{
return $this->limitStart;
}
/**
* @param string $comment
*
* @return $this
*/
public function setComment($comment)
{
// Make each line of the comment prefixed with "--",
// and remove any trailing whitespace.
$comment = '-- '.str_replace("\n", "\n-- ", \rtrim($comment));
// Trim off any trailing "-- ", to ensure that the comment is valid.
$this->comment = \rtrim($comment, '- ');
if ($this->comment) {
$this->comment .= "\n";
}
return $this;
}
/**
* @return string
*/
public function getComment()
{
return $this->comment;
}
}

View File

@ -0,0 +1,57 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/24/14
* Time: 12:30 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
/**
* Class AbstractCreationalQuery.
*/
abstract class AbstractCreationalQuery extends AbstractBaseQuery
{
/**
* @var array
*/
protected $values = [];
/**
* @param string $table
* @param array $values
*/
public function __construct($table = null, array $values = null)
{
if (isset($table)) {
$this->setTable($table);
}
if (!empty($values)) {
$this->setValues($values);
}
}
/**
* @return array
*/
public function getValues()
{
return $this->values;
}
/**
* @param array $values
*
* @return $this
*/
public function setValues(array $values)
{
$this->values = $values;
return $this;
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/24/14
* Time: 12:30 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\QueryPartInterface;
/**
* Class AbstractSetQuery.
*/
abstract class AbstractSetQuery implements QueryInterface, QueryPartInterface
{
/**
* @var array
*/
protected $union = [];
/**
* @param Select $select
*
* @return $this
*/
public function add(Select $select)
{
$this->union[] = $select;
return $this;
}
/**
* @return array
*/
public function getUnions()
{
return $this->union;
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Table
*/
public function getTable()
{
throw new QueryException(
\sprintf('%s does not support tables', $this->partName())
);
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function getWhere()
{
throw new QueryException(
\sprintf('%s does not support WHERE.', $this->partName())
);
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function where()
{
throw new QueryException(
\sprintf('%s does not support the WHERE statement.', $this->partName())
);
}
}

View File

@ -0,0 +1,263 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/25/14
* Time: 12:12 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
/**
* Class ColumnQuery.
*/
class ColumnQuery
{
/**
* @var array
*/
protected $columns = [];
/**
* @var array
*/
protected $columnSelects = [];
/**
* @var array
*/
protected $columnValues = [];
/**
* @var array
*/
protected $columnFuncs = [];
/**
* @var bool
*/
protected $isCount = false;
/**
* @var Select
*/
protected $select;
/**
* @var JoinQuery
*/
protected $joinQuery;
/**
* @param Select $select
* @param JoinQuery $joinQuery
* @param array $columns
*/
public function __construct(Select $select, JoinQuery $joinQuery, array $columns = null)
{
$this->select = $select;
$this->joinQuery = $joinQuery;
if (!isset($columns)) {
$columns = array(Column::ALL);
}
if (\count($columns)) {
$this->setColumns($columns);
}
}
/**
* @param $start
* @param int $count
*
* @return Select
*/
public function limit($start, $count = 0)
{
return $this->select->limit($start, $count);
}
/**
* @param string $whereOperator
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function where($whereOperator = 'AND')
{
return $this->select->where($whereOperator);
}
/**
* @param string $column
* @param string $direction
* @param null $table
*
* @return Select
*/
public function orderBy($column, $direction = OrderBy::ASC, $table = null)
{
return $this->select->orderBy($column, $direction, $table);
}
/**
* @param string[] $columns
*
* @return Select
*/
public function groupBy(array $columns)
{
return $this->select->groupBy($columns);
}
/**
* Allows setting a Select query as a column value.
*
* @param array $column
*
* @return $this
*/
public function setSelectAsColumn(array $column)
{
$this->columnSelects[] = $column;
return $this;
}
/**
* @return array
*/
public function getColumnSelects()
{
return $this->columnSelects;
}
/**
* Allows setting a value to the select statement.
*
* @param string $value
* @param string $alias
*
* @return $this
*/
public function setValueAsColumn($value, $alias)
{
$this->columnValues[$alias] = $value;
return $this;
}
/**
* @return array
*/
public function getColumnValues()
{
return $this->columnValues;
}
/**
* Allows calculation on columns using predefined SQL functions.
*
* @param string $funcName
* @param string[] $arguments
* @param string $alias
*
* @return $this
*/
public function setFunctionAsColumn($funcName, array $arguments, $alias)
{
$this->columnFuncs[$alias] = ['func' => $funcName, 'args' => $arguments];
return $this;
}
/**
* @return array
*/
public function getColumnFuncs()
{
return $this->columnFuncs;
}
/**
* @param string $columnName
* @param string $alias
*
* @return $this
*/
public function count($columnName = '*', $alias = '')
{
$table = $this->select->getTable();
$count = 'COUNT(';
$count .= ($columnName !== '*') ? "$table.{$columnName}" : '*';
$count .= ')';
if (isset($alias) && \strlen($alias) > 0) {
$count .= ' AS "'.$alias.'"';
}
$this->columns = array($count);
$this->isCount = true;
return $this;
}
/**
* @return bool
*/
public function isCount()
{
return $this->isCount;
}
/**
* @return array
*/
public function getAllColumns()
{
$columns = $this->getColumns();
foreach ($this->joinQuery->getJoins() as $join) {
$joinCols = $join->getAllColumns();
$columns = \array_merge($columns, $joinCols);
}
return $columns;
}
/**
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Column
*
* @throws QueryException
*/
public function getColumns()
{
if (\is_null($this->select->getTable())) {
throw new QueryException('No table specified for the Select instance');
}
return SyntaxFactory::createColumns($this->columns, $this->select->getTable());
}
/**
* Sets the column names used to write the SELECT statement.
* If key is set, key is the column's alias. Value is always the column names.
*
* @param array $columns
*
* @return $this
*/
public function setColumns(array $columns)
{
$this->columns = $columns;
return $this;
}
}

View File

@ -0,0 +1,60 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
/**
* Class Delete.
*/
class Delete extends AbstractBaseQuery
{
/**
* @var int
*/
protected $limitStart;
/**
* @param string $table
*/
public function __construct($table = null)
{
if (isset($table)) {
$this->setTable($table);
}
}
/**
* @return string
*/
public function partName()
{
return 'DELETE';
}
/**
* @return int
*/
public function getLimitStart()
{
return $this->limitStart;
}
/**
* @param int $start
*
* @return $this
*/
public function limit($start)
{
$this->limitStart = $start;
return $this;
}
}

View File

@ -0,0 +1,37 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
/**
* Class Insert.
*/
class Insert extends AbstractCreationalQuery
{
/**
* @return string
*/
public function partName()
{
return 'INSERT';
}
/**
* @return array
*/
public function getColumns()
{
$columns = \array_keys($this->values);
return SyntaxFactory::createColumns($columns, $this->getTable());
}
}

View File

@ -0,0 +1,84 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:11 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\QueryPartInterface;
/**
* Class Intersect.
*/
class Intersect implements QueryInterface, QueryPartInterface
{
const INTERSECT = 'INTERSECT';
/**
* @var array
*/
protected $intersect = [];
/**
* @return string
*/
public function partName()
{
return 'INTERSECT';
}
/**
* @param Select $select
*
* @return $this
*/
public function add(Select $select)
{
$this->intersect[] = $select;
return $this;
}
/**
* @return array
*/
public function getIntersects()
{
return $this->intersect;
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Table
*/
public function getTable()
{
throw new QueryException('INTERSECT does not support tables');
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function getWhere()
{
throw new QueryException('INTERSECT does not support WHERE.');
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function where()
{
throw new QueryException('INTERSECT does not support the WHERE statement.');
}
}

View File

@ -0,0 +1,308 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/25/14
* Time: 11:41 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\Where;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
/**
* Class JoinQuery.
*/
class JoinQuery
{
const JOIN_LEFT = 'LEFT';
const JOIN_RIGHT = 'RIGHT';
const JOIN_INNER = 'INNER';
const JOIN_CROSS = 'CROSS';
/**
* @var Where
*/
protected $joinCondition;
/**
* @var bool
*/
protected $isJoin = false;
/**
* @var string
*/
protected $joinType;
/**
* @var array
*/
protected $joins = [];
/**
* @var Select
*/
protected $select;
/**
* @param Select $select
*/
public function __construct(Select $select)
{
$this->select = $select;
}
/**
* @param string $table
*
* @return $this
*/
public function setTable($table)
{
$this->select->setTable($table);
return $this;
}
/**
* @param string $table
* @param mixed $selfColumn
* @param mixed $refColumn
* @param string[] $columns
*
* @return Select
*/
public function leftJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
{
return $this->join($table, $selfColumn, $refColumn, $columns, self::JOIN_LEFT);
}
/**
* @param string $table
* @param mixed $selfColumn
* @param mixed $refColumn
* @param string[] $columns
* @param string $joinType
*
* @return Select
*/
public function join(
$table,
$selfColumn = null,
$refColumn = null,
$columns = [],
$joinType = null
) {
if (!isset($this->joins[$table])) {
$select = QueryFactory::createSelect($table);
$select->setColumns($columns);
$select->setJoinType($joinType);
$select->setParentQuery($this->select);
$this->addJoin($select, $selfColumn, $refColumn);
}
return $this->joins[$table];
}
/**
* @param Select $select
* @param mixed $selfColumn
* @param mixed $refColumn
*
* @return Select
*/
public function addJoin(Select $select, $selfColumn, $refColumn)
{
$select->isJoin(true);
$table = $select->getTable()->getName();
if (!isset($this->joins[$table])) {
if (!$selfColumn instanceof Column) {
$newColumn = array($selfColumn);
$selfColumn = SyntaxFactory::createColumn(
$newColumn,
$this->select->getTable()
);
}
$select->joinCondition()->equals($refColumn, $selfColumn);
$this->joins[$table] = $select;
}
return $this->joins[$table];
}
/**
* Transforms Select in a joint.
*
* @param bool $isJoin
*
* @return $this
*/
public function setJoin($isJoin = true)
{
$this->isJoin = $isJoin;
return $this;
}
/**
* @param string $table
* @param mixed $selfColumn
* @param mixed $refColumn
* @param string[] $columns
*
* @internal param null $selectClass
*
* @return Select
*/
public function rightJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
{
return $this->join($table, $selfColumn, $refColumn, $columns, self::JOIN_RIGHT);
}
/**
* @param string $table
* @param mixed $selfColumn
* @param mixed $refColumn
* @param string[] $columns
*
* @return Select
*/
public function crossJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
{
return $this->join($table, $selfColumn, $refColumn, $columns, self::JOIN_CROSS);
}
/**
* @param string $table
* @param mixed $selfColumn
* @param mixed $refColumn
* @param string[] $columns
*
* @return Select
*/
public function innerJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
{
return $this->join($table, $selfColumn, $refColumn, $columns, self::JOIN_INNER);
}
/**
* Alias to joinCondition.
*
* @return Where
*/
public function on()
{
return $this->joinCondition();
}
/**
* WHERE constrains used for the ON clause of a (LEFT/RIGHT/INNER/CROSS) JOIN.
*
* @return Where
*/
public function joinCondition()
{
if (!isset($this->joinCondition)) {
$this->joinCondition = QueryFactory::createWhere($this->select);
}
return $this->joinCondition;
}
/**
* @return bool
*/
public function isJoinSelect()
{
return $this->isJoin;
}
/**
* @return bool
*/
public function isJoin()
{
return $this->isJoin;
}
/**
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function getJoinCondition()
{
return $this->joinCondition;
}
/**
* @param \NilPortugues\Sql\QueryBuilder\Syntax\Where $joinCondition
*
* @return $this
*/
public function setJoinCondition($joinCondition)
{
$this->joinCondition = $joinCondition;
return $this;
}
/**
* @return string
*/
public function getJoinType()
{
return $this->joinType;
}
/**
* @param string $joinType
*
* @return $this
*/
public function setJoinType($joinType)
{
$this->joinType = $joinType;
return $this;
}
/**
* @return array
*/
public function getJoins()
{
return $this->joins;
}
/**
* @param array $joins
*
* @return $this
*/
public function setJoins($joins)
{
$this->joins = $joins;
return $this;
}
/**
* @return array
*/
public function getAllJoins()
{
$joins = $this->joins;
foreach ($this->joins as $join) {
$joins = \array_merge($joins, $join->getAllJoins());
}
return $joins;
}
}

View File

@ -0,0 +1,95 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:11 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\QueryPartInterface;
/**
* Class Minus.
*/
class Minus implements QueryInterface, QueryPartInterface
{
const MINUS = 'MINUS';
/**
* @var Select
*/
protected $first;
/**
* @var Select
*/
protected $second;
/**
* @return string
*/
public function partName()
{
return 'MINUS';
}
/***
* @param Select $first
* @param Select $second
*/
public function __construct(Select $first, Select $second)
{
$this->first = $first;
$this->second = $second;
}
/**
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\Select
*/
public function getFirst()
{
return $this->first;
}
/**
* @return \NilPortugues\Sql\QueryBuilder\Manipulation\Select
*/
public function getSecond()
{
return $this->second;
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Table
*/
public function getTable()
{
throw new QueryException('MINUS does not support tables');
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function getWhere()
{
throw new QueryException('MINUS does not support WHERE.');
}
/**
* @throws QueryException
*
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function where()
{
throw new QueryException('MINUS does not support the WHERE statement.');
}
}

View File

@ -0,0 +1,18 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
/**
* Class QueryException.
*/
final class QueryException extends \Exception
{
}

View File

@ -0,0 +1,107 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\Where;
/**
* Class QueryFactory.
*/
final class QueryFactory
{
/**
* @param string $table
* @param array $columns
*
* @return Select
*/
public static function createSelect($table = null, array $columns = null)
{
return new Select($table, $columns);
}
/**
* @param string $table
* @param array $values
*
* @return Insert
*/
public static function createInsert($table = null, array $values = null)
{
return new Insert($table, $values);
}
/**
* @param string $table
* @param array $values
*
* @return Update
*/
public static function createUpdate($table = null, array $values = null)
{
return new Update($table, $values);
}
/**
* @param string $table
*
* @return Delete
*/
public static function createDelete($table = null)
{
return new Delete($table);
}
/**
* @param QueryInterface $query
*
* @return Where
*/
public static function createWhere(QueryInterface $query)
{
return new Where($query);
}
/**
* @return Intersect
*/
public static function createIntersect()
{
return new Intersect();
}
/**
* @param Select $first
* @param Select $second
*
* @return Minus
*/
public static function createMinus(Select $first, Select $second)
{
return new Minus($first, $second);
}
/**
* @return Union
*/
public static function createUnion()
{
return new Union();
}
/**
* @return UnionAll
*/
public static function createUnionAll()
{
return new UnionAll();
}
}

View File

@ -0,0 +1,37 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
/**
* Interface QueryInterface.
*/
interface QueryInterface
{
/**
* @return string
*/
public function partName();
/**
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Table
*/
public function getTable();
/**
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function getWhere();
/**
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Where
*/
public function where();
}

View File

@ -0,0 +1,546 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Syntax\SyntaxFactory;
use NilPortugues\Sql\QueryBuilder\Syntax\Table;
use NilPortugues\Sql\QueryBuilder\Syntax\Where;
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
/**
* Class Select.
*/
class Select extends AbstractBaseQuery
{
/**
* @var Table
*/
protected $table;
/**
* @var array
*/
protected $groupBy = [];
/**
* @var string
*/
protected $camelCaseTableName = '';
/**
* @var Where
*/
protected $having;
/**
* @var string
*/
protected $havingOperator = 'AND';
/**
* @var bool
*/
protected $isDistinct = false;
/**
* @var Where
*/
protected $where;
/**
* @var JoinQuery
*/
protected $joinQuery;
/**
* @var ColumnQuery
*/
protected $columnQuery;
/**
* @var ParentQuery
*/
protected $parentQuery;
/**
* @param string $table
* @param array $columns
*/
public function __construct($table = null, array $columns = null)
{
if (isset($table)) {
$this->setTable($table);
}
$this->joinQuery = new JoinQuery($this);
$this->columnQuery = new ColumnQuery($this, $this->joinQuery, $columns);
}
/**
* This __clone method will create an exact clone but without the object references due to the fact these
* are lost in the process of serialization and un-serialization.
*
* @return Select
*/
public function __clone()
{
return \unserialize(\serialize($this));
}
/**
* @return string
*/
public function partName()
{
return 'SELECT';
}
/**
* @param string $table
* @param string $selfColumn
* @param string $refColumn
* @param string[] $columns
*
* @return Select
*/
public function leftJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
{
return $this->joinQuery->leftJoin($table, $selfColumn, $refColumn, $columns);
}
/**
* @param string $table
* @param string $selfColumn
* @param string $refColumn
* @param string[] $columns
* @param string $joinType
*
* @return Select
*/
public function join(
$table,
$selfColumn = null,
$refColumn = null,
$columns = [],
$joinType = null
) {
return $this->joinQuery->join($table, $selfColumn, $refColumn, $columns, $joinType);
}
/**
* WHERE constrains used for the ON clause of a (LEFT/RIGHT/INNER/CROSS) JOIN.
*
* @return Where
*/
public function joinCondition()
{
return $this->joinQuery->joinCondition();
}
/**
* @param Select $select
* @param string $selfColumn
* @param string $refColumn
*
* @return Select
*/
public function addJoin(Select $select, $selfColumn, $refColumn)
{
return $this->joinQuery->addJoin($select, $selfColumn, $refColumn);
}
/**
* Transforms Select in a joint.
*
* @param bool $isJoin
*
* @return JoinQuery
*/
public function isJoin($isJoin = true)
{
return $this->joinQuery->setJoin($isJoin);
}
/**
* @param string $table
* @param string $selfColumn
* @param string $refColumn
* @param string[] $columns
*
* @internal param null $selectClass
*
* @return Select
*/
public function rightJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
{
return $this->joinQuery->rightJoin($table, $selfColumn, $refColumn, $columns);
}
/**
* @param string $table
* @param string $selfColumn
* @param string $refColumn
* @param string[] $columns
*
* @return Select
*/
public function crossJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
{
return $this->joinQuery->crossJoin($table, $selfColumn, $refColumn, $columns);
}
/**
* @param string $table
* @param string $selfColumn
* @param string $refColumn
* @param string[] $columns
*
* @return Select
*/
public function innerJoin($table, $selfColumn = null, $refColumn = null, $columns = [])
{
return $this->joinQuery->innerJoin($table, $selfColumn, $refColumn, $columns);
}
/**
* Alias to joinCondition.
*
* @return Where
*/
public function on()
{
return $this->joinQuery->joinCondition();
}
/**
* @return bool
*/
public function isJoinSelect()
{
return $this->joinQuery->isJoin();
}
/**
* @return array
*/
public function getAllColumns()
{
return $this->columnQuery->getAllColumns();
}
/**
* @return \NilPortugues\Sql\QueryBuilder\Syntax\Column
*
* @throws QueryException
*/
public function getColumns()
{
return $this->columnQuery->getColumns();
}
/**
* Sets the column names used to write the SELECT statement.
* If key is set, key is the column's alias. Value is always the column names.
*
* @param string[] $columns
*
* @return ColumnQuery
*/
public function setColumns(array $columns)
{
return $this->columnQuery->setColumns($columns);
}
/**
* Allows setting a Select query as a column value.
*
* @param array $column
*
* @return ColumnQuery
*/
public function setSelectAsColumn(array $column)
{
return $this->columnQuery->setSelectAsColumn($column);
}
/**
* @return array
*/
public function getColumnSelects()
{
return $this->columnQuery->getColumnSelects();
}
/**
* Allows setting a value to the select statement.
*
* @param string $value
* @param string $alias
*
* @return ColumnQuery
*/
public function setValueAsColumn($value, $alias)
{
return $this->columnQuery->setValueAsColumn($value, $alias);
}
/**
* @return array
*/
public function getColumnValues()
{
return $this->columnQuery->getColumnValues();
}
/**
* Allows calculation on columns using predefined SQL functions.
*
* @param string $funcName
* @param string[] $arguments
* @param string $alias
*
* @return ColumnQuery
*/
public function setFunctionAsColumn($funcName, array $arguments, $alias)
{
return $this->columnQuery->setFunctionAsColumn($funcName, $arguments, $alias);
}
/**
* @return array
*/
public function getColumnFuncs()
{
return $this->columnQuery->getColumnFuncs();
}
/**
* Returns all the Where conditions to the BuilderInterface class in order to write the SQL WHERE statement.
*
* @return array
*/
public function getAllWheres()
{
return $this->getAllOperation($this->where, 'getAllWheres');
}
/**
* @param null|Where $data
* @param string $operation
*
* @return array
*/
protected function getAllOperation($data, $operation)
{
$collection = [];
if (!is_null($data)) {
$collection[] = $data;
}
foreach ($this->joinQuery->getJoins() as $join) {
$collection = \array_merge($collection, $join->$operation());
}
return $collection;
}
/**
* @return array
*/
public function getAllHavings()
{
return $this->getAllOperation($this->having, 'getAllHavings');
}
/**
* @param string $columnName
* @param string $alias
*
* @return ColumnQuery
*/
public function count($columnName = '*', $alias = '')
{
return $this->columnQuery->count($columnName, $alias);
}
/**
* @return bool
*/
public function isCount()
{
return $this->columnQuery->isCount();
}
/**
* @param int $start
* @param $count
*
* @return $this
*/
public function limit($start, $count = 0)
{
$this->limitStart = $start;
$this->limitCount = $count;
return $this;
}
/**
* @return array
*/
public function getAllJoins()
{
return $this->joinQuery->getAllJoins();
}
/**
* @return array
*/
public function getGroupBy()
{
return SyntaxFactory::createColumns($this->groupBy, $this->getTable());
}
/**
* @param string[] $columns
*
* @return $this
*/
public function groupBy(array $columns)
{
$this->groupBy = $columns;
return $this;
}
/**
* @return Where
*/
public function getJoinCondition()
{
return $this->joinQuery->getJoinCondition();
}
/**
* @return string
*/
public function getJoinType()
{
return $this->joinQuery->getJoinType();
}
/**
* @param string|null $joinType
*
* @return $this
*/
public function setJoinType($joinType)
{
$this->joinQuery->setJoinType($joinType);
return $this;
}
/**
* @param $havingOperator
*
* @throws QueryException
*
* @return Where
*/
public function having($havingOperator = 'AND')
{
if (!isset($this->having)) {
$this->having = QueryFactory::createWhere($this);
}
if (!in_array($havingOperator, array(Where::CONJUNCTION_AND, Where::CONJUNCTION_OR))) {
throw new QueryException(
"Invalid conjunction specified, must be one of AND or OR, but '".$havingOperator."' was found."
);
}
$this->havingOperator = $havingOperator;
return $this->having;
}
/**
* @return string
*/
public function getHavingOperator()
{
return $this->havingOperator;
}
/**
* @return $this
*/
public function distinct()
{
$this->isDistinct = true;
return $this;
}
/**
* @return bool
*/
public function isDistinct()
{
return $this->isDistinct;
}
/**
* @return array
*/
public function getAllOrderBy()
{
return $this->orderBy;
}
/**
* @return ParentQuery
*/
public function getParentQuery()
{
return $this->parentQuery;
}
/**
* @param Select $parentQuery
*
* @return $this
*/
public function setParentQuery(Select $parentQuery)
{
$this->parentQuery = $parentQuery;
return $this;
}
/**
* @param string $column
* @param string $direction
* @param null $table
*
* @return $this
*/
public function orderBy($column, $direction = OrderBy::ASC, $table = null)
{
$current = parent::orderBy($column, $direction, $table);
if ($this->getParentQuery() != null) {
$this->getParentQuery()->orderBy($column, $direction, \is_null($table) ? $this->getTable() : $table);
}
return $current;
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:11 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
/**
* Class Union.
*/
class Union extends AbstractSetQuery
{
const UNION = 'UNION';
/**
* @return string
*/
public function partName()
{
return 'UNION';
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:11 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
/**
* Class UnionAll.
*/
class UnionAll extends AbstractSetQuery
{
const UNION_ALL = 'UNION ALL';
/**
* @return string
*/
public function partName()
{
return 'UNION ALL';
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Manipulation;
/**
* Class Update.
*/
class Update extends AbstractCreationalQuery
{
/**
* @var int
*/
protected $limitStart;
/**
* @var array
*/
protected $orderBy = [];
/**
* @return string
*/
public function partName()
{
return 'UPDATE';
}
/**
* @return int
*/
public function getLimitStart()
{
return $this->limitStart;
}
/**
* @param int $start
*
* @return $this
*/
public function limit($start)
{
$this->limitStart = $start;
return $this;
}
}

View File

@ -0,0 +1,139 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Syntax;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryException;
/**
* Class Column.
*/
class Column implements QueryPartInterface
{
const ALL = '*';
/**
* @var Table
*/
protected $table;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $alias;
/**
* @param string $name
* @param string $table
* @param string $alias
*/
public function __construct($name, $table, $alias = '')
{
$this->setName($name);
$this->setTable($table);
$this->setAlias($alias);
}
/**
* @return string
*/
public function partName()
{
return 'COLUMN';
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*
* @return $this
*/
public function setName($name)
{
$this->name = (string) $name;
return $this;
}
/**
* @return Table
*/
public function getTable()
{
return $this->table;
}
/**
* @param string $table
*
* @return $this
*/
public function setTable($table)
{
$newTable = array($table);
$this->table = SyntaxFactory::createTable($newTable);
return $this;
}
/**
* @return string
*/
public function getAlias()
{
return $this->alias;
}
/**
* @param null|string $alias
*
* @return $this
*
* @throws QueryException
*/
public function setAlias($alias)
{
if (0 == \strlen($alias)) {
$this->alias = null;
return $this;
}
if ($this->isAll()) {
throw new QueryException("Can't use alias because column name is ALL (*)");
}
$this->alias = (string) $alias;
return $this;
}
/**
* Check whether column name is '*' or not.
*
* @return bool
*/
public function isAll()
{
return $this->getName() == self::ALL;
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Syntax;
/**
* Class OrderBy.
*/
class OrderBy
{
const ASC = 'ASC';
const DESC = 'DESC';
/**
* @var Column
*/
protected $column;
/**
* @var string
*/
protected $direction;
/**
* @var bool
*/
protected $useAlias;
/**
* @param Column $column
* @param string $direction
*/
public function __construct(Column $column, $direction)
{
$this->setColumn($column);
$this->setDirection($direction);
}
/**
* @return Column
*/
public function getColumn()
{
return $this->column;
}
/**
* @param Column $column
*
* @return $this
*/
public function setColumn($column)
{
$this->column = $column;
return $this;
}
/**
* @return string
*/
public function getDirection()
{
return $this->direction;
}
/**
* @param string $direction
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setDirection($direction)
{
if (!in_array($direction, array(self::ASC, self::DESC))) {
throw new \InvalidArgumentException(
"Specified direction '$direction' is not allowed. Only ASC or DESC are allowed."
);
}
$this->direction = $direction;
return $this;
}
}

View File

@ -0,0 +1,22 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/8/14
* Time: 5:32 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Syntax;
/**
* Interface QueryPartInterface.
*/
interface QueryPartInterface
{
/**
* @return string
*/
public function partName();
}

View File

@ -0,0 +1,93 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Syntax;
/**
* Class SyntaxFactory.
*/
final class SyntaxFactory
{
/**
* Creates a collection of Column objects.
*
* @param array $arguments
* @param Table|null $table
*
* @return array
*/
public static function createColumns(array &$arguments, $table = null)
{
$createdColumns = [];
foreach ($arguments as $index => $column) {
if (!is_object($column)) {
$newColumn = array($column);
$column = self::createColumn($newColumn, $table);
if (!is_numeric($index)) {
$column->setAlias($index);
}
$createdColumns[] = $column;
} else if ($column instanceof Column) {
$createdColumns[] = $column;
}
}
return \array_filter($createdColumns);
}
/**
* Creates a Column object.
*
* @param array $argument
* @param null|Table $table
*
* @return Column
*/
public static function createColumn(array &$argument, $table = null)
{
$columnName = \array_values($argument);
$columnName = $columnName[0];
$columnAlias = \array_keys($argument);
$columnAlias = $columnAlias[0];
if (\is_numeric($columnAlias) || \strpos($columnName, '*') !== false) {
$columnAlias = null;
}
return new Column($columnName, (string) $table, $columnAlias);
}
/**
* Creates a Table object.
*
* @param string[] $table
*
* @return Table
*/
public static function createTable($table)
{
$tableName = $table;
if (\is_array($table)) {
$tableName = \current($table);
$tableAlias = \key($table);
}
$newTable = new Table($tableName);
if (isset($tableAlias) && !is_numeric($tableAlias)) {
$newTable->setAlias($tableAlias);
}
return $newTable;
}
}

View File

@ -0,0 +1,138 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Syntax;
/**
* Class Table.
*/
class Table
{
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $alias;
/**
* @var string
*/
protected $schema;
/**
* @var bool
*/
protected $view = false;
/**
* @param $name
* @param string $schema
*/
public function __construct($name, $schema = null)
{
$this->name = $name;
if (!is_null($schema)) {
$this->schema = $schema;
}
}
/**
* @return string
*/
public function __toString()
{
return (string) $this->name;
}
/**
* @param bool $view
*
* @return $this
*/
public function setView($view)
{
$this->view = $view;
return $this;
}
/**
* @return bool
*/
public function isView()
{
return $this->view;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return string
*/
public function getAlias()
{
return $this->alias;
}
/**
* @return string
*/
public function getCompleteName()
{
$alias = ($this->alias) ? " AS {$this->alias}" : '';
$schema = ($this->schema) ? "{$this->schema}." : '';
return $schema.$this->name.$alias;
}
/**
* @param string $alias
*
* @return $this
*/
public function setAlias($alias)
{
$this->alias = $alias;
return $this;
}
/**
* @return string
*/
public function getSchema()
{
return $this->schema;
}
/**
* @param string
* @param string $schema
*
* @return $this
*/
public function setSchema($schema)
{
$this->schema = $schema;
return $this;
}
}

View File

@ -0,0 +1,646 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryBuilder\Syntax;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryException;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryFactory;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryInterface;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class Where.
*/
class Where
{
const OPERATOR_GREATER_THAN_OR_EQUAL = '>=';
const OPERATOR_GREATER_THAN = '>';
const OPERATOR_LESS_THAN_OR_EQUAL = '<=';
const OPERATOR_LESS_THAN = '<';
const OPERATOR_LIKE = 'LIKE';
const OPERATOR_NOT_LIKE = 'NOT LIKE';
const OPERATOR_EQUAL = '=';
const OPERATOR_NOT_EQUAL = '<>';
const CONJUNCTION_AND = 'AND';
const CONJUNCTION_AND_NOT = 'AND NOT';
const CONJUNCTION_OR = 'OR';
const CONJUNCTION_OR_NOT = 'OR NOT';
const CONJUNCTION_EXISTS = 'EXISTS';
const CONJUNCTION_NOT_EXISTS = 'NOT EXISTS';
/**
* @var array
*/
protected $comparisons = [];
/**
* @var array
*/
protected $betweens = [];
/**
* @var array
*/
protected $isNull = [];
/**
* @var array
*/
protected $isNotNull = [];
/**
* @var array
*/
protected $booleans = [];
/**
* @var array
*/
protected $match = [];
/**
* @var array
*/
protected $ins = [];
/**
* @var array
*/
protected $notIns = [];
/**
* @var array
*/
protected $subWheres = [];
/**
* @var string
*/
protected $conjunction = self::CONJUNCTION_AND;
/**
* @var QueryInterface
*/
protected $query;
/**
* @var Table
*/
protected $table;
/**
* @var array
*/
protected $exists = [];
/**
* @var array
*/
protected $notExists = [];
/**
* @var array
*/
protected $notBetweens = [];
/**
* @param QueryInterface $query
*/
public function __construct(QueryInterface $query)
{
$this->query = $query;
}
/**
* Deep copy for nested references.
*
* @return mixed
*/
public function __clone()
{
return \unserialize(\serialize($this));
}
/**
* @return bool
*/
public function isEmpty()
{
$empty = \array_merge(
$this->comparisons,
$this->booleans,
$this->betweens,
$this->isNotNull,
$this->isNull,
$this->ins,
$this->notIns,
$this->subWheres,
$this->exists
);
return 0 == \count($empty);
}
/**
* @return string
*/
public function getConjunction()
{
return $this->conjunction;
}
/**
* @param string $operator
*
* @return $this
*
* @throws QueryException
*/
public function conjunction($operator)
{
if (false === \in_array(
$operator,
[self::CONJUNCTION_AND, self::CONJUNCTION_OR, self::CONJUNCTION_OR_NOT, self::CONJUNCTION_AND_NOT]
)
) {
throw new QueryException(
"Invalid conjunction specified, must be one of AND or OR, but '".$operator."' was found."
);
}
$this->conjunction = $operator;
return $this;
}
/**
* @return array
*/
public function getSubWheres()
{
return $this->subWheres;
}
/**
* @param $operator
*
* @return Where
*/
public function subWhere($operator = 'OR')
{
/** @var Where $filter */
$filter = QueryFactory::createWhere($this->query);
$filter->conjunction($operator);
$filter->setTable($this->getTable());
$this->subWheres[] = $filter;
return $filter;
}
/**
* @return Table
*/
public function getTable()
{
return $this->query->getTable();
}
/**
* Used for subWhere query building.
*
* @param Table $table string
*
* @return $this
*/
public function setTable($table)
{
$this->table = $table;
return $this;
}
/**
* equals alias.
*
* @param $column
* @param int $value
*
* @return static
*/
public function eq($column, $value)
{
return $this->equals($column, $value);
}
/**
* @param $column
* @param $value
*
* @return static
*/
public function equals($column, $value)
{
return $this->compare($column, $value, self::OPERATOR_EQUAL);
}
/**
* @param $column
* @param $value
* @param string $operator
*
* @return $this
*/
protected function compare($column, $value, $operator)
{
$column = $this->prepareColumn($column);
$this->comparisons[] = [
'subject' => $column,
'conjunction' => $operator,
'target' => $value,
];
return $this;
}
/**
* @param $column
*
* @return Column|Select
*/
protected function prepareColumn($column)
{
//This condition handles the "Select as a a column" special case.
//or when compare column is customized.
if ($column instanceof Select || $column instanceof Column) {
return $column;
}
$newColumn = [$column];
return SyntaxFactory::createColumn($newColumn, $this->getTable());
}
/**
* @param string $column
* @param int $value
*
* @return static
*/
public function notEquals($column, $value)
{
return $this->compare($column, $value, self::OPERATOR_NOT_EQUAL);
}
/**
* @param string $column
* @param int $value
*
* @return static
*/
public function greaterThan($column, $value)
{
return $this->compare($column, $value, self::OPERATOR_GREATER_THAN);
}
/**
* @param string $column
* @param int $value
*
* @return static
*/
public function greaterThanOrEqual($column, $value)
{
return $this->compare($column, $value, self::OPERATOR_GREATER_THAN_OR_EQUAL);
}
/**
* @param string $column
* @param int $value
*
* @return static
*/
public function lessThan($column, $value)
{
return $this->compare($column, $value, self::OPERATOR_LESS_THAN);
}
/**
* @param string $column
* @param int $value
*
* @return static
*/
public function lessThanOrEqual($column, $value)
{
return $this->compare($column, $value, self::OPERATOR_LESS_THAN_OR_EQUAL);
}
/**
* @param string $column
* @param $value
*
* @return static
*/
public function like($column, $value)
{
return $this->compare($column, $value, self::OPERATOR_LIKE);
}
/**
* @param string $column
* @param int $value
*
* @return static
*/
public function notLike($column, $value)
{
return $this->compare($column, $value, self::OPERATOR_NOT_LIKE);
}
/**
* @param string[] $columns
* @param mixed[] $values
*
* @return static
*/
public function match(array $columns, array $values)
{
return $this->genericMatch($columns, $values, 'natural');
}
/**
* @param string[] $columns
* @param mixed[] $values
* @param string $mode
*
* @return $this
*/
protected function genericMatch(array &$columns, array &$values, $mode)
{
$this->match[] = [
'columns' => $columns,
'values' => $values,
'mode' => $mode,
];
return $this;
}
/**
* @param string $literal
*
* @return $this
*/
public function asLiteral($literal)
{
$this->comparisons[] = $literal;
return $this;
}
/**
* @param string[] $columns
* @param mixed[] $values
*
* @return $this
*/
public function matchBoolean(array $columns, array $values)
{
return $this->genericMatch($columns, $values, 'boolean');
}
/**
* @param string[] $columns
* @param mixed[] $values
*
* @return $this
*/
public function matchWithQueryExpansion(array $columns, array $values)
{
return $this->genericMatch($columns, $values, 'query_expansion');
}
/**
* @param string $column
* @param int[] $values
*
* @return $this
*/
public function in($column, array $values)
{
$this->ins[$column] = $values;
return $this;
}
/**
* @param string $column
* @param int[] $values
*
* @return $this
*/
public function notIn($column, array $values)
{
$this->notIns[$column] = $values;
return $this;
}
/**
* @param string $column
* @param int $a
* @param int $b
*
* @return $this
*/
public function between($column, $a, $b)
{
$column = $this->prepareColumn($column);
$this->betweens[] = ['subject' => $column, 'a' => $a, 'b' => $b];
return $this;
}
/**
* @param string $column
* @param int $a
* @param int $b
*
* @return $this
*/
public function notBetween($column, $a, $b)
{
$column = $this->prepareColumn($column);
$this->notBetweens[] = ['subject' => $column, 'a' => $a, 'b' => $b];
return $this;
}
/**
* @param string $column
*
* @return static
*/
public function isNull($column)
{
$column = $this->prepareColumn($column);
$this->isNull[] = ['subject' => $column];
return $this;
}
/**
* @param string $column
*
* @return $this
*/
public function isNotNull($column)
{
$column = $this->prepareColumn($column);
$this->isNotNull[] = ['subject' => $column];
return $this;
}
/**
* @param string $column
* @param int $value
*
* @return $this
*/
public function addBitClause($column, $value)
{
$column = $this->prepareColumn($column);
$this->booleans[] = ['subject' => $column, 'value' => $value];
return $this;
}
/**
* @param Select $select
*
* @return $this
*/
public function exists(Select $select)
{
$this->exists[] = $select;
return $this;
}
/**
* @return array
*/
public function getExists()
{
return $this->exists;
}
/**
* @param Select $select
*
* @return $this
*/
public function notExists(Select $select)
{
$this->notExists[] = $select;
return $this;
}
/**
* @return array
*/
public function getNotExists()
{
return $this->notExists;
}
/**
* @return array
*/
public function getMatches()
{
return $this->match;
}
/**
* @return array
*/
public function getIns()
{
return $this->ins;
}
/**
* @return array
*/
public function getNotIns()
{
return $this->notIns;
}
/**
* @return array
*/
public function getBetweens()
{
return $this->betweens;
}
/**
* @return array
*/
public function getNotBetweens()
{
return $this->notBetweens;
}
/**
* @return array
*/
public function getBooleans()
{
return $this->booleans;
}
/**
* @return array
*/
public function getComparisons()
{
return $this->comparisons;
}
/**
* @return array
*/
public function getNotNull()
{
return $this->isNotNull;
}
/**
* @return array
*/
public function getNull()
{
return $this->isNull;
}
/**
* @return QueryInterface
*/
public function end()
{
return $this->query;
}
}

View File

@ -0,0 +1,284 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/16/14
* Time: 8:56 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
class GenericBuilderTest extends \PHPUnit_Framework_TestCase
{
/**
* @var GenericBuilder
*/
private $writer;
/**
*
*/
public function setUp()
{
$this->writer = new GenericBuilder();
}
/**
* @test
*/
public function itShouldCreateSelectObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Select';
$this->assertInstanceOf($className, $this->writer->select());
}
/**
* @test
*/
public function itShouldCreateInsertObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Insert';
$this->assertInstanceOf($className, $this->writer->insert());
}
/**
* @test
*/
public function itShouldCreateUpdateObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Update';
$this->assertInstanceOf($className, $this->writer->update());
}
/**
* @test
*/
public function itShouldCreateDeleteObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Delete';
$this->assertInstanceOf($className, $this->writer->delete());
}
/**
* @test
*/
public function itShouldCreateIntersectObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Intersect';
$this->assertInstanceOf($className, $this->writer->intersect());
}
/**
* @test
*/
public function itShouldCreateMinusObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Minus';
$this->assertInstanceOf($className, $this->writer->minus(new Select('table1'), new Select('table2')));
}
/**
* @test
*/
public function itShouldCreateUnionObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Union';
$this->assertInstanceOf($className, $this->writer->union());
}
/**
* @test
*/
public function itShouldCreateUnionAllObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\UnionAll';
$this->assertInstanceOf($className, $this->writer->unionAll());
}
/**
* @test
*/
public function itCanAcceptATableNameForSelectInsertUpdateDeleteQueries()
{
$table = 'user';
$queries = [
'select' => $this->writer->select($table),
'insert' => $this->writer->insert($table),
'update' => $this->writer->update($table),
'delete' => $this->writer->delete($table),
];
foreach ($queries as $type => $query) {
$this->assertEquals($table, $query->getTable()->getName(), "Checking table in $type query");
}
}
/**
* @test
*/
public function itCanAcceptATableAndColumnsForSelect()
{
$table = 'user';
$columns = ['id', 'role'];
$expected = <<<QUERY
SELECT
user.id,
user.role
FROM
user
QUERY;
$select = $this->writer->select($table, $columns);
$this->assertSame($expected, $this->writer->writeFormatted($select));
}
/**
* @test
*/
public function itCanAcceptATableAndValuesForInsert()
{
$table = 'user';
$values = ['id' => 1, 'role' => 'admin'];
$expected = <<<QUERY
INSERT INTO user (user.id, user.role)
VALUES
(:v1, :v2)
QUERY;
$insert = $this->writer->insert($table, $values);
$this->assertSame($expected, $this->writer->writeFormatted($insert));
}
/**
* @test
*/
public function itCanAcceptATableAndValuesForUpdate()
{
$table = 'user';
$values = ['id' => 1, 'role' => 'super-admin'];
$expected = <<<QUERY
UPDATE
user
SET
user.id = :v1,
user.role = :v2
QUERY;
$update = $this->writer->update($table, $values);
$this->assertSame($expected, $this->writer->writeFormatted($update));
}
/**
* @test
*/
public function itShouldOutputHumanReadableQuery()
{
$selectRole = $this->writer->select();
$selectRole
->setTable('role')
->setColumns(array('role_name'))
->limit(1)
->where()
->equals('role_id', 3);
$select = $this->writer->select();
$select->setTable('user')
->setColumns(array('user_id', 'username'))
->setSelectAsColumn(array('user_role' => $selectRole))
->setSelectAsColumn(array($selectRole))
->where()
->equals('user_id', 4);
$expected = <<<QUERY
SELECT
user.user_id,
user.username,
(
SELECT
role.role_name
FROM
role
WHERE
(role.role_id = :v1)
LIMIT
:v2,
:v3
) AS "user_role",
(
SELECT
role.role_name
FROM
role
WHERE
(role.role_id = :v4)
LIMIT
:v5,
:v6
) AS "role"
FROM
user
WHERE
(user.user_id = :v7)
QUERY;
$this->assertSame($expected, $this->writer->writeFormatted($select));
}
/**
* @test
*/
public function it_should_inject_the_builder()
{
$query = $this->writer->select();
$this->assertSame($this->writer, $query->getBuilder());
}
/**
* @test
*/
public function itShouldWriteWhenGettingSql()
{
$query = $this->writer->select()
->setTable('user');
$expected = $this->writer->write($query);
$this->assertSame($expected, $query->getSql());
}
/**
* @test
*/
public function itShouldWriteFormattedWhenGettingFormattedSql()
{
$query = $this->writer->select()
->setTable('user');
$formatted = true;
$expected = $this->writer->writeFormatted($query);
$this->assertSame($expected, $query->getSql($formatted));
}
/**
* @test
*/
public function itShouldWriteSqlWhenCastToString()
{
$query = $this->writer->select()
->setTable('user');
$expected = $this->writer->write($query);
$this->assertSame($expected, (string) $query);
}
}

View File

@ -0,0 +1,74 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/4/14
* Time: 12:40 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder;
use NilPortugues\Sql\QueryBuilder\Builder\MySqlBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class MySqlBuilderTest.
*/
class MySqlBuilderTest extends \PHPUnit_Framework_TestCase
{
/**
* @var MySqlBuilder
*/
protected $writer;
/**
*
*/
protected function setUp()
{
$this->writer = new MySqlBuilder();
}
/**
*
*/
protected function tearDown()
{
$this->writer = null;
}
/**
* @test
*/
public function itShouldWrapTableNames()
{
$query = new Select('user');
$expected = 'SELECT `user`.* FROM `user`';
$this->assertSame($expected, $this->writer->write($query));
}
/**
* @test
*/
public function itShouldWrapColumnNames()
{
$query = new Select('user', array('user_id', 'name'));
$expected = 'SELECT `user`.`user_id`, `user`.`name` FROM `user`';
$this->assertSame($expected, $this->writer->write($query));
}
/**
* @test
*/
public function itShouldWrapColumnAlias()
{
$query = new Select('user', array('userId' => 'user_id', 'name' => 'name'));
$expected = 'SELECT `user`.`user_id` AS `userId`, `user`.`name` AS `name` FROM `user`';
$this->assertSame($expected, $this->writer->write($query));
}
}

View File

@ -0,0 +1,187 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 10:45 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\ColumnWriter;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\PlaceholderWriter;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
/**
* Class ColumnWriterTest.
*/
class ColumnWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var ColumnWriter
*/
private $columnWriter;
/**
* @var GenericBuilder
*/
private $writer;
/**
* @var Select
*/
private $query;
/**
*
*/
protected function setUp()
{
$this->writer = new GenericBuilder();
$this->query = new Select();
$this->columnWriter = new ColumnWriter(new GenericBuilder(), new PlaceholderWriter());
}
/**
* @test
*/
public function itShouldWriteColumn()
{
$column = new Column('user_id', 'user');
$result = $this->columnWriter->writeColumn($column);
$this->assertSame('user.user_id', $result);
}
/**
* @test
*/
public function itShouldWriteValueAsColumns()
{
$select = new Select('user');
$select->setValueAsColumn('1', 'user_id');
$result = $this->columnWriter->writeValueAsColumns($select);
$this->assertInstanceOf('NilPortugues\Sql\QueryBuilder\Syntax\Column', $result[0]);
}
/**
* @test
*/
public function itShouldWriteFuncAsColumns()
{
$select = new Select('user');
$select->setFunctionAsColumn('MAX', ['user_id'], 'max_value');
$result = $this->columnWriter->writeFuncAsColumns($select);
$this->assertInstanceOf('NilPortugues\Sql\QueryBuilder\Syntax\Column', $result[0]);
}
/**
* @test
*/
public function itShouldWriteColumnWithAlias()
{
$column = new Column('user_id', 'user', 'userId');
$result = $this->columnWriter->writeColumnWithAlias($column);
$this->assertSame('user.user_id AS "userId"', $result);
}
/**
* @test
*/
public function itShouldBeAbleToWriteColumnAsASelectStatement()
{
$selectRole = new Select();
$selectRole
->setTable('role')
->setColumns(array('role_name'))
->limit(1)
->where()
->equals('role_id', 3);
$this->query
->setTable('user')
->setColumns(array('user_id', 'username'))
->setSelectAsColumn(array('user_role' => $selectRole))
->setSelectAsColumn(array($selectRole))
->where()
->equals('user_id', 4);
$expected = 'SELECT user.user_id, user.username, '.
'(SELECT role.role_name FROM role WHERE (role.role_id = :v1) LIMIT :v2, :v3) AS "user_role", '.
'(SELECT role.role_name FROM role WHERE (role.role_id = :v4) LIMIT :v5, :v6) AS "role" '.
'FROM user WHERE (user.user_id = :v7)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 3, ':v2' => 1, ':v3' => 0, ':v4' => 3, ':v5' => 1, ':v6' => 0, ':v7' => 4);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToWriteColumnAsAValueStatement()
{
$this->query
->setTable('user')
->setColumns(array('user_id', 'username'))
->setValueAsColumn('10', 'priority')
->where()
->equals('user_id', 1);
$expected = 'SELECT user.user_id, user.username, :v1 AS "priority" FROM user WHERE (user.user_id = :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 10, ':v2' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToWriteColumnAsAFuncWithBracketsStatement()
{
$this->query
->setTable('user')
->setColumns(array('user_id', 'username'))
->setFunctionAsColumn('MAX', array('user_id'), 'max_id')
->where()
->equals('user_id', 1);
$expected = 'SELECT user.user_id, user.username, MAX(user_id) AS "max_id" FROM user WHERE (user.user_id = :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToWriteColumnAsAFuncWithoutBracketsStatement()
{
$this->query
->setTable('user')
->setColumns(array('user_id', 'username'))
->setFunctionAsColumn('CURRENT_TIMESTAMP', array(), 'server_time')
->where()
->equals('user_id', 1);
$expected = 'SELECT user.user_id, user.username, CURRENT_TIMESTAMP AS "server_time" FROM user WHERE (user.user_id = :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
}

View File

@ -0,0 +1,106 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 10:45 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Delete;
/**
* Class DeleteWriterTest.
*/
class DeleteWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var GenericBuilder
*/
private $writer;
/**
* @var Delete
*/
private $query;
/**
*
*/
protected function setUp()
{
$this->writer = new GenericBuilder();
$this->query = new Delete();
}
/**
* @test
*/
public function itShouldWriteDeleteAllTableContentsQuery()
{
$this->query->setTable('user');
$expected = 'DELETE FROM user';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldWriteDeleteRowLimit1()
{
$this->query
->setTable('user')
->limit(1);
$expected = 'DELETE FROM user LIMIT :v1';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToWriteCommentInQuery()
{
$this->query
->setTable('user')
->setComment('This is a comment');
$expected = <<<SQL
-- This is a comment
DELETE FROM user
SQL;
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldWriteDeleteRowWithWhereConditionAndLimit1()
{
$this->query->setTable('user');
$conditions = $this->query->where();
$conditions
->equals('user_id', 10)
->equals('user_id', 20)
->equals('user_id', 30);
$this->query->limit(1);
$expected = <<<SQL
DELETE FROM user WHERE (user.user_id = :v1) AND (user.user_id = :v2) AND (user.user_id = :v3) LIMIT :v4
SQL;
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 10, ':v2' => 20, ':v3' => 30, ':v4' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
}

View File

@ -0,0 +1,104 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 10:45 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Insert;
/**
* Class InsertWriterTest.
*/
class InsertWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var GenericBuilder
*/
private $writer;
/**
* @var Insert
*/
private $query;
/**
* @var string
*/
private $exceptionClass = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
*
*/
protected function setUp()
{
$this->writer = new GenericBuilder();
$this->query = new Insert();
}
/**
* @test
*/
public function itShouldThrowQueryExceptionBecauseNoColumnsWereDefined()
{
$this->setExpectedException($this->exceptionClass, 'No columns were defined for the current schema.');
$this->query->setTable('user');
$this->writer->write($this->query);
}
/**
* @test
*/
public function itShouldWriteInsertQuery()
{
$valueArray = array(
'user_id' => 1,
'name' => 'Nil',
'contact' => 'contact@nilportugues.com',
);
$this->query
->setTable('user')
->setValues($valueArray);
$expected = 'INSERT INTO user (user.user_id, user.name, user.contact) VALUES (:v1, :v2, :v3)';
$this->assertSame($expected, $this->writer->write($this->query));
$this->assertEquals(\array_values($valueArray), \array_values($this->query->getValues()));
$expected = array(':v1' => 1, ':v2' => 'Nil', ':v3' => 'contact@nilportugues.com');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToWriteCommentInQuery()
{
$valueArray = array(
'user_id' => 1,
'name' => 'Nil',
'contact' => 'contact@nilportugues.com',
);
$this->query
->setTable('user')
->setComment('This is a comment')
->setValues($valueArray);
$expected = "-- This is a comment\n".'INSERT INTO user (user.user_id, user.name, user.contact) VALUES (:v1, :v2, :v3)';
$this->assertSame($expected, $this->writer->write($this->query));
$this->assertEquals(\array_values($valueArray), \array_values($this->query->getValues()));
$expected = array(':v1' => 1, ':v2' => 'Nil', ':v3' => 'contact@nilportugues.com');
$this->assertEquals($expected, $this->writer->getValues());
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:34 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\IntersectWriter;
use NilPortugues\Sql\QueryBuilder\Manipulation\Intersect;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class IntersectWriterTest.
*/
class IntersectWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var GenericBuilder
*/
private $writer;
/**
* @var IntersectWriter
*/
private $intersectWriter;
/**
*
*/
public function setUp()
{
$this->intersectWriter = new IntersectWriter(new GenericBuilder());
$this->writer = new GenericBuilder();
}
public function tearDown()
{
$this->intersectWriter = null;
$this->writer = null;
}
/**
* @test
*/
public function itShouldWriteIntersect()
{
$intersect = new Intersect();
$intersect->add(new Select('user'));
$intersect->add(new Select('user_email'));
$expected = <<<SQL
SELECT user.* FROM user
INTERSECT
SELECT user_email.* FROM user_email
SQL;
$this->assertEquals($expected, $this->intersectWriter->write($intersect));
}
/**
* @test
*/
public function itShouldWriteIntersectFromGenericBuilder()
{
$intersect = $this->writer->intersect();
$intersect->add(new Select('user'));
$intersect->add(new Select('user_email'));
$expected = <<<SQL
SELECT user.* FROM user
INTERSECT
SELECT user_email.* FROM user_email
SQL;
$this->assertEquals($expected, $this->writer->write($intersect));
}
/**
* @test
*/
public function itShouldNotResetPlaceholders()
{
$select1 = (new Select('table1'))
->where()
->between('column', 1, 2)
->end();
$select2 = (new Select('table2'))
->where()
->between('column', 3, 4)
->end();
$union = (new Intersect())
->add($select1)
->add($select2);
$expectedSql = <<<SQL
SELECT table1.* FROM table1 WHERE (table1.column BETWEEN :v1 AND :v2)
INTERSECT
SELECT table2.* FROM table2 WHERE (table2.column BETWEEN :v3 AND :v4)
SQL;
$expectedParams = [
':v1' => 1,
':v2' => 2,
':v3' => 3,
':v4' => 4,
];
$this->assertEquals($expectedSql, $this->writer->write($union));
$this->assertEquals($expectedParams, $this->writer->getValues());
}
}

View File

@ -0,0 +1,77 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:34 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\MinusWriter;
use NilPortugues\Sql\QueryBuilder\Manipulation\Minus;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class MinusWriterTest.
*/
class MinusWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var MinusWriter
*/
private $minusWriter;
/**
* @var GenericBuilder
*/
private $writer;
/**
*
*/
public function setUp()
{
$this->minusWriter = new MinusWriter(new GenericBuilder());
$this->writer = new GenericBuilder();
}
public function tearDown()
{
$this->minusWriter = null;
$this->writer = null;
}
/**
* @test
*/
public function itShouldWriteMinus()
{
$minus = new Minus(new Select('user'), new Select('user_email'));
$expected = <<<SQL
SELECT user.* FROM user
MINUS
SELECT user_email.* FROM user_email
SQL;
$this->assertEquals($expected, $this->minusWriter->write($minus));
}
/**
* @test
*/
public function itShouldWriteUnionAllFromGenericBuilder()
{
$minus = $this->writer->minus(new Select('user'), new Select('user_email'));
$expected = <<<SQL
SELECT user.* FROM user
MINUS
SELECT user_email.* FROM user_email
SQL;
$this->assertEquals($expected, $this->writer->write($minus));
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 10:46 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\PlaceholderWriter;
/**
* Class PlaceholderWriterTest.
*/
class PlaceholderWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var PlaceholderWriter
*/
private $writer;
/**
*
*/
protected function setUp()
{
$this->writer = new PlaceholderWriter();
}
/**
* @test
*/
public function itShouldAddValueAndReturnPlaceholder()
{
$result = $this->writer->add(1);
$this->assertEquals(':v1', $result);
}
/**
* @test
*/
public function itShouldAddValueAndGetReturnsArrayHoldingPlaceholderData()
{
$this->writer->add(1);
$this->assertEquals(array(':v1' => 1), $this->writer->get());
}
/**
* @test
*/
public function itShouldTranslatePhpNullToSqlNullValue()
{
$this->writer->add('');
$this->writer->add(null);
$this->assertEquals(array(':v1' => 'NULL', ':v2' => 'NULL'), $this->writer->get());
}
/**
* @test
*/
public function itShouldTranslatePhpBoolToSqlBoolValue()
{
$this->writer->add(true);
$this->writer->add(false);
$this->assertEquals(array(':v1' => 1, ':v2' => 0), $this->writer->get());
}
}

View File

@ -0,0 +1,625 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 10:46 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
/**
* Class SelectWriterTest.
*/
class SelectWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var GenericBuilder
*/
private $writer;
/**
* @var Select
*/
private $query;
/**
* @var string
*/
private $exceptionClass = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
*
*/
protected function setUp()
{
$this->writer = new GenericBuilder();
$this->query = new Select();
}
/**
* @test
*/
public function itShouldBeCloneableWithoutKeepingReferences()
{
$query1 = new Select('user');
$query2 = clone $query1;
$query2->setTable('users');
$this->assertFalse($query1->getTable() == $query2->getTable());
}
/**
* @test
*/
public function itShouldBeConstructedWithConstructor()
{
$this->query = new Select('user');
$expected = 'SELECT user.* FROM user';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToWriteCommentInQuery()
{
$this->query = new Select('user');
$this->query->setComment('This is a comment');
$expected = <<<SQL
-- This is a comment
SELECT user.* FROM user
SQL;
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldThrowExceptionWhenGettingColumnsButNoTableIsSet()
{
$this->setExpectedException($this->exceptionClass);
$this->query = new Select();
$this->query->getColumns();
}
/**
* @test
*/
public function itShouldBeConstructedWithConstructorWithColumns()
{
$this->query = new Select('user', array('user_id', 'name'));
$expected = 'SELECT user.user_id, user.name FROM user';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldSelectAll()
{
$this->query->setTable('user');
$expected = 'SELECT user.* FROM user';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldSelectAllDistinct()
{
$this->query->setTable('user')->distinct();
$expected = 'SELECT DISTINCT user.* FROM user';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldSelectAllWithLimit1()
{
$this->query->setTable('user')->limit(1);
$expected = 'SELECT user.* FROM user LIMIT :v1, :v2';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 0);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldSelectAllWithLimit1Offset2()
{
$this->query->setTable('user')->limit(1, 2);
$expected = 'SELECT user.* FROM user LIMIT :v1, :v2';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldSelectAllGetFirst20()
{
$this->query->setTable('user')->limit(0, 20);
$expected = 'SELECT user.* FROM user LIMIT :v1, :v2';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 0, ':v2' => 20);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldAllowColumnAlias()
{
$this->query
->setTable('user')
->setColumns(
array(
'userId' => 'user_id', // Alias -> column name
'username' => 'name',
'email' => 'email',
)
);
$expected = 'SELECT user.user_id AS "userId", user.name AS "username", user.email AS "email" FROM user';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldAllowColumnOrder()
{
$this->query
->setTable('user')
->orderBy('user_id', OrderBy::ASC);
$expected = 'SELECT user.* FROM user ORDER BY user.user_id ASC';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldAllowColumnOrderUsingColumnAlias()
{
$tableName = 'user';
$this->query
->setTable($tableName)
->setColumns(
array(
'userId' => 'user_id', // Alias -> column name
'username' => 'name',
'email' => 'email',
)
)
->orderBy('user_id', OrderBy::ASC)
->orderBy('email', OrderBy::DESC);
$expected =
'SELECT user.user_id AS "userId", user.name AS "username", user.email AS "email" FROM '.
'user ORDER BY user.user_id ASC, user.email DESC';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoALeftJoin()
{
$this->query
->setTable('user')
->leftJoin('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'));
$expected = 'SELECT user.*, news.title, news.body, news.created_at, news.updated_at FROM user LEFT JOIN '.
'news ON (news.author_id = user.user_id)';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoARightJoin()
{
$this->query
->setTable('user')
->rightJoin('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'));
$expected = 'SELECT user.*, news.title, news.body, news.created_at, news.updated_at FROM user RIGHT JOIN '.
'news ON (news.author_id = user.user_id)';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoAInnerJoin()
{
$this->query
->setTable('user')
->innerJoin('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'));
$expected = 'SELECT user.*, news.title, news.body, news.created_at, news.updated_at FROM user INNER JOIN '.
'news ON (news.author_id = user.user_id)';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoACrossJoin()
{
$this->query
->setTable('user')
->crossJoin('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'));
$expected = 'SELECT user.*, news.title, news.body, news.created_at, news.updated_at FROM user CROSS JOIN '.
'news ON (news.author_id = user.user_id)';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoALeftJoinWithOrderByOnJoinedTable()
{
$this->query
->setTable('user')
->setColumns(
array(
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at',
)
)
->orderBy('user_id', OrderBy::DESC)
->leftJoin('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'))
->orderBy('created_at', OrderBy::DESC);
$expected = 'SELECT user.user_id AS "userId", user.name AS "username", user.email AS "email", user.created_at,'.
' news.title, news.body, news.created_at, news.updated_at FROM user LEFT JOIN news ON (news.author_id '.
'= user.user_id) ORDER BY user.user_id DESC, news.created_at DESC';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoAJoin()
{
$this->query
->setTable('user')
->join('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'));
$expected = 'SELECT user.*, news.title, news.body, news.created_at, news.updated_at FROM user JOIN '.
'news ON (news.author_id = user.user_id)';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoAJoinWithOrderByOnJoinedTable()
{
$this->query
->setTable('user')
->setColumns(
array(
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at',
)
)
->orderBy('user_id', OrderBy::DESC)
->join('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'))
->orderBy('created_at', OrderBy::DESC);
$expected = 'SELECT user.user_id AS "userId", user.name AS "username", user.email AS "email", user.created_at,'.
' news.title, news.body, news.created_at, news.updated_at FROM user JOIN news ON (news.author_id ='.
' user.user_id) ORDER BY user.user_id DESC, news.created_at DESC';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoAJoinWithCustomColumns()
{
$this->query
->setTable('user')
->setColumns(
array(
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at',
)
)
->orderBy('user_id', OrderBy::DESC)
->join('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'))
->orderBy('created_at', OrderBy::DESC)
->join('articles', new Column('news_id', 'article'), new Column('id', 'news'));
$expected = 'SELECT user.user_id AS "userId", user.name AS "username", user.email AS "email", user.created_at,'.
' news.title, news.body, news.created_at, news.updated_at FROM user JOIN news ON (news.author_id ='.
' user.user_id) JOIN articles ON (news.id = article.news_id) ORDER BY user.user_id DESC, news.created_at DESC';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToDoAnAddWithMultipleJoins()
{
$this->query->setTable('user');
for ($i = 1; $i <= 5; ++$i) {
//Select QueryInterface for "news" table
$select = new Select();
$select
->setTable('news'.$i)
->setColumns(array('title'.$i));
//Select query for user table, being joined with "newsX" select.
$this->query->addJoin($select, 'user_id', 'author_id'.$i);
}
$expected = 'SELECT user.*, news1.title1, news2.title2, news3.title3, news4.title4, news5.title5 '.
'FROM user JOIN news1 ON (news1.author_id1 = user.user_id) JOIN news2 ON (news2.author_id2 = user.user_id)'.
' JOIN news3 ON (news3.author_id3 = user.user_id) JOIN news4 ON (news4.author_id4 = user.user_id) '.
'JOIN news5 ON (news5.author_id5 = user.user_id)';
$this->assertSame($expected, $this->writer->write($this->query));
}
/**
* @test
*/
public function itShouldBeAbleToOn()
{
$this->query
->setTable('user')
->setColumns(
array(
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at',
)
)
->orderBy('user_id', OrderBy::DESC)
->join('news', 'user_id', 'author_id', array('title', 'body', 'created_at', 'updated_at'))
->orderBy('created_at', OrderBy::DESC)
->on()
->eq('author_id', 1);
$this->query->limit(1, 10);
$expected = 'SELECT user.user_id AS "userId", user.name AS "username", user.email AS "email", user.created_at,'.
' news.title, news.body, news.created_at, news.updated_at FROM user JOIN news ON '.
'(news.author_id = user.user_id) AND (news.author_id = :v1) ORDER BY '.
'user.user_id DESC, news.created_at DESC LIMIT :v2, :v3';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 1, ':v3' => 10);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToCountTotalRows()
{
$this->query
->setTable('user')
->count()
->groupBy(array('user_id', 'name'))
->having()
->equals('user_id', 1)
->equals('user_id', 2);
$expected = 'SELECT COUNT(*) FROM user GROUP BY user.user_id, user.name HAVING (user.user_id = :v1) AND (user.user_id = :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToCountTotalRowsSettingDefaultColumn()
{
$this->query
->setTable('user')
->count('user_id')
->groupBy(array('user_id', 'name'))
->having()
->equals('user_id', 1)
->equals('user_id', 2);
$expected = 'SELECT COUNT(user.user_id) FROM user GROUP BY user.user_id, user.name HAVING (user.user_id = :v1) AND (user.user_id = :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToCountTotalRowsSettingDefaultColumnWithAlias()
{
$this->query
->setTable('user')
->count('user_id', 'total_users')
->groupBy(array('user_id', 'name'))
->having()
->equals('user_id', 1)
->equals('user_id', 2);
$expected = 'SELECT COUNT(user.user_id) AS "total_users" FROM user GROUP BY user.user_id, user.name HAVING (user.user_id = :v1) AND (user.user_id = :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToGroupByOperator()
{
$this->query
->setTable('user')
->setColumns(
array(
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at',
)
)
->groupBy(array('user_id', 'name'))
->having()
->equals('user_id', 1)
->equals('user_id', 2);
$expected = 'SELECT user.user_id AS "userId", user.name AS "username", user.email AS "email", user.created_at'.
' FROM user GROUP BY user.user_id, user.name HAVING (user.user_id = :v1) AND (user.user_id = :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldThrowExceptionInvalidHavingConjunction()
{
$this->setExpectedException($this->exceptionClass);
$this->query
->setTable('user')
->setColumns(
array(
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at',
)
)
->groupBy(array('user_id', 'name'))
->having('AAAAAAAAAAAAAAAA');
}
/**
* @test
*/
public function itShouldBeAbleToSetHavingOperatorToOr()
{
$this->query
->setTable('user')
->setColumns(
array(
'userId' => 'user_id',
'username' => 'name',
'email' => 'email',
'created_at',
)
)
->groupBy(array('user_id', 'name'))
->having('OR')
->equals('user_id', 1)
->equals('user_id', 2);
$expected = 'SELECT user.user_id AS "userId", user.name AS "username", user.email AS "email", user.created_at'.
' FROM user GROUP BY user.user_id, user.name HAVING (user.user_id = :v1) OR (user.user_id = :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldAllowSelectQueryToActAsAColumn()
{
$table1 = new Select('Table1');
$table1
->where()
->equals('table1_id', 1);
$table2 = new Select('Table2');
$table2
->where()
->eq($table1, 2);
$expected = 'SELECT Table2.* FROM Table2 WHERE ((SELECT Table1.* FROM Table1 '.
'WHERE (Table1.table1_id = :v1)) = :v2)';
$this->assertSame($expected, $this->writer->write($table2));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldWriteJoin()
{
$this->query
->isJoin(true)
->setTable('user')
->on()
->equals('user_id', 1);
$expected = 'JOIN user ON (user.user_id = :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:34 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\UnionAllWriter;
use NilPortugues\Sql\QueryBuilder\Manipulation\UnionAll;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class UnionAllWriterTest.
*/
class UnionAllWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var UnionAllWriter
*/
private $unionAllWriter;
/**
* @var GenericBuilder
*/
private $writer;
/**
*
*/
public function setUp()
{
$this->unionAllWriter = new UnionAllWriter(new GenericBuilder());
$this->writer = new GenericBuilder();
}
public function tearDown()
{
$this->unionAllWriter = null;
$this->writer = null;
}
/**
* @test
*/
public function itShouldWriteUnionAll()
{
$union = new UnionAll();
$union->add(new Select('user'));
$union->add(new Select('user_email'));
$expected = <<<SQL
SELECT user.* FROM user
UNION ALL
SELECT user_email.* FROM user_email
SQL;
$this->assertEquals($expected, $this->unionAllWriter->write($union));
}
/**
* @test
*/
public function itShouldWriteUnionAllFromGenericBuilder()
{
$unionAll = $this->writer->unionAll();
$unionAll->add(new Select('user'));
$unionAll->add(new Select('user_email'));
$expected = <<<SQL
SELECT user.* FROM user
UNION ALL
SELECT user_email.* FROM user_email
SQL;
$this->assertEquals($expected, $this->writer->write($unionAll));
}
/**
* @test
*/
public function itShouldNotResetPlaceholders()
{
$select1 = (new Select('table1'))
->where()
->between('column', 1, 2)
->end();
$select2 = (new Select('table2'))
->where()
->between('column', 3, 4)
->end();
$union = (new UnionAll())
->add($select1)
->add($select2);
$expectedSql = <<<SQL
SELECT table1.* FROM table1 WHERE (table1.column BETWEEN :v1 AND :v2)
UNION ALL
SELECT table2.* FROM table2 WHERE (table2.column BETWEEN :v3 AND :v4)
SQL;
$expectedParams = [
':v1' => 1,
':v2' => 2,
':v3' => 3,
':v4' => 4,
];
$this->assertEquals($expectedSql, $this->writer->write($union));
$this->assertEquals($expectedParams, $this->writer->getValues());
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:34 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\UnionWriter;
use NilPortugues\Sql\QueryBuilder\Manipulation\Union;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class UnionWriterTest.
*/
class UnionWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var UnionWriter
*/
private $unionWriter;
/**
* @var GenericBuilder
*/
private $writer;
/**
*
*/
public function setUp()
{
$this->unionWriter = new UnionWriter(new GenericBuilder());
$this->writer = new GenericBuilder();
}
public function tearDown()
{
$this->unionWriter = null;
$this->writer = null;
}
/**
* @test
*/
public function itShouldWriteUnion()
{
$union = new Union();
$union->add(new Select('user'));
$union->add(new Select('user_email'));
$expected = <<<SQL
SELECT user.* FROM user
UNION
SELECT user_email.* FROM user_email
SQL;
$this->assertEquals($expected, $this->unionWriter->write($union));
}
/**
* @test
*/
public function itShouldWriteUnionFromGenericBuilder()
{
$unionAll = $this->writer->union();
$unionAll->add(new Select('user'));
$unionAll->add(new Select('user_email'));
$expected = <<<SQL
SELECT user.* FROM user
UNION
SELECT user_email.* FROM user_email
SQL;
$this->assertEquals($expected, $this->writer->write($unionAll));
}
/**
* @test
*/
public function itShouldNotResetPlaceholders()
{
$select1 = (new Select('table1'))
->where()
->between('column', 1, 2)
->end();
$select2 = (new Select('table2'))
->where()
->between('column', 3, 4)
->end();
$union = (new Union())
->add($select1)
->add($select2);
$expectedSql = <<<SQL
SELECT table1.* FROM table1 WHERE (table1.column BETWEEN :v1 AND :v2)
UNION
SELECT table2.* FROM table2 WHERE (table2.column BETWEEN :v3 AND :v4)
SQL;
$expectedParams = [
':v1' => 1,
':v2' => 2,
':v3' => 3,
':v4' => 4,
];
$this->assertEquals($expectedSql, $this->writer->write($union));
$this->assertEquals($expectedParams, $this->writer->getValues());
}
}

View File

@ -0,0 +1,140 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 10:47 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Update;
/**
* Class UpdateWriterTest.
*/
class UpdateWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var array
*/
private $valueArray = array();
/**
* @var GenericBuilder
*/
private $writer;
/**
* @var Update
*/
private $query;
/**
* @var string
*/
private $exceptionClass = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
*
*/
protected function setUp()
{
$this->writer = new GenericBuilder();
$this->query = new Update();
$this->valueArray = array(
'user_id' => 1,
'name' => 'Nil',
'contact' => 'contact@nilportugues.com',
);
}
/**
* @test
*/
public function itShouldThrowQueryException()
{
$this->setExpectedException($this->exceptionClass);
$this->query->setTable('user');
$this->writer->write($this->query);
}
/**
* @test
*/
public function itShouldWriteUpdateQuery()
{
$this->query
->setTable('user')
->setValues($this->valueArray);
$expected = 'UPDATE user SET user.user_id = :v1, user.name = :v2, user.contact = :v3';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 'Nil', ':v3' => 'contact@nilportugues.com');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToWriteCommentInQuery()
{
$this->query
->setTable('user')
->setValues($this->valueArray)
->setComment('This is a comment');
$expected = <<<SQL
-- This is a comment
UPDATE user SET user.user_id = :v1, user.name = :v2, user.contact = :v3
SQL;
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 'Nil', ':v3' => 'contact@nilportugues.com');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldWriteUpdateQueryWithWhereConstrain()
{
$this->query
->setTable('user')
->setValues($this->valueArray)
->where()
->equals('user_id', 1);
$expected = 'UPDATE user SET user.user_id = :v1, user.name = :v2, user.contact = :v3 WHERE (user.user_id = :v4)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 'Nil', ':v3' => 'contact@nilportugues.com', ':v4' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldWriteUpdateQueryWithWhereConstrainAndLimit1()
{
$this->query
->setTable('user')
->setValues($this->valueArray)
->where()
->equals('user_id', 1);
$this->query->limit(1);
$expected = 'UPDATE user SET user.user_id = :v1, user.name = :v2, user.contact = :v3 WHERE (user.user_id = :v4) LIMIT :v5';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 'Nil', ':v3' => 'contact@nilportugues.com', ':v4' => 1, ':v5' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
}

View File

@ -0,0 +1,561 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/13/14
* Time: 12:46 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class WhereWriterTest.
*/
class WhereWriterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var GenericBuilder
*/
private $writer;
/**
* @var Select
*/
private $query;
/**
*
*/
protected function setUp()
{
$this->writer = new GenericBuilder();
$this->query = new Select();
}
/**
* @test
*/
public function itShouldAllowWhereConditions()
{
$this->query
->setTable('user')
->where()
->equals('user_id', 1)
->like('name', '%N%');
$expected = 'SELECT user.* FROM user WHERE (user.user_id = :v1) AND (user.name LIKE :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => '%N%');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldAllowWhereOrConditions()
{
$this->query
->setTable('user')
->where('OR')
->equals('user_id', 1)
->like('name', '%N%');
$this->assertSame('OR', $this->query->getWhereOperator());
$expected = 'SELECT user.* FROM user WHERE (user.user_id = :v1) OR (user.name LIKE :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => '%N%');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementNotBeEqualTo()
{
$column = 'user_id';
$value = 1;
$this->query
->setTable('user')
->where()
->notEquals($column, $value);
$expected = 'SELECT user.* FROM user WHERE (user.user_id <> :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementBeGreaterThan()
{
$column = 'user_id';
$value = 1;
$this->query
->setTable('user')
->where()
->greaterThan($column, $value);
$expected = 'SELECT user.* FROM user WHERE (user.user_id > :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementBeGreaterThanOrEqual()
{
$column = 'user_id';
$value = 1;
$this->query
->setTable('user')
->where()
->greaterThanOrEqual($column, $value);
$expected = 'SELECT user.* FROM user WHERE (user.user_id >= :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementBeLessThan()
{
$column = 'user_id';
$value = 1;
$this->query
->setTable('user')
->where()
->lessThan($column, $value);
$expected = 'SELECT user.* FROM user WHERE (user.user_id < :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementBeLessThanOrEqual()
{
$column = 'user_id';
$value = 1;
$this->query
->setTable('user')
->where()
->lessThanOrEqual($column, $value);
$expected = 'SELECT user.* FROM user WHERE (user.user_id <= :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementBeLike()
{
$column = 'user_id';
$value = 1;
$this->query
->setTable('user')
->where()
->like($column, $value);
$expected = 'SELECT user.* FROM user WHERE (user.user_id LIKE :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementBeNotLike()
{
$column = 'user_id';
$value = 1;
$this->query
->setTable('user')
->where()
->notLike($column, $value);
$expected = 'SELECT user.* FROM user WHERE (user.user_id NOT LIKE :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementAccumulateInConditions()
{
$column = 'user_id';
$this->query
->setTable('user')
->where()
->in($column, array(1, 2, 3));
$expected = 'SELECT user.* FROM user WHERE (user.user_id IN (:v1, :v2, :v3))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2, ':v3' => 3);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementAccumulateNotInConditions()
{
$column = 'user_id';
$this->query
->setTable('user')
->where()
->notIn($column, array(1, 2, 3));
$expected = 'SELECT user.* FROM user WHERE (user.user_id NOT IN (:v1, :v2, :v3))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2, ':v3' => 3);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementWriteBetweenConditions()
{
$column = 'user_id';
$this->query
->setTable('user')
->where()
->between($column, 1, 2);
$expected = 'SELECT user.* FROM user WHERE (user.user_id BETWEEN :v1 AND :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementWriteNotBetweenConditions()
{
$column = 'user_id';
$this->query
->setTable('user')
->where()
->notBetween($column, 1, 2);
$expected = 'SELECT user.* FROM user WHERE (user.user_id NOT BETWEEN :v1 AND :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementSetNullValueCondition()
{
$column = 'user_id';
$this->query
->setTable('user')
->where()
->isNull($column);
$expected = 'SELECT user.* FROM user WHERE (user.user_id IS NULL)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array();
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementSetIsNotNullValueCondition()
{
$column = 'user_id';
$this->query
->setTable('user')
->where()
->isNotNull($column);
$expected = 'SELECT user.* FROM user WHERE (user.user_id IS NOT NULL)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array();
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementSetBitClauseValueCondition()
{
$column = 'user_id';
$this->query
->setTable('user')
->where()
->addBitClause($column, 1);
$expected = 'SELECT user.* FROM user WHERE (ISNULL(user.user_id, 0) = :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToLetWhereStatementSubconditions()
{
$column = 'user_id';
$this->query
->setTable('user')
->where()
->equals($column, 1)
->equals($column, 2)
->subWhere('OR')
->lessThan($column, 10)
->greaterThan($column, 100);
$expected = 'SELECT user.* FROM user WHERE (user.user_id = :v1) AND (user.user_id = :v2) '.
'AND ((user.user_id < :v3) OR (user.user_id > :v4))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => 2, ':v3' => 10, ':v4' => 100);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldAllowSelectWhereButNotWriteCondition()
{
$table1 = new Select('Table1');
$table1
->where();
$expected = 'SELECT Table1.* FROM Table1';
$this->assertSame($expected, $this->writer->write($table1));
}
/**
* @test
*/
public function itShouldAllowHavingConditions()
{
$this->query
->setTable('user')
->having()
->greaterThan('user_id', 1)
->like('name', '%N%');
$expected = 'SELECT user.* FROM user HAVING (user.user_id > :v1) AND (user.name LIKE :v2)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 1, ':v2' => '%N%');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToUseSelectStatementsInWhere()
{
$selectRole = new Select();
$selectRole
->setTable('role')
->setColumns(array('role_name'))
->limit(1)
->where()
->equals('role_id', 3);
$this->query
->setTable('user')
->setColumns(array('user_id', 'role_id'))
->where()
->equals('role_id', $selectRole);
$expected = 'SELECT user.user_id, user.role_id FROM user WHERE '.
'(user.role_id = (SELECT role.role_name FROM role WHERE (role.role_id = :v1) LIMIT :v2, :v3))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 3, ':v2' => 1, ':v3' => 0);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToSelectWithFullMatchSearchUsingMatchInNaturalMode()
{
$this->query
->setTable('user')
->setColumns(array('user_id', 'role_id'))
->where()
->match(array('username', 'email'), array('Nil'));
$expected = 'SELECT user.user_id, user.role_id FROM user '.
'WHERE (MATCH(user.username, user.email) AGAINST(:v1))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 'Nil');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToSelectWithFullMatchSearchUsingMatchInBooleanMode()
{
$this->query
->setTable('user')
->setColumns(array('user_id', 'role_id'))
->where()
->matchBoolean(array('username', 'email'), array('Nil'));
$expected = 'SELECT user.user_id, user.role_id FROM user '.
'WHERE (MATCH(user.username, user.email) AGAINST(:v1 IN BOOLEAN MODE))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 'Nil');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToSelectWithFullMatchSearchUsingMatchInQueryExpansionMode()
{
$this->query
->setTable('user')
->setColumns(array('user_id', 'role_id'))
->where()
->matchWithQueryExpansion(array('username', 'email'), array('Nil'));
$expected = 'SELECT user.user_id, user.role_id FROM user '.
'WHERE (MATCH(user.username, user.email) AGAINST(:v1 WITH QUERY EXPANSION))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 'Nil');
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToDoWhereExists()
{
$select = new Select('banned_user');
$select->where()->equals('user_id', 1);
$this->query
->setTable('user')
->setColumns(array('user_id', 'role_id'))
->where()
->exists($select)
->equals('user', 'Nil');
$expected = 'SELECT user.user_id, user.role_id FROM user WHERE (user.user = :v1) AND '.
'EXISTS (SELECT banned_user.* FROM banned_user WHERE (banned_user.user_id = :v2))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 'Nil', ':v2' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldBeAbleToDoWhereNotExists()
{
$select = new Select('banned_user');
$select->where()->equals('user_id', 1);
$this->query
->setTable('user')
->setColumns(array('user_id', 'role_id'))
->where()
->notExists($select)
->equals('user', 'Nil');
$expected = 'SELECT user.user_id, user.role_id FROM user WHERE (user.user = :v1) AND '.
'NOT EXISTS (SELECT banned_user.* FROM banned_user WHERE (banned_user.user_id = :v2))';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => 'Nil', ':v2' => 1);
$this->assertEquals($expected, $this->writer->getValues());
}
/**
* @test
*/
public function itShouldAllowWhereConditionAsLiteral()
{
$this->query
->setTable('user')
->where()
->asLiteral('(username is not null and status=:status)')
->notEquals('name', '%N%');
$expected = 'SELECT user.* FROM user WHERE (username is not null and status=:status) AND (user.name <> :v1)';
$this->assertSame($expected, $this->writer->write($this->query));
$expected = array(':v1' => '%N%');
$this->assertEquals($expected, $this->writer->getValues());
}
}

View File

@ -0,0 +1,150 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 10:47 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Builder\Syntax;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\PlaceholderWriter;
use NilPortugues\Sql\QueryBuilder\Builder\Syntax\WriterFactory;
/**
* Class WriterFactoryTest.
*/
class WriterFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @var PlaceholderWriter
*/
private $placeholder;
/**
* @var GenericBuilder
*/
private $writer;
/**
*
*/
public function setUp()
{
$this->writer = new GenericBuilder();
$this->placeholder = new PlaceholderWriter();
}
/**
* @test
*/
public function itShouldCreateColumnWriter()
{
$writer = WriterFactory::createColumnWriter($this->writer, $this->placeholder);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\ColumnWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateWhereWriter()
{
$writer = WriterFactory::createWhereWriter($this->writer, $this->placeholder);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\WhereWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateSelectWriter()
{
$writer = WriterFactory::createSelectWriter($this->writer, $this->placeholder);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\SelectWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateInsertWriter()
{
$writer = WriterFactory::createInsertWriter($this->writer, $this->placeholder);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\InsertWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateUpdateWriter()
{
$writer = WriterFactory::createUpdateWriter($this->writer, $this->placeholder);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\UpdateWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateDeleteWriter()
{
$writer = WriterFactory::createDeleteWriter($this->writer, $this->placeholder);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\DeleteWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreatePlaceholderWriter()
{
$writer = WriterFactory::createPlaceholderWriter();
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\PlaceholderWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateIntersectWriter()
{
$writer = WriterFactory::createIntersectWriter($this->writer);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\IntersectWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateMinusWriter()
{
$writer = WriterFactory::createMinusWriter($this->writer);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\MinusWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateUnion()
{
$writer = WriterFactory::createUnionWriter($this->writer);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\UnionWriter', \get_class($writer));
}
/**
* @test
*/
public function itShouldCreateUnionAll()
{
$writer = WriterFactory::createUnionAllWriter($this->writer);
$this->assertSame('NilPortugues\Sql\QueryBuilder\Builder\Syntax\UnionAllWriter', \get_class($writer));
}
}

View File

@ -0,0 +1,74 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/7/14
* Time: 11:44 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
/**
* Class BaseQueryTest.
*/
class BaseQueryTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \NilPortugues\Tests\Sql\QueryBuilder\Manipulation\Resources\DummyQuery
*/
private $query;
/**
* @var string
*/
private $whereClass = '\NilPortugues\Sql\QueryBuilder\Syntax\Where';
/**
*
*/
protected function setUp()
{
$this->query = new Resources\DummyQuery();
$this->query->setTable('tablename');
}
/**
*
*/
protected function tearDown()
{
$this->query = null;
}
/**
* @test
*/
public function itShouldBeAbleToSetTableName()
{
$this->assertSame('tablename', $this->query->getTable()->getName());
}
/**
* @test
*/
public function itShouldGetWhere()
{
$this->assertNull($this->query->getWhere());
$this->query->where();
$this->assertInstanceOf($this->whereClass, $this->query->getWhere());
}
/**
* @test
*/
public function itShouldGetWhereOperator()
{
$this->assertSame('AND', $this->query->getWhereOperator());
$this->query->where('OR');
$this->assertSame('OR', $this->query->getWhereOperator());
}
}

View File

@ -0,0 +1,56 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 1:37 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Builder\GenericBuilder;
use NilPortugues\Sql\QueryBuilder\Manipulation\Delete;
/**
* Class DeleteTest.
*/
class DeleteTest extends \PHPUnit_Framework_TestCase
{
/**
* @var GenericBuilder
*/
private $writer;
/**
* @var Delete
*/
private $query;
/**
*
*/
protected function setUp()
{
$this->query = new Delete();
}
/**
* @test
*/
public function itShouldGetPartName()
{
$this->assertSame('DELETE', $this->query->partName());
}
/**
* @test
*/
public function itShouldReturnLimit1()
{
$this->query->limit(1);
$this->assertSame(1, $this->query->getLimitStart());
}
}

View File

@ -0,0 +1,77 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 1:37 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Manipulation\Insert;
/**
* Class InsertTest.
*/
class InsertTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Insert
*/
private $query;
/**
*
*/
protected function setUp()
{
$this->query = new Insert();
}
/**
* @test
*/
public function itShouldGetPartName()
{
$this->assertSame('INSERT', $this->query->partName());
}
/**
* @test
*/
public function itShouldSetValues()
{
$values = ['user_id' => 1, 'username' => 'nilportugues'];
$this->query->setValues($values);
$this->assertSame($values, $this->query->getValues());
}
/**
* @test
*/
public function itShouldGetColumns()
{
$values = ['user_id' => 1, 'username' => 'nilportugues'];
$this->query->setValues($values);
$columns = $this->query->getColumns();
$this->assertInstanceOf('NilPortugues\Sql\QueryBuilder\Syntax\Column', $columns[0]);
}
/**
* @test
*/
public function itShouldSetNullableValues()
{
$values = ['user_id' => 1, 'description' => null, 'isVisible' => false];
$this->query->setValues($values);
$this->assertSame($values, $this->query->getValues());
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:26 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Manipulation\Intersect;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class IntersectTest.
*/
class IntersectTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Intersect
*/
private $query;
/**
* @var string
*/
private $exceptionClass = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
*
*/
protected function setUp()
{
$this->query = new Intersect();
}
/**
* @test
*/
public function itShouldGetPartName()
{
$this->assertSame('INTERSECT', $this->query->partName());
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedGetTable()
{
$this->setExpectedException($this->exceptionClass);
$this->query->getTable();
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedGetWhere()
{
$this->setExpectedException($this->exceptionClass);
$this->query->getWhere();
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedWhere()
{
$this->setExpectedException($this->exceptionClass);
$this->query->where();
}
/**
* @test
*/
public function itShouldGetIntersectSelects()
{
$this->assertEquals(array(), $this->query->getIntersects());
$select1 = new Select('user');
$select2 = new Select('user_email');
$this->query->add($select1);
$this->query->add($select2);
$this->assertEquals(array($select1, $select2), $this->query->getIntersects());
}
}

View File

@ -0,0 +1,82 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:26 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Manipulation\Minus;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class MinusTest.
*/
class MinusTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Minus
*/
private $query;
/**
* @var string
*/
private $exceptionClass = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
*
*/
protected function setUp()
{
$this->query = new Minus(new Select('user'), new Select('user_email'));
}
/**
* @test
*/
public function itShouldGetPartName()
{
$this->assertSame('MINUS', $this->query->partName());
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedGetTable()
{
$this->setExpectedException($this->exceptionClass);
$this->query->getTable();
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedGetWhere()
{
$this->setExpectedException($this->exceptionClass);
$this->query->getWhere();
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedWhere()
{
$this->setExpectedException($this->exceptionClass);
$this->query->where();
}
/**
* @test
*/
public function itShouldGetMinusSelects()
{
$this->assertEquals(new Select('user'), $this->query->getFirst());
$this->assertEquals(new Select('user_email'), $this->query->getSecond());
}
}

View File

@ -0,0 +1,98 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/16/14
* Time: 8:50 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Manipulation\QueryFactory;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class QueryFactoryTest.
*/
class QueryFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function itShouldCreateSelectObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Select';
$this->assertInstanceOf($className, QueryFactory::createSelect());
}
/**
* @test
*/
public function itShouldCreateInsertObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Insert';
$this->assertInstanceOf($className, QueryFactory::createInsert());
}
/**
* @test
*/
public function itShouldCreateUpdateObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Update';
$this->assertInstanceOf($className, QueryFactory::createUpdate());
}
/**
* @test
*/
public function itShouldCreateDeleteObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Delete';
$this->assertInstanceOf($className, QueryFactory::createDelete());
}
/**
* @test
*/
public function itShouldCreateMinusObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Minus';
$this->assertInstanceOf($className, QueryFactory::createMinus(new Select('table1'), new Select('table2')));
}
/**
* @test
*/
public function itShouldCreateUnionObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\Union';
$this->assertInstanceOf($className, QueryFactory::createUnion());
}
/**
* @test
*/
public function itShouldCreateUnionAllObject()
{
$className = '\NilPortugues\Sql\QueryBuilder\Manipulation\UnionAll';
$this->assertInstanceOf($className, QueryFactory::createUnionAll());
}
/**
* @test
*/
public function itShouldCreateWhereObject()
{
$mockClass = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryInterface';
$query = $this->getMockBuilder($mockClass)
->disableOriginalConstructor()
->getMock();
$className = '\NilPortugues\Sql\QueryBuilder\Syntax\Where';
$this->assertInstanceOf($className, QueryFactory::createWhere($query));
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:58 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation\Resources;
use NilPortugues\Sql\QueryBuilder\Manipulation\AbstractBaseQuery;
/**
* Class DummyQuery.
*/
class DummyQuery extends AbstractBaseQuery
{
/**
* @return string
*/
public function partName()
{
return 'DUMMY';
}
}

View File

@ -0,0 +1,101 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 1:36 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
/**
* Class SelectTest.
*/
class SelectTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Select
*/
private $query;
/**
*
*/
protected function setUp()
{
$this->query = new Select();
}
/**
* @test
*/
public function itShouldGetPartName()
{
$this->assertSame('SELECT', $this->query->partName());
}
/**
* @test
*/
public function itShouldSetParentOrderByAlso()
{
$columns = [
'id',
'phase_id',
'league_id',
'date',
];
$parentTable = 'events';
$this->query->setTable($parentTable);
$this->query->setColumns($columns);
$sorts = [
[
'field' => 'league_id',
'direction' => 1,
],
[
'field' => 'start_date',
'direction' => 0,
'table' => 'phases',
'joinBy' => 'phase_id',
'joinWith' => 'id',
],
[
'field' => 'date',
'direction' => 1,
],
];
if (is_array($sorts)) {
foreach ($sorts as $sort) {
$order = (int)$sort['direction'] > 0 ? OrderBy::ASC : OrderBy::DESC;
if (count($sort) == 5) {
$this->query->leftJoin(
$sort['table'],
$sort['joinBy'],
$sort['joinWith']
)->orderBy($sort['field'], $order);
} else {
$this->query->orderBy($sort['field'], $order);
}
}
}
$returnedOrders = $this->query->getAllOrderBy();
foreach ($returnedOrders as $id => $orderByObject) {
$column = $orderByObject->getColumn();
$table = $column->getTable();
$expectedColumn = $sorts[$id]['field'];
$expectedTable = array_key_exists('table', $sorts[$id]) ? $sorts[$id]['table'] : $parentTable;
$expectedDirection = (int)$sorts[$id]['direction'] > 0 ? OrderBy::ASC : OrderBy::DESC;
$this->assertSame($expectedColumn, $column->getName());
$this->assertSame($expectedTable, $table->getName());
$this->assertSame($expectedDirection, $orderByObject->getDirection());
}
$this->assertCount(count($sorts), $returnedOrders);
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:26 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Manipulation\UnionAll;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class UnionAllTest.
*/
class UnionAllTest extends \PHPUnit_Framework_TestCase
{
/**
* @var UnionAll
*/
private $query;
/**
* @var string
*/
private $exceptionClass = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
*
*/
protected function setUp()
{
$this->query = new UnionAll();
}
/**
* @test
*/
public function itShouldGetPartName()
{
$this->assertSame('UNION ALL', $this->query->partName());
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedGetTable()
{
$this->setExpectedException($this->exceptionClass);
$this->query->getTable();
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedGetWhere()
{
$this->setExpectedException($this->exceptionClass);
$this->query->getWhere();
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedWhere()
{
$this->setExpectedException($this->exceptionClass);
$this->query->where();
}
/**
* @test
*/
public function itShouldGetIntersectSelects()
{
$this->assertEquals(array(), $this->query->getUnions());
$select1 = new Select('user');
$select2 = new Select('user_email');
$this->query->add($select1);
$this->query->add($select2);
$this->assertEquals(array($select1, $select2), $this->query->getUnions());
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 9/12/14
* Time: 7:26 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Manipulation\Union;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
/**
* Class UnionTest.
*/
class UnionTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Union
*/
private $query;
/**
* @var string
*/
private $exceptionClass = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
*
*/
protected function setUp()
{
$this->query = new Union();
}
/**
* @test
*/
public function itShouldGetPartName()
{
$this->assertSame('UNION', $this->query->partName());
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedGetTable()
{
$this->setExpectedException($this->exceptionClass);
$this->query->getTable();
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedGetWhere()
{
$this->setExpectedException($this->exceptionClass);
$this->query->getWhere();
}
/**
* @test
*/
public function itShouldThrowExceptionForUnsupportedWhere()
{
$this->setExpectedException($this->exceptionClass);
$this->query->where();
}
/**
* @test
*/
public function itShouldGetIntersectSelects()
{
$this->assertEquals(array(), $this->query->getUnions());
$select1 = new Select('user');
$select2 = new Select('user_email');
$this->query->add($select1);
$this->query->add($select2);
$this->assertEquals(array($select1, $select2), $this->query->getUnions());
}
}

View File

@ -0,0 +1,73 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 1:37 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Manipulation;
use NilPortugues\Sql\QueryBuilder\Manipulation\Update;
/**
* Class UpdateTest.
*/
class UpdateTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Update
*/
private $query;
/**
*
*/
protected function setUp()
{
$this->query = new Update();
}
/**
* @test
*/
public function itShouldGetPartName()
{
$this->assertSame('UPDATE', $this->query->partName());
}
/**
* @test
*/
public function itShouldReturnLimit1()
{
$this->query->limit(1);
$this->assertSame(1, $this->query->getLimitStart());
}
/**
* @test
*/
public function itShouldReturnValues()
{
$values = ['user_id' => 1];
$this->query->setValues($values);
$this->assertSame($values, $this->query->getValues());
}
/**
* @test
*/
public function itShouldSetNullableValues()
{
$values = ['user_id' => 1, 'description' => null, 'isVisible' => false];
$this->query->setValues($values);
$this->assertSame($values, $this->query->getValues());
}
}

View File

@ -0,0 +1,96 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/2/14
* Time: 11:54 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Syntax;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\Table;
/**
* Class ColumnTest.
*/
class ColumnTest extends \PHPUnit_Framework_TestCase
{
/**
* @var string
*/
protected $tableClass = '\NilPortugues\Sql\QueryBuilder\Syntax\Table';
/**
* @var string
*/
protected $queryException = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
* @test
*/
public function itShouldReturnPartName()
{
$column = new Column('id', 'user');
$this->assertSame('COLUMN', $column->partName());
}
/**
* @test
*/
public function itShouldConstruct()
{
$column = new Column('id', 'user');
$this->assertEquals('id', $column->getName());
$this->assertInstanceOf($this->tableClass, $column->getTable());
$this->assertEquals('user', $column->getTable()->getName());
}
/**
* @test
*/
public function itShouldSetColumnName()
{
$column = new Column('id', 'user');
$column->setName('user_id');
$this->assertEquals('user_id', $column->getName());
}
/**
* @test
*/
public function itShouldSetTableName()
{
$tableName = 'user';
$column = new Column('id', $tableName);
$column->setTable(new Table($tableName));
$this->assertInstanceOf($this->tableClass, $column->getTable());
$this->assertEquals($tableName, $column->getTable()->getName());
}
/**
* @test
*/
public function itShouldSetAliasName()
{
$column = new Column('user_id', 'user', 'userId');
$this->assertEquals('userId', $column->getAlias());
}
/**
* @test
*/
public function itShouldThrowExceptionIfAliasOnAllSelection()
{
$this->setExpectedException($this->queryException);
new Column('*', 'user', 'userId');
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:07 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Syntax;
use NilPortugues\Sql\QueryBuilder\Syntax\Column;
use NilPortugues\Sql\QueryBuilder\Syntax\OrderBy;
/**
* Class OrderByTest.
*/
class OrderByTest extends \PHPUnit_Framework_TestCase
{
/**
* @var string
*/
protected $columnClass = '\NilPortugues\Sql\QueryBuilder\Syntax\Column';
/**
* @test
*/
public function itShouldConstructOrderBy()
{
$column = new Column('registration_date', 'user');
$order = new OrderBy($column, OrderBy::ASC);
$this->assertInstanceOf($this->columnClass, $order->getColumn());
$this->assertEquals(OrderBy::ASC, $order->getDirection());
}
/**
* @test
*/
public function itShouldGetOrderByDirection()
{
$column = new Column('registration_date', 'user');
$order = new OrderBy($column, OrderBy::ASC);
$this->assertEquals(OrderBy::ASC, $order->getDirection());
$order->setDirection(OrderBy::DESC);
$this->assertEquals(OrderBy::DESC, $order->getDirection());
}
/**
* @test
*/
public function itShouldThrowExceptionIfDirectionNotValid()
{
$column = new Column('registration_date', 'user');
$order = new OrderBy($column, OrderBy::ASC);
$this->setExpectedException('\InvalidArgumentException');
$order->setDirection('this is not a valid direction');
}
}

View File

@ -0,0 +1,97 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/2/14
* Time: 11:34 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Syntax;
use NilPortugues\Sql\QueryBuilder\Syntax\Table;
/**
* Class TableTest.
*/
class TableTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function testConstruct()
{
$table = new Table('user');
$this->assertEquals('user', $table->getName());
}
/**
* @test
*/
public function itShouldReturnNullIfTableNameHasNoAlias()
{
$table = new Table('user');
$this->assertNull($table->getAlias());
}
/**
* @test
*/
public function itShouldReturnAliasIfTableNameAliasHasBeenSet()
{
$table = new Table('user');
$table->setAlias('u');
$this->assertEquals('u', $table->getAlias());
}
/**
* @test
*/
public function itShouldReturnNullIfSchemaNotSet()
{
$table = new Table('user');
$this->assertNull($table->getSchema());
}
/**
* @test
*/
public function itShouldReturnSchemaIfSchemaHasValue()
{
$table = new Table('user', 'website');
$this->assertEquals('website', $table->getSchema());
}
/**
* @test
*/
public function itShouldReturnTheCompleteName()
{
$table = new Table('user');
$table->setAlias('p');
$table->setSchema('website');
$this->assertEquals('website.user AS p', $table->getCompleteName());
}
/**
* @test
*/
public function itShouldReturnFalseOnIsView()
{
$table = new Table('user_status');
$this->assertFalse($table->isView());
}
/**
* @test
*/
public function itShouldReturnTrueOnIsView()
{
$table = new Table('user_status');
$table->setView(true);
$this->assertTrue($table->isView());
}
}

View File

@ -0,0 +1,419 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/3/14
* Time: 12:31 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryBuilder\Syntax;
use NilPortugues\Sql\QueryBuilder\Manipulation\Select;
use NilPortugues\Sql\QueryBuilder\Syntax\Where;
use NilPortugues\Tests\Sql\QueryBuilder\Manipulation\Resources\DummyQuery;
/**
* Class WhereTest.
*/
class WhereTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Where
*/
protected $where;
/**
* @var string
*/
protected $whereClass = '\NilPortugues\Sql\QueryBuilder\Syntax\Where';
/**
* @var string
*/
protected $columnClass = '\NilPortugues\Sql\QueryBuilder\Syntax\Column';
/**
* @var string
*/
protected $queryException = '\NilPortugues\Sql\QueryBuilder\Manipulation\QueryException';
/**
*
*/
protected function setUp()
{
$query = new DummyQuery();
$query->setTable('users');
$this->where = new Where($query);
}
/**
* @test
*/
public function itShouldBeCloneable()
{
$this->assertEquals($this->where, clone $this->where);
}
/**
* @test
*/
public function itShouldBeEmptyOnConstruct()
{
$this->assertTrue($this->where->isEmpty());
}
/**
* @test
*/
public function itShouldReturnDefaultConjuctionAnd()
{
$this->assertSame('AND', $this->where->getConjunction());
}
/**
* @test
*/
public function itShouldReturnDefaultSubWhere()
{
$this->assertSame(array(), $this->where->getSubWheres());
}
/**
* @test
*/
public function itShouldReturnSubFilter()
{
$filter = $this->where->subWhere();
$this->assertSame(array(), $filter->getSubWheres());
$this->assertInstanceOf($this->whereClass, $filter);
}
/**
* @test
*/
public function itShouldReturnTheSameEqAndEqual()
{
$column = 'user_id';
$value = 1;
$this->assertSame(
$this->where->equals($column, $value),
$this->where->eq($column, $value)
);
}
/**
* @test
*/
public function itShouldNotBeEqualTo()
{
$column = 'user_id';
$value = 1;
$result = $this->where->notEquals($column, $value)->getComparisons();
$this->assertSame('<>', $result[0]['conjunction']);
$this->assertSame($column, $result[0]['subject']->getName());
$this->assertSame($value, $result[0]['target']);
}
/**
* @test
*/
public function itShouldBeGreaterThan()
{
$column = 'user_id';
$value = 1;
$result = $this->where->greaterThan($column, $value)->getComparisons();
$this->assertSame('>', $result[0]['conjunction']);
$this->assertSame($column, $result[0]['subject']->getName());
$this->assertSame($value, $result[0]['target']);
}
/**
* @test
*/
public function itShouldBeGreaterThanOrEqual()
{
$column = 'user_id';
$value = 1;
$result = $this->where->greaterThanOrEqual($column, $value)->getComparisons();
$this->assertSame('>=', $result[0]['conjunction']);
$this->assertSame($column, $result[0]['subject']->getName());
$this->assertSame($value, $result[0]['target']);
}
/**
* @test
*/
public function itShouldBeLessThan()
{
$column = 'user_id';
$value = 1;
$result = $this->where->lessThan($column, $value)->getComparisons();
$this->assertSame('<', $result[0]['conjunction']);
$this->assertSame($column, $result[0]['subject']->getName());
$this->assertSame($value, $result[0]['target']);
}
/**
* @test
*/
public function itShouldBeLessThanOrEqual()
{
$column = 'user_id';
$value = 1;
$result = $this->where->lessThanOrEqual($column, $value)->getComparisons();
$this->assertSame('<=', $result[0]['conjunction']);
$this->assertSame($column, $result[0]['subject']->getName());
$this->assertSame($value, $result[0]['target']);
}
/**
* @test
*/
public function itShouldBeLike()
{
$column = 'user_id';
$value = 1;
$result = $this->where->like($column, $value)->getComparisons();
$this->assertSame('LIKE', $result[0]['conjunction']);
$this->assertSame($column, $result[0]['subject']->getName());
$this->assertSame($value, $result[0]['target']);
}
/**
* @test
*/
public function itShouldBeNotLike()
{
$column = 'user_id';
$value = 1;
$result = $this->where->notLike($column, $value)->getComparisons();
$this->assertSame('NOT LIKE', $result[0]['conjunction']);
$this->assertSame($column, $result[0]['subject']->getName());
$this->assertSame($value, $result[0]['target']);
}
/**
* @test
*/
public function itShouldAccumulateMatchConditions()
{
$column = array('user_id');
$result = $this->where
->match($column, array(1, 2, 3))
->getMatches();
$expected = array(
0 => array(
'columns' => array('user_id'),
'values' => array(1, 2, 3),
'mode' => 'natural',
),
);
$this->assertEquals($expected, $result);
}
/**
* @test
*/
public function itShouldAccumulateMatchBooleanConditions()
{
$column = array('user_id');
$result = $this->where
->matchBoolean($column, array(1, 2, 3))
->getMatches();
$expected = array(
0 => array(
'columns' => array('user_id'),
'values' => array(1, 2, 3),
'mode' => 'boolean',
),
);
$this->assertEquals($expected, $result);
}
/**
* @test
*/
public function itShouldAccumulateMatchQueryExpansionConditions()
{
$column = array('user_id');
$result = $this->where
->matchWithQueryExpansion($column, array(1, 2, 3))
->getMatches();
$expected = array(
0 => array(
'columns' => array('user_id'),
'values' => array(1, 2, 3),
'mode' => 'query_expansion',
),
);
$this->assertEquals($expected, $result);
}
/**
* @test
*/
public function itShouldAccumulateInConditions()
{
$column = 'user_id';
$result = $this->where
->in($column, array(1, 2, 3))
->getIns();
$expected = array($column => array(1, 2, 3));
$this->assertEquals($expected, $result);
}
/**
* @test
*/
public function itShouldAccumulateNotInConditions()
{
$column = 'user_id';
$result = $this->where
->notIn($column, array(1, 2, 3))
->getNotIns();
$expected = array($column => array(1, 2, 3));
$this->assertEquals($expected, $result);
}
/**
* @test
*/
public function itShouldWriteBetweenConditions()
{
$column = 'user_id';
$result = $this->where
->between($column, 1, 2)
->getBetweens();
$this->assertInstanceOf($this->columnClass, $result[0]['subject']);
$this->assertEquals(1, $result[0]['a']);
$this->assertEquals(2, $result[0]['b']);
}
/**
* @test
*/
public function itShouldSetNullValueCondition()
{
$column = 'user_id';
$result = $this->where
->isNull($column)
->getNull();
$this->assertInstanceOf($this->columnClass, $result[0]['subject']);
}
/**
* @test
*/
public function itShouldSetIsNotNullValueCondition()
{
$column = 'user_id';
$result = $this->where
->isNotNull($column)
->getNotNull();
$this->assertInstanceOf($this->columnClass, $result[0]['subject']);
}
/**
* @test
*/
public function itShouldSetBitClauseValueCondition()
{
$column = 'user_id';
$result = $this->where
->addBitClause($column, 1)
->getBooleans();
$this->assertEquals(1, $result[0]['value']);
$this->assertInstanceOf($this->columnClass, $result[0]['subject']);
}
/**
* @test
*/
public function ItShouldChangeAndToOrOperator()
{
$result = $this->where->conjunction('OR');
$this->assertEquals('OR', $result->getConjunction());
}
/**
* @test
*/
public function itShouldThrowExceptionOnUnknownConjunction()
{
$this->setExpectedException($this->queryException);
$this->where->conjunction('NOT_VALID_CONJUNCTION');
}
/**
* @test
*/
public function itShouldSetExistsCondition()
{
$select1 = new Select('user');
$select1->where()->equals('user_id', 10);
$result = $this->where->exists($select1)->getExists();
$this->assertEquals(array($select1), $result);
}
/**
* @test
*/
public function itShouldSetNotExistsCondition()
{
$select1 = new Select('user');
$select1->where()->equals('user_id', 10);
$result = $this->where->notExists($select1)->getNotExists();
$this->assertEquals(array($select1), $result);
}
/**
* @test
*/
public function itShouldReturnLiterals()
{
$result = $this->where->asLiteral('(username is not null and status=:status)')->getComparisons();
$this->assertSame('(username is not null and status=:status)', $result[0]);
}
}

View File

@ -0,0 +1 @@
repo_token: IBpLC0WCtjsqFy5M7PSyMvz2yMpd81xLD

View File

@ -0,0 +1,5 @@
.idea/
bin/
build/
vendor/
composer.lock

View File

@ -0,0 +1,16 @@
language: php
php:
- "5.5"
- "5.6"
- "7.0"
- "hhvm"
before_script:
- composer install
script:
- bin/phpunit --coverage-text
matrix:
allow_failures:
- php: "hhvm"

View File

@ -0,0 +1,33 @@
Contributing
============
First of all, **thank you** for contributing, **you are awesome**!
Here are a few rules to follow in order to ease code reviews, and discussions before
maintainers accept and merge your work.
You MUST follow the [PSR-1](http://www.php-fig.org/psr/1/) and
[PSR-2](http://www.php-fig.org/psr/2/). If you don't know about any of them, you
should really read the recommendations. Can't wait? Use the [PHP-CS-Fixer
tool](http://cs.sensiolabs.org/).
You MUST run the test suite.
You MUST write (or update) unit tests.
You SHOULD write documentation.
Please, write [commit messages that make
sense](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
and [rebase your branch](http://git-scm.com/book/en/Git-Branching-Rebasing)
before submitting your Pull Request.
One may ask you to [squash your
commits](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)
too. This is used to "clean" your Pull Request before merging it (we don't want
commits such as `fix tests`, `fix 2`, `fix 3`, etc.).
Also, while creating your Pull Request on GitHub, you MUST write a description
which gives the context and/or explains why you are creating it.
Thank you!

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Nil Portugués Calderó
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,143 @@
SQL Query Formatter
=================
[![Build Status](https://travis-ci.org/nilportugues/sql-query-formatter.svg)](https://travis-ci.org/nilportugues/sql-query-formatter) [![Coverage Status](https://img.shields.io/coveralls/nilportugues/sql-query-formatter.svg)](https://coveralls.io/r/nilportugues/sql-query-formatter?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nilportugues/sql-query-formatter/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nilportugues/sql-query-formatter/?branch=master) [![SensioLabsInsight](https://insight.sensiolabs.com/projects/a57aa8f3-bbe1-43a5-941e-689d8435ab20/mini.png)](https://insight.sensiolabs.com/projects/a57aa8f3-bbe1-43a5-941e-689d8435ab20) [![Latest Stable Version](https://poser.pugx.org/nilportugues/sql-query-formatter/v/stable)](https://packagist.org/packages/nilportugues/sql-query-formatter) [![Total Downloads](https://poser.pugx.org/nilportugues/sql-query-formatter/downloads)](https://packagist.org/packages/nilportugues/sql-query-formatter) [![License](https://poser.pugx.org/nilportugues/sql-query-formatter/license)](https://packagist.org/packages/nilportugues/sql-query-formatter)
A very lightweight PHP class that re-formats unreadable or computer-generated SQL query statements to human-friendly readable text.
* [1.Installation](#block1)
* [2. Features](#block2)
* [3. Usage](#block3)
* [4. Code Quality](#block5)
* [5. Author](#block6)
* [6. Special Thanks](#block6)
* [7. License](#block7)
<a name="block1"></a>
## 1.Installation
The recommended way to install the SQL Query Formatter is through [Composer](http://getcomposer.org). Run the following command to install it:
```sh
php composer.phar require nilportugues/sql-query-formatter
```
<a name="block2"></a>
## 2. Features
**Human readable SQL formatting**
- Human readable plain text. No colours, no highlighting. Plain text is good enough in most cases.
**Data Binding Awareness**
- SQL Query Formatter takes data binding seriously.
- Placeholder syntax such as `:variable` or `?` is taken into account and is preserved when formatting.
<a name="block3"></a>
## 3. Usage
Sample code:
```php
<?php
use NilPortugues\Sql\QueryFormatter\Formatter;
$query = <<<SQL
SELECT user.user_id, user.username, (SELECT
role.role_name FROM role WHERE (role.role_id = :v1)
LIMIT :v2, :v3 ) AS user_role, (SELECT
role.role_name FROM role WHERE (role.role_id = :v4)
LIMIT :v5, :v6 ) AS role FROM user WHERE (user.user_id = :v7)
SQL;
$formatter = new Formatter();
echo $formatter->format($query);
```
Real output:
```sql
SELECT
user.user_id,
user.username,
(
SELECT
role.role_name
FROM
role
WHERE
(role.role_id = :v1)
LIMIT
:v2,
:v3
) AS user_role,
(
SELECT
role.role_name
FROM
role
WHERE
(role.role_id = :v4)
LIMIT
:v5,
:v6
) AS role
FROM
user
WHERE
(user.user_id = :v7)
```
<a name="block4"></a>
## 4. Fully tested
Testing has been done using PHPUnit and [Travis-CI](https://travis-ci.org). All code has been tested to be compatible from PHP 5.4 up to PHP 5.6 and [HHVM (nightly release)](http://hhvm.com/).
To run the test suite, you need [Composer](http://getcomposer.org):
```bash
php composer.phar install --dev
bin/phpunit
```
<a name="block5"></a>
## 5. Author
Nil Portugués Calderó
- <contact@nilportugues.com>
- [http://nilportugues.com](http://nilportugues.com)
<a name="block6"></a>
## 6. Special Thanks
I would like to thank the following people:
- [Jeremy Dorn](mailto:jeremy@jeremydorn.com) for his [sql-formatter](https://github.com/jdorn/sql-formatter) implementation I used as a basis for building this version.
<a name="block7"></a>
## 7. License
SQL Query Formatter is licensed under the MIT license.
```
Copyright (c) 2015 Nil Portugués Calderó
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
```

View File

@ -0,0 +1,42 @@
{
"name":"nilportugues/sql-query-formatter",
"description":"A very lightweight PHP class that reformats unreadable and computer-generated SQL query statements to human-friendly, readable text.",
"keywords": [ "sql", "mysql", "query", "formatter", "format", "parser", "tokenizer", "reformat", "sql server" ],
"type":"library",
"license":"MIT",
"homepage":"http://nilportugues.com",
"authors":
[
{
"name":"Nil Portugués Calderó",
"email":"contact@nilportugues.com",
"homepage":"http://nilportugues.com",
"role":"Lead Developer"
}
],
"autoload":{
"psr-4":{
"NilPortugues\\Sql\\QueryFormatter\\":"src/"
}
},
"autoload-dev":{
"psr-4":{
"NilPortugues\\Tests\\Sql\\QueryFormatter\\":"tests/"
}
},
"require":
{
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "4.*",
"fabpot/php-cs-fixer": "~1.9",
"nilportugues/php_backslasher": "~0.2"
},
"config":
{
"bin-dir": "bin"
},
"minimum-stability": "stable"
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit cacheTokens="false"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
syntaxCheck="true"
bootstrap="vendor/autoload.php"
verbose="true">
<php>
<ini name="intl.default_locale" value="en_US.UTF-8" />
<ini name="intl.error_level" value="0" />
<ini name="memory_limit" value="-1" />
<ini name="max_execution_time" value="-1"/>
<ini name="date.timezone" value="Europe/Madrid" />
<ini name="error_reporting" value="E_ALL" />
</php>
<testsuites>
<testsuite name="Test Suite">
<directory>./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./</directory>
<exclude>
<directory>./vendor/</directory>
<directory>./tests/</directory>
</exclude>
</whitelist>
</filter>
<logging>
<log type="junit" target="build/logs/junit.xml"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
<log type="coverage-html" target="build/coverage"/>
</logging>
</phpunit>

View File

@ -0,0 +1,350 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/26/14
* Time: 12:10 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter;
use NilPortugues\Sql\QueryFormatter\Helper\Comment;
use NilPortugues\Sql\QueryFormatter\Helper\Indent;
use NilPortugues\Sql\QueryFormatter\Helper\NewLine;
use NilPortugues\Sql\QueryFormatter\Helper\Parentheses;
use NilPortugues\Sql\QueryFormatter\Helper\Token;
use NilPortugues\Sql\QueryFormatter\Helper\WhiteSpace;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Lightweight Formatter heavily based on https://github.com/jdorn/sql-formatter.
*
* Class Formatter
*/
class Formatter
{
/**
* @var Tokenizer
*/
protected $tokenizer;
/**
* @var NewLine
*/
protected $newLine;
/**
* @var Parentheses
*/
protected $parentheses;
/**
* @var string
*/
protected $tab = ' ';
/**
* @var int
*/
protected $inlineCount = 0;
/**
* @var bool
*/
protected $clauseLimit = false;
/**
* @var string
*/
protected $formattedSql = '';
/**
* @var Indent
*/
protected $indentation;
/**
* @var Comment
*/
protected $comment;
/**
* Returns a SQL string in a readable human-friendly format.
*
* @param string $sql
*
* @return string
*/
public function format($sql)
{
$this->reset();
$tab = "\t";
$originalTokens = $this->tokenizer->tokenize((string) $sql);
$tokens = WhiteSpace::removeTokenWhitespace($originalTokens);
foreach ($tokens as $i => $token) {
$queryValue = $token[Tokenizer::TOKEN_VALUE];
$this->indentation->increaseSpecialIndent()->increaseBlockIndent();
$addedNewline = $this->newLine->addNewLineBreak($tab);
if ($this->comment->stringHasCommentToken($token)) {
$this->formattedSql = $this->comment->writeCommentBlock($token, $tab, $queryValue);
continue;
}
if ($this->parentheses->getInlineParentheses()) {
if ($this->parentheses->stringIsClosingParentheses($token)) {
$this->parentheses->writeInlineParenthesesBlock($tab, $queryValue);
continue;
}
$this->newLine->writeNewLineForLongCommaInlineValues($token);
$this->inlineCount += \strlen($token[Tokenizer::TOKEN_VALUE]);
}
switch ($token) {
case $this->parentheses->stringIsOpeningParentheses($token):
$tokens = $this->formatOpeningParenthesis($token, $i, $tokens, $originalTokens);
break;
case $this->parentheses->stringIsClosingParentheses($token):
$this->indentation->decreaseIndentLevelUntilIndentTypeIsSpecial($this);
$this->newLine->addNewLineBeforeToken($addedNewline, $tab);
break;
case $this->stringIsEndOfLimitClause($token):
$this->clauseLimit = false;
break;
case $token[Tokenizer::TOKEN_VALUE] === ',' && false === $this->parentheses->getInlineParentheses():
$this->newLine->writeNewLineBecauseOfComma();
break;
case Token::isTokenTypeReservedTopLevel($token):
$queryValue = $this->formatTokenTypeReservedTopLevel($addedNewline, $tab, $token, $queryValue);
break;
case $this->newLine->isTokenTypeReservedNewLine($token):
$this->newLine->addNewLineBeforeToken($addedNewline, $tab);
if (WhiteSpace::tokenHasExtraWhiteSpaces($token)) {
$queryValue = \preg_replace('/\s+/', ' ', $queryValue);
}
break;
}
$this->formatBoundaryCharacterToken($token, $i, $tokens, $originalTokens);
$this->formatWhiteSpaceToken($token, $queryValue);
$this->formatDashToken($token, $i, $tokens);
}
return \trim(\str_replace(["\t", " \n"], [$this->tab, "\n"], $this->formattedSql))."\n";
}
/**
*
*/
public function reset()
{
$this->tokenizer = new Tokenizer();
$this->indentation = new Indent();
$this->parentheses = new Parentheses($this, $this->indentation);
$this->newLine = new NewLine($this, $this->indentation, $this->parentheses);
$this->comment = new Comment($this, $this->indentation, $this->newLine);
$this->formattedSql = '';
}
/**
* @param $token
* @param $i
* @param array $tokens
* @param array $originalTokens
*
* @return array
*/
protected function formatOpeningParenthesis($token, $i, array &$tokens, array &$originalTokens)
{
$length = 0;
for ($j = 1; $j <= 250; ++$j) {
if (isset($tokens[$i + $j])) {
$next = $tokens[$i + $j];
if ($this->parentheses->stringIsClosingParentheses($next)) {
$this->parentheses->writeNewInlineParentheses();
break;
}
if ($this->parentheses->invalidParenthesesTokenValue($next)
|| $this->parentheses->invalidParenthesesTokenType($next)
) {
break;
}
$length += \strlen($next[Tokenizer::TOKEN_VALUE]);
}
}
$this->newLine->writeNewLineForLongInlineValues($length);
if (WhiteSpace::isPrecedingCurrentTokenOfTokenTypeWhiteSpace($originalTokens, $token)) {
$this->formattedSql = \rtrim($this->formattedSql, ' ');
}
$this->newLine->addNewLineAfterOpeningParentheses();
return $tokens;
}
/**
* @param $token
*
* @return bool
*/
protected function stringIsEndOfLimitClause($token)
{
return $this->clauseLimit
&& $token[Tokenizer::TOKEN_VALUE] !== ','
&& $token[Tokenizer::TOKEN_TYPE] !== Tokenizer::TOKEN_TYPE_NUMBER
&& $token[Tokenizer::TOKEN_TYPE] !== Tokenizer::TOKEN_TYPE_WHITESPACE;
}
/**
* @param bool $addedNewline
* @param string $tab
* @param $token
* @param $queryValue
*
* @return mixed
*/
protected function formatTokenTypeReservedTopLevel($addedNewline, $tab, $token, $queryValue)
{
$this->indentation
->setIncreaseSpecialIndent(true)
->decreaseSpecialIndentIfCurrentIndentTypeIsSpecial();
$this->newLine->writeNewLineBecauseOfTopLevelReservedWord($addedNewline, $tab);
if (WhiteSpace::tokenHasExtraWhiteSpaces($token)) {
$queryValue = \preg_replace('/\s+/', ' ', $queryValue);
}
Token::tokenHasLimitClause($token, $this->parentheses, $this);
return $queryValue;
}
/**
* @param $token
* @param $i
* @param array $tokens
* @param array $originalTokens
*/
protected function formatBoundaryCharacterToken($token, $i, array &$tokens, array &$originalTokens)
{
if (Token::tokenHasMultipleBoundaryCharactersTogether($token, $tokens, $i, $originalTokens)) {
$this->formattedSql = \rtrim($this->formattedSql, ' ');
}
}
/**
* @param $token
* @param $queryValue
*/
protected function formatWhiteSpaceToken($token, $queryValue)
{
if (WhiteSpace::tokenHasExtraWhiteSpaceLeft($token)) {
$this->formattedSql = \rtrim($this->formattedSql, ' ');
}
$this->formattedSql .= $queryValue.' ';
if (WhiteSpace::tokenHasExtraWhiteSpaceRight($token)) {
$this->formattedSql = \rtrim($this->formattedSql, ' ');
}
}
/**
* @param $token
* @param $i
* @param array $tokens
*/
protected function formatDashToken($token, $i, array &$tokens)
{
if (Token::tokenIsMinusSign($token, $tokens, $i)) {
$previousTokenType = $tokens[$i - 1][Tokenizer::TOKEN_TYPE];
if (WhiteSpace::tokenIsNumberAndHasExtraWhiteSpaceRight($previousTokenType)) {
$this->formattedSql = \rtrim($this->formattedSql, ' ');
}
}
}
/**
* @return string
*/
public function getFormattedSql()
{
return $this->formattedSql;
}
/**
* @param string $formattedSql
*
* @return $this
*/
public function setFormattedSql($formattedSql)
{
$this->formattedSql = $formattedSql;
return $this;
}
/**
* @param $string
*
* @return $this
*/
public function appendToFormattedSql($string)
{
$this->formattedSql .= $string;
return $this;
}
/**
* @return int
*/
public function getInlineCount()
{
return $this->inlineCount;
}
/**
* @param int $inlineCount
*
* @return $this
*/
public function setInlineCount($inlineCount)
{
$this->inlineCount = $inlineCount;
return $this;
}
/**
* @return bool
*/
public function getClauseLimit()
{
return $this->clauseLimit;
}
/**
* @param bool $clauseLimit
*
* @return $this
*/
public function setClauseLimit($clauseLimit)
{
$this->clauseLimit = $clauseLimit;
return $this;
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/22/14
* Time: 10:09 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Helper;
use NilPortugues\Sql\QueryFormatter\Formatter;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class Comment.
*/
class Comment
{
/**
* @var \NilPortugues\Sql\QueryFormatter\Formatter
*/
protected $formatter;
/**
* @var Indent
*/
protected $indentation;
/**
* @var NewLine
*/
protected $newLine;
/**
* @param Formatter $formatter
* @param Indent $indentation
* @param NewLine $newLine
*/
public function __construct(Formatter $formatter, Indent $indentation, NewLine $newLine)
{
$this->formatter = $formatter;
$this->indentation = $indentation;
$this->newLine = $newLine;
}
/**
* @param $token
*
* @return bool
*/
public function stringHasCommentToken($token)
{
return $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_COMMENT
|| $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_BLOCK_COMMENT;
}
/**
* @param $token
* @param string $tab
* @param $queryValue
*
* @return string
*/
public function writeCommentBlock($token, $tab, $queryValue)
{
if ($token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_BLOCK_COMMENT) {
$indent = \str_repeat($tab, $this->indentation->getIndentLvl());
$this->formatter->appendToFormattedSql("\n".$indent);
$queryValue = \str_replace("\n", "\n".$indent, $queryValue);
}
$this->formatter->appendToFormattedSql($queryValue);
$this->newLine->setNewline(true);
return $this->formatter->getFormattedSql();
}
}

View File

@ -0,0 +1,213 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/22/14
* Time: 11:37 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Helper;
use NilPortugues\Sql\QueryFormatter\Formatter;
/**
* Class Indent.
*/
class Indent
{
/**
* @var bool
*/
protected $inlineIndented = false;
/**
* @var bool
*/
protected $increaseSpecialIndent = false;
/**
* @var int
*/
protected $indentLvl = 0;
/**
* @var bool
*/
protected $increaseBlockIndent = false;
/**
* @var array
*/
protected $indentTypes = [];
/**
* Increase the Special Indent if increaseSpecialIndent is true after the current iteration.
*
* @return $this
*/
public function increaseSpecialIndent()
{
if ($this->increaseSpecialIndent) {
++$this->indentLvl;
$this->increaseSpecialIndent = false;
\array_unshift($this->indentTypes, 'special');
}
return $this;
}
/**
* Increase the Block Indent if increaseBlockIndent is true after the current iteration.
*
* @return $this
*/
public function increaseBlockIndent()
{
if ($this->increaseBlockIndent) {
++$this->indentLvl;
$this->increaseBlockIndent = false;
\array_unshift($this->indentTypes, 'block');
}
return $this;
}
/**
* Closing parentheses decrease the block indent level.
*
* @param Formatter $formatter
*
* @return $this
*/
public function decreaseIndentLevelUntilIndentTypeIsSpecial(Formatter $formatter)
{
$formatter->setFormattedSql(\rtrim($formatter->getFormattedSql(), ' '));
--$this->indentLvl;
while ($j = \array_shift($this->indentTypes)) {
if ('special' !== $j) {
break;
}
--$this->indentLvl;
}
return $this;
}
/**
* @return $this
*/
public function decreaseSpecialIndentIfCurrentIndentTypeIsSpecial()
{
\reset($this->indentTypes);
if (\current($this->indentTypes) === 'special') {
--$this->indentLvl;
\array_shift($this->indentTypes);
}
return $this;
}
/**
* @return bool
*/
public function getIncreaseBlockIndent()
{
return $this->increaseBlockIndent;
}
/**
* @return bool
*/
public function getIncreaseSpecialIndent()
{
return $this->increaseSpecialIndent;
}
/**
* @return int
*/
public function getIndentLvl()
{
return $this->indentLvl;
}
/**
* @return mixed
*/
public function getIndentTypes()
{
return $this->indentTypes;
}
/**
* @param bool $increaseBlockIndent
*
* @return $this
*/
public function setIncreaseBlockIndent($increaseBlockIndent)
{
$this->increaseBlockIndent = $increaseBlockIndent;
return $this;
}
/**
* @param bool $increaseSpecialIndent
*
* @return $this
*/
public function setIncreaseSpecialIndent($increaseSpecialIndent)
{
$this->increaseSpecialIndent = $increaseSpecialIndent;
return $this;
}
/**
* @param int $indentLvl
*
* @return $this
*/
public function setIndentLvl($indentLvl)
{
$this->indentLvl = $indentLvl;
return $this;
}
/**
* @param array $indentTypes
*
* @return $this
*/
public function setIndentTypes($indentTypes)
{
$this->indentTypes = $indentTypes;
return $this;
}
/**
* @param bool $inlineIndented
*
* @return $this
*/
public function setInlineIndented($inlineIndented)
{
$this->inlineIndented = $inlineIndented;
return $this;
}
/**
* @return bool
*/
public function getInlineIndented()
{
return $this->inlineIndented;
}
}

View File

@ -0,0 +1,183 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/22/14
* Time: 11:37 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Helper;
use NilPortugues\Sql\QueryFormatter\Formatter;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class NewLine.
*/
class NewLine
{
/**
* @var bool
*/
protected $newline = false;
/**
* @var \NilPortugues\Sql\QueryFormatter\Formatter
*/
protected $formatter;
/**
* @var Indent
*/
protected $indentation;
/**
* @var Parentheses
*/
protected $parentheses;
/**
* @param Formatter $formatter
* @param Indent $indentation
* @param Parentheses $parentheses
*/
public function __construct(Formatter $formatter, Indent $indentation, Parentheses $parentheses)
{
$this->formatter = $formatter;
$this->indentation = $indentation;
$this->parentheses = $parentheses;
}
/**
* Adds a new line break if needed.
*
* @param string $tab
*
* @return bool
*/
public function addNewLineBreak($tab)
{
$addedNewline = false;
if (true === $this->newline) {
$this->formatter->appendToFormattedSql("\n".str_repeat($tab, $this->indentation->getIndentLvl()));
$this->newline = false;
$addedNewline = true;
}
return $addedNewline;
}
/**
* @param $token
*/
public function writeNewLineForLongCommaInlineValues($token)
{
if (',' === $token[Tokenizer::TOKEN_VALUE]) {
if ($this->formatter->getInlineCount() >= 30) {
$this->formatter->setInlineCount(0);
$this->newline = true;
}
}
}
/**
* @param int $length
*/
public function writeNewLineForLongInlineValues($length)
{
if ($this->parentheses->getInlineParentheses() && $length > 30) {
$this->indentation->setIncreaseBlockIndent(true);
$this->indentation->setInlineIndented(true);
$this->newline = true;
}
}
/**
* Adds a new line break for an opening parentheses for a non-inline expression.
*/
public function addNewLineAfterOpeningParentheses()
{
if (false === $this->parentheses->getInlineParentheses()) {
$this->indentation->setIncreaseBlockIndent(true);
$this->newline = true;
}
}
/**
* @param bool $addedNewline
* @param string $tab
*/
public function addNewLineBeforeToken($addedNewline, $tab)
{
if (false === $addedNewline) {
$this->formatter->appendToFormattedSql(
"\n".str_repeat($tab, $this->indentation->getIndentLvl())
);
}
}
/**
* Add a newline before the top level reserved word if necessary and indent.
*
* @param bool $addedNewline
* @param string $tab
*/
public function writeNewLineBecauseOfTopLevelReservedWord($addedNewline, $tab)
{
if (false === $addedNewline) {
$this->formatter->appendToFormattedSql("\n");
} else {
$this->formatter->setFormattedSql(\rtrim($this->formatter->getFormattedSql(), $tab));
}
$this->formatter->appendToFormattedSql(\str_repeat($tab, $this->indentation->getIndentLvl()));
$this->newline = true;
}
/**
* Commas start a new line unless they are found within inline parentheses or SQL 'LIMIT' clause.
* If the previous TOKEN_VALUE is 'LIMIT', undo new line.
*/
public function writeNewLineBecauseOfComma()
{
$this->newline = true;
if (true === $this->formatter->getClauseLimit()) {
$this->newline = false;
$this->formatter->setClauseLimit(false);
}
}
/**
* @param $token
*
* @return bool
*/
public function isTokenTypeReservedNewLine($token)
{
return $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_RESERVED_NEWLINE;
}
/**
* @return bool
*/
public function getNewline()
{
return $this->newline;
}
/**
* @param bool $newline
*
* @return $this
*/
public function setNewline($newline)
{
$this->newline = $newline;
return $this;
}
}

View File

@ -0,0 +1,139 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/22/14
* Time: 11:37 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Helper;
use NilPortugues\Sql\QueryFormatter\Formatter;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class Parentheses.
*/
class Parentheses
{
/**
* @var bool
*/
protected $inlineParentheses = false;
/**
* @var \NilPortugues\Sql\QueryFormatter\Formatter
*/
protected $formatter;
/**
* @var Indent
*/
protected $indentation;
/**
* @param Formatter $formatter
* @param Indent $indentation
*/
public function __construct(Formatter $formatter, Indent $indentation)
{
$this->formatter = $formatter;
$this->indentation = $indentation;
}
/**
* @return bool
*/
public function getInlineParentheses()
{
return $this->inlineParentheses;
}
/**
* @param bool $inlineParentheses
*
* @return $this
*/
public function setInlineParentheses($inlineParentheses)
{
$this->inlineParentheses = $inlineParentheses;
return $this;
}
/**
* @param $token
*
* @return bool
*/
public function stringIsOpeningParentheses($token)
{
return $token[Tokenizer::TOKEN_VALUE] === '(';
}
/**
*
*/
public function writeNewInlineParentheses()
{
$this->inlineParentheses = true;
$this->formatter->setInlineCount(0);
$this->indentation->setInlineIndented(false);
}
/**
* @param $token
*
* @return bool
*/
public function invalidParenthesesTokenValue($token)
{
return $token[Tokenizer::TOKEN_VALUE] === ';'
|| $token[Tokenizer::TOKEN_VALUE] === '(';
}
/**
* @param $token
*
* @return bool
*/
public function invalidParenthesesTokenType($token)
{
return $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_RESERVED_TOP_LEVEL
|| $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_RESERVED_NEWLINE
|| $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_COMMENT
|| $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_BLOCK_COMMENT;
}
/**
* @param $token
*
* @return bool
*/
public function stringIsClosingParentheses($token)
{
return $token[Tokenizer::TOKEN_VALUE] === ')';
}
/**
* @param string $tab
* @param $queryValue
*/
public function writeInlineParenthesesBlock($tab, $queryValue)
{
$this->formatter->setFormattedSql(\rtrim($this->formatter->getFormattedSql(), ' '));
if ($this->indentation->getInlineIndented()) {
$indentTypes = $this->indentation->getIndentTypes();
\array_shift($indentTypes);
$this->indentation->setIndentTypes($indentTypes);
$this->indentation->setIndentLvl($this->indentation->getIndentLvl() - 1);
$this->formatter->appendToFormattedSql("\n".str_repeat($tab, $this->indentation->getIndentLvl()));
}
$this->inlineParentheses = false;
$this->formatter->appendToFormattedSql($queryValue.' ');
}
}

View File

@ -0,0 +1,749 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/22/14
* Time: 11:38 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Helper;
use NilPortugues\Sql\QueryFormatter\Formatter;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class Token.
*/
final class Token
{
/**
* @var array
*/
public static $reserved = [
'ACCESSIBLE',
'ACTION',
'AGAINST',
'AGGREGATE',
'ALGORITHM',
'ALL',
'ALTER',
'ANALYSE',
'ANALYZE',
'AS',
'ASC',
'AUTOCOMMIT',
'AUTO_INCREMENT',
'BACKUP',
'BEGIN',
'BETWEEN',
'BINLOG',
'BOTH',
'CASCADE',
'CASE',
'CHANGE',
'CHANGED',
'CHARACTER SET',
'CHARSET',
'CHECK',
'CHECKSUM',
'COLLATE',
'COLLATION',
'COLUMN',
'COLUMNS',
'COMMENT',
'COMMIT',
'COMMITTED',
'COMPRESSED',
'CONCURRENT',
'CONSTRAINT',
'CONTAINS',
'CONVERT',
'CREATE',
'CROSS',
'CURRENT_TIMESTAMP',
'DATABASE',
'DATABASES',
'DAY',
'DAY_HOUR',
'DAY_MINUTE',
'DAY_SECOND',
'DEFAULT',
'DEFINER',
'DELAYED',
'DELETE',
'DESC',
'DESCRIBE',
'DETERMINISTIC',
'DISTINCT',
'DISTINCTROW',
'DIV',
'DO',
'DUMPFILE',
'DUPLICATE',
'DYNAMIC',
'ELSE',
'ENCLOSED',
'END',
'ENGINE',
'ENGINE_TYPE',
'ENGINES',
'ESCAPE',
'ESCAPED',
'EVENTS',
'EXEC',
'EXECUTE',
'EXISTS',
'EXPLAIN',
'EXTENDED',
'FAST',
'FIELDS',
'FILE',
'FIRST',
'FIXED',
'FLUSH',
'FOR',
'FORCE',
'FOREIGN',
'FULL',
'FULLTEXT',
'FUNCTION',
'GLOBAL',
'GRANT',
'GRANTS',
'GROUP_CONCAT',
'HEAP',
'HIGH_PRIORITY',
'HOSTS',
'HOUR',
'HOUR_MINUTE',
'HOUR_SECOND',
'IDENTIFIED',
'IF',
'IFNULL',
'IGNORE',
'IN',
'INDEX',
'INDEXES',
'INFILE',
'INSERT',
'INSERT_ID',
'INSERT_METHOD',
'INTERVAL',
'INTO',
'INVOKER',
'IS',
'ISOLATION',
'KEY',
'KEYS',
'KILL',
'LAST_INSERT_ID',
'LEADING',
'LEVEL',
'LIKE',
'LINEAR',
'LINES',
'LOAD',
'LOCAL',
'LOCK',
'LOCKS',
'LOGS',
'LOW_PRIORITY',
'MARIA',
'MASTER',
'MASTER_CONNECT_RETRY',
'MASTER_HOST',
'MASTER_LOG_FILE',
'MATCH',
'MAX_CONNECTIONS_PER_HOUR',
'MAX_QUERIES_PER_HOUR',
'MAX_ROWS',
'MAX_UPDATES_PER_HOUR',
'MAX_USER_CONNECTIONS',
'MEDIUM',
'MERGE',
'MINUTE',
'MINUTE_SECOND',
'MIN_ROWS',
'MODE',
'MODIFY',
'MONTH',
'MRG_MYISAM',
'MYISAM',
'NAMES',
'NATURAL',
'NOT',
'NOW()',
'NULL',
'OFFSET',
'ON',
'OPEN',
'OPTIMIZE',
'OPTION',
'OPTIONALLY',
'ON UPDATE',
'ON DELETE',
'OUTFILE',
'PACK_KEYS',
'PAGE',
'PARTIAL',
'PARTITION',
'PARTITIONS',
'PASSWORD',
'PRIMARY',
'PRIVILEGES',
'PROCEDURE',
'PROCESS',
'PROCESSLIST',
'PURGE',
'QUICK',
'RANGE',
'RAID0',
'RAID_CHUNKS',
'RAID_CHUNKSIZE',
'RAID_TYPE',
'READ',
'READ_ONLY',
'READ_WRITE',
'REFERENCES',
'REGEXP',
'RELOAD',
'RENAME',
'REPAIR',
'REPEATABLE',
'REPLACE',
'REPLICATION',
'RESET',
'RESTORE',
'RESTRICT',
'RETURN',
'RETURNS',
'REVOKE',
'RLIKE',
'ROLLBACK',
'ROW',
'ROWS',
'ROW_FORMAT',
'SECOND',
'SECURITY',
'SEPARATOR',
'SERIALIZABLE',
'SESSION',
'SHARE',
'SHOW',
'SHUTDOWN',
'SLAVE',
'SONAME',
'SOUNDS',
'SQL',
'SQL_AUTO_IS_NULL',
'SQL_BIG_RESULT',
'SQL_BIG_SELECTS',
'SQL_BIG_TABLES',
'SQL_BUFFER_RESULT',
'SQL_CALC_FOUND_ROWS',
'SQL_LOG_BIN',
'SQL_LOG_OFF',
'SQL_LOG_UPDATE',
'SQL_LOW_PRIORITY_UPDATES',
'SQL_MAX_JOIN_SIZE',
'SQL_QUOTE_SHOW_CREATE',
'SQL_SAFE_UPDATES',
'SQL_SELECT_LIMIT',
'SQL_SLAVE_SKIP_COUNTER',
'SQL_SMALL_RESULT',
'SQL_WARNINGS',
'SQL_CACHE',
'SQL_NO_CACHE',
'START',
'STARTING',
'STATUS',
'STOP',
'STORAGE',
'STRAIGHT_JOIN',
'STRING',
'STRIPED',
'SUPER',
'TABLE',
'TABLES',
'TEMPORARY',
'TERMINATED',
'THEN',
'TO',
'TRAILING',
'TRANSACTIONAL',
'TRUE',
'TRUNCATE',
'TYPE',
'TYPES',
'UNCOMMITTED',
'UNIQUE',
'UNLOCK',
'UNSIGNED',
'USAGE',
'USE',
'USING',
'VARIABLES',
'VIEW',
'WHEN',
'WITH',
'WORK',
'WRITE',
'YEAR_MONTH',
];
/**
* @var array
*/
public static $reservedTopLevel = [
'SELECT',
'FROM',
'WHERE',
'SET',
'ORDER BY',
'GROUP BY',
'LIMIT',
'DROP',
'VALUES',
'UPDATE',
'HAVING',
'ADD',
'AFTER',
'ALTER TABLE',
'DELETE FROM',
'UNION ALL',
'UNION',
'EXCEPT',
'INTERSECT',
];
/**
* @var array
*/
public static $reservedNewLine = [
'LEFT OUTER JOIN',
'RIGHT OUTER JOIN',
'LEFT JOIN',
'RIGHT JOIN',
'OUTER JOIN',
'INNER JOIN',
'JOIN',
'XOR',
'OR',
'AND',
];
/**
* @var array
*/
public static $functions = [
'ABS',
'ACOS',
'ADDDATE',
'ADDTIME',
'AES_DECRYPT',
'AES_ENCRYPT',
'AREA',
'ASBINARY',
'ASCII',
'ASIN',
'ASTEXT',
'ATAN',
'ATAN2',
'AVG',
'BDMPOLYFROMTEXT',
'BDMPOLYFROMWKB',
'BDPOLYFROMTEXT',
'BDPOLYFROMWKB',
'BENCHMARK',
'BIN',
'BIT_AND',
'BIT_COUNT',
'BIT_LENGTH',
'BIT_OR',
'BIT_XOR',
'BOUNDARY',
'BUFFER',
'CAST',
'CEIL',
'CEILING',
'CENTROID',
'CHAR',
'CHARACTER_LENGTH',
'CHARSET',
'CHAR_LENGTH',
'COALESCE',
'COERCIBILITY',
'COLLATION',
'COMPRESS',
'CONCAT',
'CONCAT_WS',
'CONNECTION_ID',
'CONTAINS',
'CONV',
'CONVERT',
'CONVERT_TZ',
'CONVEXHULL',
'COS',
'COT',
'COUNT',
'CRC32',
'CROSSES',
'CURDATE',
'CURRENT_DATE',
'CURRENT_TIME',
'CURRENT_TIMESTAMP',
'CURRENT_USER',
'CURTIME',
'DATABASE',
'DATE',
'DATEDIFF',
'DATE_ADD',
'DATE_DIFF',
'DATE_FORMAT',
'DATE_SUB',
'DAY',
'DAYNAME',
'DAYOFMONTH',
'DAYOFWEEK',
'DAYOFYEAR',
'DECODE',
'DEFAULT',
'DEGREES',
'DES_DECRYPT',
'DES_ENCRYPT',
'DIFFERENCE',
'DIMENSION',
'DISJOINT',
'DISTANCE',
'ELT',
'ENCODE',
'ENCRYPT',
'ENDPOINT',
'ENVELOPE',
'EQUALS',
'EXP',
'EXPORT_SET',
'EXTERIORRING',
'EXTRACT',
'EXTRACTVALUE',
'FIELD',
'FIND_IN_SET',
'FLOOR',
'FORMAT',
'FOUND_ROWS',
'FROM_DAYS',
'FROM_UNIXTIME',
'GEOMCOLLFROMTEXT',
'GEOMCOLLFROMWKB',
'GEOMETRYCOLLECTION',
'GEOMETRYCOLLECTIONFROMTEXT',
'GEOMETRYCOLLECTIONFROMWKB',
'GEOMETRYFROMTEXT',
'GEOMETRYFROMWKB',
'GEOMETRYN',
'GEOMETRYTYPE',
'GEOMFROMTEXT',
'GEOMFROMWKB',
'GET_FORMAT',
'GET_LOCK',
'GLENGTH',
'GREATEST',
'GROUP_CONCAT',
'GROUP_UNIQUE_USERS',
'HEX',
'HOUR',
'IF',
'IFNULL',
'INET_ATON',
'INET_NTOA',
'INSERT',
'INSTR',
'INTERIORRINGN',
'INTERSECTION',
'INTERSECTS',
'INTERVAL',
'ISCLOSED',
'ISEMPTY',
'ISNULL',
'ISRING',
'ISSIMPLE',
'IS_FREE_LOCK',
'IS_USED_LOCK',
'LAST_DAY',
'LAST_INSERT_ID',
'LCASE',
'LEAST',
'LEFT',
'LENGTH',
'LINEFROMTEXT',
'LINEFROMWKB',
'LINESTRING',
'LINESTRINGFROMTEXT',
'LINESTRINGFROMWKB',
'LN',
'LOAD_FILE',
'LOCALTIME',
'LOCALTIMESTAMP',
'LOCATE',
'LOG',
'LOG10',
'LOG2',
'LOWER',
'LPAD',
'LTRIM',
'MAKEDATE',
'MAKETIME',
'MAKE_SET',
'MASTER_POS_WAIT',
'MAX',
'MBRCONTAINS',
'MBRDISJOINT',
'MBREQUAL',
'MBRINTERSECTS',
'MBROVERLAPS',
'MBRTOUCHES',
'MBRWITHIN',
'MD5',
'MICROSECOND',
'MID',
'MIN',
'MINUTE',
'MLINEFROMTEXT',
'MLINEFROMWKB',
'MOD',
'MONTH',
'MONTHNAME',
'MPOINTFROMTEXT',
'MPOINTFROMWKB',
'MPOLYFROMTEXT',
'MPOLYFROMWKB',
'MULTILINESTRING',
'MULTILINESTRINGFROMTEXT',
'MULTILINESTRINGFROMWKB',
'MULTIPOINT',
'MULTIPOINTFROMTEXT',
'MULTIPOINTFROMWKB',
'MULTIPOLYGON',
'MULTIPOLYGONFROMTEXT',
'MULTIPOLYGONFROMWKB',
'NAME_CONST',
'NULLIF',
'NUMGEOMETRIES',
'NUMINTERIORRINGS',
'NUMPOINTS',
'OCT',
'OCTET_LENGTH',
'OLD_PASSWORD',
'ORD',
'OVERLAPS',
'PASSWORD',
'PERIOD_ADD',
'PERIOD_DIFF',
'PI',
'POINT',
'POINTFROMTEXT',
'POINTFROMWKB',
'POINTN',
'POINTONSURFACE',
'POLYFROMTEXT',
'POLYFROMWKB',
'POLYGON',
'POLYGONFROMTEXT',
'POLYGONFROMWKB',
'POSITION',
'POW',
'POWER',
'QUARTER',
'QUOTE',
'RADIANS',
'RAND',
'RELATED',
'RELEASE_LOCK',
'REPEAT',
'REPLACE',
'REVERSE',
'RIGHT',
'ROUND',
'ROW_COUNT',
'RPAD',
'RTRIM',
'SCHEMA',
'SECOND',
'SEC_TO_TIME',
'SESSION_USER',
'SHA',
'SHA1',
'SIGN',
'SIN',
'SLEEP',
'SOUNDEX',
'SPACE',
'SQRT',
'SRID',
'STARTPOINT',
'STD',
'STDDEV',
'STDDEV_POP',
'STDDEV_SAMP',
'STRCMP',
'STR_TO_DATE',
'SUBDATE',
'SUBSTR',
'SUBSTRING',
'SUBSTRING_INDEX',
'SUBTIME',
'SUM',
'SYMDIFFERENCE',
'SYSDATE',
'SYSTEM_USER',
'TAN',
'TIME',
'TIMEDIFF',
'TIMESTAMP',
'TIMESTAMPADD',
'TIMESTAMPDIFF',
'TIME_FORMAT',
'TIME_TO_SEC',
'TOUCHES',
'TO_DAYS',
'TRIM',
'TRUNCATE',
'UCASE',
'UNCOMPRESS',
'UNCOMPRESSED_LENGTH',
'UNHEX',
'UNIQUE_USERS',
'UNIX_TIMESTAMP',
'UPDATEXML',
'UPPER',
'USER',
'UTC_DATE',
'UTC_TIME',
'UTC_TIMESTAMP',
'UUID',
'VARIANCE',
'VAR_POP',
'VAR_SAMP',
'VERSION',
'WEEK',
'WEEKDAY',
'WEEKOFYEAR',
'WITHIN',
'X',
'Y',
'YEAR',
'YEARWEEK',
];
/**
* @var array
*/
public static $boundaries = [
',',
';',
')',
'(',
'.',
'=',
'<',
'>',
'+',
'-',
'*',
'/',
'!',
'^',
'%',
'|',
'&',
'#',
];
/**
* @param $token
*
* @return bool
*/
public static function isTokenTypeReservedTopLevel($token)
{
return $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_RESERVED_TOP_LEVEL;
}
/**
* @param string $token
* @param Parentheses $parentheses
* @param Formatter $formatter
*/
public static function tokenHasLimitClause($token, Parentheses $parentheses, Formatter $formatter)
{
if ('LIMIT' === $token[Tokenizer::TOKEN_VALUE] && false === $parentheses->getInlineParentheses()) {
$formatter->setClauseLimit(true);
}
}
/**
* @param $token
* @param $tokens
* @param $i
* @param $originalTokens
*
* @return bool
*/
public static function tokenHasMultipleBoundaryCharactersTogether($token, &$tokens, $i, &$originalTokens)
{
return $token[Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_BOUNDARY
&& self::tokenPreviousCharacterIsBoundary($tokens, $i)
&& self::tokenPreviousCharacterIsWhiteSpace($token, $originalTokens);
}
/**
* @param $tokens
* @param $i
*
* @return bool
*/
public static function tokenPreviousCharacterIsBoundary(&$tokens, $i)
{
return (isset($tokens[$i - 1]) && $tokens[$i - 1][Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_BOUNDARY);
}
/**
* @param $token
* @param $originalTokens
*
* @return bool
*/
public static function tokenPreviousCharacterIsWhiteSpace($token, &$originalTokens)
{
return (isset($originalTokens[$token['i'] - 1])
&& $originalTokens[$token['i'] - 1][Tokenizer::TOKEN_TYPE] !== Tokenizer::TOKEN_TYPE_WHITESPACE);
}
/**
* @param $token
* @param $tokens
* @param $i
*
* @return bool
*/
public static function tokenIsMinusSign($token, &$tokens, $i)
{
return '-' === $token[Tokenizer::TOKEN_VALUE]
&& self::tokenNextCharacterIsNumber($tokens, $i)
&& isset($tokens[$i - 1]);
}
/**
* @param $tokens
* @param $i
*
* @return bool
*/
public static function tokenNextCharacterIsNumber(&$tokens, $i)
{
return (isset($tokens[$i + 1])
&& $tokens[$i + 1][Tokenizer::TOKEN_TYPE] === Tokenizer::TOKEN_TYPE_NUMBER);
}
}

View File

@ -0,0 +1,100 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/22/14
* Time: 1:19 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Helper;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class WhiteSpace.
*/
class WhiteSpace
{
/**
* @param $token
*
* @return bool
*/
public static function tokenHasExtraWhiteSpaceLeft($token)
{
return
$token[Tokenizer::TOKEN_VALUE] === '.'
|| $token[Tokenizer::TOKEN_VALUE] === ','
|| $token[Tokenizer::TOKEN_VALUE] === ';';
}
/**
* @param $token
*
* @return bool
*/
public static function tokenHasExtraWhiteSpaceRight($token)
{
return
$token[Tokenizer::TOKEN_VALUE] === '('
|| $token[Tokenizer::TOKEN_VALUE] === '.';
}
/**
* @param $tokenType
*
* @return bool
*/
public static function tokenIsNumberAndHasExtraWhiteSpaceRight($tokenType)
{
return
$tokenType !== Tokenizer::TOKEN_TYPE_QUOTE
&& $tokenType !== Tokenizer::TOKEN_TYPE_BACK_TICK_QUOTE
&& $tokenType !== Tokenizer::TOKEN_TYPE_WORD
&& $tokenType !== Tokenizer::TOKEN_TYPE_NUMBER;
}
/**
* @param $token
*
* @return bool
*/
public static function tokenHasExtraWhiteSpaces($token)
{
return \strpos($token[Tokenizer::TOKEN_VALUE], ' ') !== false
|| \strpos($token[Tokenizer::TOKEN_VALUE], "\n") !== false
|| \strpos($token[Tokenizer::TOKEN_VALUE], "\t") !== false;
}
/**
* @param $originalTokens
* @param $token
*
* @return bool
*/
public static function isPrecedingCurrentTokenOfTokenTypeWhiteSpace($originalTokens, $token)
{
return isset($originalTokens[$token['i'] - 1])
&& $originalTokens[$token['i'] - 1][Tokenizer::TOKEN_TYPE] !== Tokenizer::TOKEN_TYPE_WHITESPACE;
}
/**
* @param $originalTokens
*
* @return array
*/
public static function removeTokenWhitespace(array &$originalTokens)
{
$tokens = [];
foreach ($originalTokens as $i => &$token) {
if ($token[Tokenizer::TOKEN_TYPE] !== Tokenizer::TOKEN_TYPE_WHITESPACE) {
$token['i'] = $i;
$tokens[] = $token;
}
}
return $tokens;
}
}

View File

@ -0,0 +1,58 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/23/14
* Time: 1:34 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer\Parser;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class Boundary.
*/
final class Boundary
{
/**
* @param Tokenizer $tokenizer
* @param string $string
* @param array $matches
*/
public static function isBoundary(Tokenizer $tokenizer, $string, array &$matches)
{
if (!$tokenizer->getNextToken() &&
self::isBoundaryCharacter($string, $matches, $tokenizer->getRegexBoundaries())
) {
$tokenizer->setNextToken(self::getBoundaryCharacter($matches));
}
}
/**
* @param string $string
* @param array $matches
* @param string $regexBoundaries
*
* @return bool
*/
protected static function isBoundaryCharacter($string, array &$matches, $regexBoundaries)
{
return (1 == \preg_match('/^('.$regexBoundaries.')/', $string, $matches));
}
/**
* @param array $matches
*
* @return array
*/
protected static function getBoundaryCharacter(array &$matches)
{
return [
Tokenizer::TOKEN_VALUE => $matches[1],
Tokenizer::TOKEN_TYPE => Tokenizer::TOKEN_TYPE_BOUNDARY,
];
}
}

View File

@ -0,0 +1,93 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/23/14
* Time: 1:22 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer\Parser;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class Comment.
*/
final class Comment
{
/**
* @param Tokenizer $tokenizer
* @param string $string
*/
public static function isComment(Tokenizer $tokenizer, $string)
{
if (!$tokenizer->getNextToken() && self::isCommentString($string)) {
$tokenizer->setNextToken(self::getCommentString($string));
}
}
/**
* @param string $string
*
* @return bool
*/
protected static function isCommentString($string)
{
return !empty($string[0]) && ($string[0] === '#' || self::isTwoCharacterComment($string));
}
/**
* @param string $string
*
* @return bool
*/
protected static function isTwoCharacterComment($string)
{
return !empty($string[1]) && (isset($string[1]) && (self::startsWithDoubleDash($string) || self::startsAsBlock($string)));
}
/**
* @param string $string
*
* @return bool
*/
protected static function startsWithDoubleDash($string)
{
return !empty($string[1]) && ($string[0] === '-' && ($string[1] === $string[0]));
}
/**
* @param string $string
*
* @return bool
*/
protected static function startsAsBlock($string)
{
return !empty($string[1]) && ($string[0] === '/' && $string[1] === '*');
}
/**
* @param string $string
*
* @return array
*/
protected static function getCommentString($string)
{
$last = \strpos($string, '*/', 2) + 2;
$type = Tokenizer::TOKEN_TYPE_BLOCK_COMMENT;
if (!empty($string[0]) && ($string[0] === '-' || $string[0] === '#')) {
$last = \strpos($string, "\n");
$type = Tokenizer::TOKEN_TYPE_COMMENT;
}
$last = ($last === false) ? \strlen($string) : $last;
return [
Tokenizer::TOKEN_VALUE => \substr($string, 0, $last),
Tokenizer::TOKEN_TYPE => $type,
];
}
}

View File

@ -0,0 +1,81 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/23/14
* Time: 1:36 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer\Parser;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class LiteralString.
*/
final class LiteralString
{
/**
* @param Tokenizer $tokenizer
* @param string $string
* @param array $matches
*/
public static function isFunction(Tokenizer $tokenizer, $string, array &$matches)
{
if (!$tokenizer->getNextToken() && self::isFunctionString($string, $matches, $tokenizer->getRegexFunction())) {
$tokenizer->setNextToken(self::getFunctionString($string, $matches));
}
}
/**
* A function must be succeeded by '('.
* This makes it so that a function such as "COUNT(" is considered a function, but "COUNT" alone is not function.
*
* @param string $string
* @param array $matches
* @param string $regexFunction
*
* @return bool
*/
protected static function isFunctionString($string, array &$matches, $regexFunction)
{
return (1 == \preg_match('/^('.$regexFunction.'[(]|\s|[)])/', \strtoupper($string), $matches));
}
/**
* @param string $string
* @param array $matches
*
* @return array
*/
protected static function getFunctionString($string, array &$matches)
{
return [
Tokenizer::TOKEN_TYPE => Tokenizer::TOKEN_TYPE_RESERVED,
Tokenizer::TOKEN_VALUE => \substr($string, 0, \strlen($matches[1]) - 1),
];
}
/**
* @param Tokenizer $tokenizer
* @param string $string
* @param array $matches
*/
public static function getNonReservedString(Tokenizer $tokenizer, $string, array &$matches)
{
if (!$tokenizer->getNextToken()) {
$data = [];
if (1 == \preg_match('/^(.*?)($|\s|["\'`]|'.$tokenizer->getRegexBoundaries().')/', $string, $matches)) {
$data = [
Tokenizer::TOKEN_VALUE => $matches[1],
Tokenizer::TOKEN_TYPE => Tokenizer::TOKEN_TYPE_WORD,
];
}
$tokenizer->setNextToken($data);
}
}
}

View File

@ -0,0 +1,59 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/23/14
* Time: 1:32 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer\Parser;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class Numeral.
*/
final class Numeral
{
/**
* @param Tokenizer $tokenizer
* @param string $string
* @param array $matches
*
* @return array
*/
public static function isNumeral(Tokenizer $tokenizer, $string, array &$matches)
{
if (!$tokenizer->getNextToken() && self::isNumeralString($string, $matches, $tokenizer->getRegexBoundaries())) {
$tokenizer->setNextToken(self::getNumeralString($matches));
}
}
/**
* @param string $string
* @param array $matches
* @param string $regexBoundaries
*
* @return bool
*/
protected static function isNumeralString($string, array &$matches, $regexBoundaries)
{
return (1 == \preg_match(
'/^([0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)($|\s|"\'`|'.$regexBoundaries.')/',
$string,
$matches
));
}
/**
* @param array $matches
*
* @return array
*/
protected static function getNumeralString(array &$matches)
{
return [Tokenizer::TOKEN_VALUE => $matches[1], Tokenizer::TOKEN_TYPE => Tokenizer::TOKEN_TYPE_NUMBER];
}
}

View File

@ -0,0 +1,84 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/23/14
* Time: 1:23 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer\Parser;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class Quoted.
*/
final class Quoted
{
/**
* @param Tokenizer $tokenizer
* @param string $string
*/
public static function isQuoted(Tokenizer $tokenizer, $string)
{
if (!$tokenizer->getNextToken() && self::isQuotedString($string)) {
$tokenizer->setNextToken(self::getQuotedString($string));
}
}
/**
* @param string $string
*
* @return bool
*/
protected static function isQuotedString($string)
{
return !empty($string[0]) && ($string[0] === '"' || $string[0] === '\'' || $string[0] === '`' || $string[0] === '[');
}
/**
* @param string $string
*
* @return array
*/
protected static function getQuotedString($string)
{
$tokenType = Tokenizer::TOKEN_TYPE_QUOTE;
if (!empty($string[0]) && ($string[0] === '`' || $string[0] === '[')) {
$tokenType = Tokenizer::TOKEN_TYPE_BACK_TICK_QUOTE;
}
return [
Tokenizer::TOKEN_TYPE => $tokenType,
Tokenizer::TOKEN_VALUE => self::wrapStringWithQuotes($string),
];
}
/**
* This checks for the following patterns:
* 1. backtick quoted string using `` to escape
* 2. square bracket quoted string (SQL Server) using ]] to escape
* 3. double quoted string using "" or \" to escape
* 4. single quoted string using '' or \' to escape.
*
* @param string $string
*
* @return null
*/
public static function wrapStringWithQuotes($string)
{
$returnString = null;
$regex = '/^(((`[^`]*($|`))+)|((\[[^\]]*($|\]))(\][^\]]*($|\]))*)|'.
'(("[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)|((\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*(\'|$))+))/s';
if (1 == \preg_match($regex, $string, $matches)) {
$returnString = $matches[1];
}
return $returnString;
}
}

View File

@ -0,0 +1,116 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/23/14
* Time: 1:18 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer\Parser;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class Reserved.
*/
final class Reserved
{
/**
* @var array
*/
protected static $regex = [
Tokenizer::TOKEN_TYPE_RESERVED_TOP_LEVEL => 'getRegexReservedTopLevel',
Tokenizer::TOKEN_TYPE_RESERVED_NEWLINE => 'getRegexReservedNewLine',
Tokenizer::TOKEN_TYPE_RESERVED => 'getRegexReserved',
];
/**
* @param Tokenizer $tokenizer
* @param string $string
* @param array|null $previous
*
* @return array
*/
public static function isReserved(Tokenizer $tokenizer, $string, $previous)
{
$tokenData = [];
if (!$tokenizer->getNextToken() && self::isReservedPrecededByDotCharacter($previous)) {
$upperCase = \strtoupper($string);
self::getReservedString($tokenData, Tokenizer::TOKEN_TYPE_RESERVED_TOP_LEVEL, $string, $tokenizer);
self::getReservedString($tokenData, Tokenizer::TOKEN_TYPE_RESERVED_NEWLINE, $upperCase, $tokenizer);
self::getReservedString($tokenData, Tokenizer::TOKEN_TYPE_RESERVED, $string, $tokenizer);
$tokenizer->setNextToken($tokenData);
}
}
/**
* A reserved word cannot be preceded by a "." in order to differentiate "mytable.from" from the token "from".
*
* @param $previous
*
* @return bool
*/
protected static function isReservedPrecededByDotCharacter($previous)
{
return !$previous || !isset($previous[Tokenizer::TOKEN_VALUE]) || $previous[Tokenizer::TOKEN_VALUE] !== '.';
}
/**
* @param array $tokenData
* @param $type
* @param string $string
* @param Tokenizer $tokenizer
*/
protected static function getReservedString(array &$tokenData, $type, $string, Tokenizer $tokenizer)
{
$matches = [];
$method = self::$regex[$type];
if (empty($tokenData) && self::isReservedString(
$string,
$matches,
$tokenizer->$method(),
$tokenizer->getRegexBoundaries()
)
) {
$tokenData = self::getStringTypeArray($type, $string, $matches);
}
}
/**
* @param string $upper
* @param array $matches
* @param string $regexReserved
* @param string $regexBoundaries
*
* @return bool
*/
protected static function isReservedString($upper, array &$matches, $regexReserved, $regexBoundaries)
{
return 1 == \preg_match(
'/^('.$regexReserved.')($|\s|'.$regexBoundaries.')/',
\strtoupper($upper),
$matches
);
}
/**
* @param string $type
* @param string $string
* @param array $matches
*
* @return array
*/
protected static function getStringTypeArray($type, $string, array &$matches)
{
return [
Tokenizer::TOKEN_TYPE => $type,
Tokenizer::TOKEN_VALUE => \substr($string, 0, \strlen($matches[1])),
];
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/23/14
* Time: 1:26 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer\Parser;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class UserDefined.
*/
final class UserDefined
{
/**
* @param Tokenizer $tokenizer
* @param string $string
*
* @return array
*/
public static function isUserDefinedVariable(Tokenizer $tokenizer, $string)
{
if (!$tokenizer->getNextToken() && self::isUserDefinedVariableString($string)) {
$tokenizer->setNextToken(self::getUserDefinedVariableString($string));
}
}
/**
* @param string $string
*
* @return bool
*/
protected static function isUserDefinedVariableString(&$string)
{
return !empty($string[0]) && !empty($string[1]) && ($string[0] === '@' && isset($string[1]));
}
/**
* Gets the user defined variables for in quoted or non-quoted fashion.
*
* @param string $string
*
* @return array
*/
protected static function getUserDefinedVariableString(&$string)
{
$returnData = [
Tokenizer::TOKEN_VALUE => null,
Tokenizer::TOKEN_TYPE => Tokenizer::TOKEN_TYPE_VARIABLE,
];
self::setTokenValueStartingWithAtSymbolAndWrapped($returnData, $string);
self::setTokenValueStartingWithAtSymbol($returnData, $string);
return $returnData;
}
/**
* @param array $returnData
* @param string $string
*/
protected static function setTokenValueStartingWithAtSymbolAndWrapped(array &$returnData, $string)
{
if (!empty($string[1]) && ($string[1] === '"' || $string[1] === '\'' || $string[1] === '`')) {
$returnData[Tokenizer::TOKEN_VALUE] = '@'.Quoted::wrapStringWithQuotes(\substr($string, 1));
}
}
/**
* @param array $returnData
* @param string $string
*/
protected static function setTokenValueStartingWithAtSymbol(array &$returnData, $string)
{
if (null === $returnData[Tokenizer::TOKEN_VALUE]) {
$matches = [];
\preg_match('/^(@[a-zA-Z0-9\._\$]+)/', $string, $matches);
if ($matches) {
$returnData[Tokenizer::TOKEN_VALUE] = $matches[1];
}
}
}
}

View File

@ -0,0 +1,55 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 12/23/14
* Time: 1:19 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer\Parser;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class WhiteSpace.
*/
final class WhiteSpace
{
/**
* @param Tokenizer $tokenizer
* @param string $string
* @param array $matches
*/
public static function isWhiteSpace(Tokenizer $tokenizer, $string, array &$matches)
{
if (self::isWhiteSpaceString($string, $matches)) {
$tokenizer->setNextToken(self::getWhiteSpaceString($matches));
}
}
/**
* @param string $string
* @param array $matches
*
* @return bool
*/
public static function isWhiteSpaceString($string, array &$matches)
{
return (1 == \preg_match('/^\s+/', $string, $matches));
}
/**
* @param array $matches
*
* @return array
*/
public static function getWhiteSpaceString(array &$matches)
{
return [
Tokenizer::TOKEN_VALUE => $matches[0],
Tokenizer::TOKEN_TYPE => Tokenizer::TOKEN_TYPE_WHITESPACE,
];
}
}

View File

@ -0,0 +1,350 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/26/14
* Time: 12:10 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Sql\QueryFormatter\Tokenizer;
use NilPortugues\Sql\QueryFormatter\Helper\Token;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Parser\Boundary;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Parser\Comment;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Parser\Numeral;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Parser\Quoted;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Parser\Reserved;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Parser\LiteralString;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Parser\UserDefined;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Parser\WhiteSpace;
/**
* Class Tokenizer.
*/
class Tokenizer
{
const TOKEN_TYPE_WHITESPACE = 0;
const TOKEN_TYPE_WORD = 1;
const TOKEN_TYPE_QUOTE = 2;
const TOKEN_TYPE_BACK_TICK_QUOTE = 3;
const TOKEN_TYPE_RESERVED = 4;
const TOKEN_TYPE_RESERVED_TOP_LEVEL = 5;
const TOKEN_TYPE_RESERVED_NEWLINE = 6;
const TOKEN_TYPE_BOUNDARY = 7;
const TOKEN_TYPE_COMMENT = 8;
const TOKEN_TYPE_BLOCK_COMMENT = 9;
const TOKEN_TYPE_NUMBER = 10;
const TOKEN_TYPE_ERROR = 11;
const TOKEN_TYPE_VARIABLE = 12;
const TOKEN_TYPE = 0;
const TOKEN_VALUE = 1;
/**
* @var string
*/
protected $regexBoundaries;
/**
* @var string
*/
protected $regexReserved;
/**
* @var string
*/
protected $regexReservedNewLine;
/**
* @var string
*/
protected $regexReservedTopLevel;
/**
* @var string
*/
protected $regexFunction;
/**
* @var int
*/
protected $maxCacheKeySize = 15;
/**
* @var array
*/
protected $tokenCache = [];
/**
* @var array
*/
protected $nextToken = [];
/**
* @var int
*/
protected $currentStringLength = 0;
/**
* @var int
*/
protected $oldStringLength = 0;
/**
* @var string
*/
protected $previousToken = '';
/**
* @var int
*/
protected $tokenLength = 0;
/**
* @var array
*/
protected $tokens = [];
/**
* Builds all the regular expressions needed to Tokenize the input.
*/
public function __construct()
{
$reservedMap = \array_combine(Token::$reserved, \array_map('strlen', Token::$reserved));
\arsort($reservedMap);
Token::$reserved = \array_keys($reservedMap);
$this->regexFunction = $this->initRegex(Token::$functions);
$this->regexBoundaries = $this->initRegex(Token::$boundaries);
$this->regexReserved = $this->initRegex(Token::$reserved);
$this->regexReservedTopLevel = \str_replace(' ', '\\s+', $this->initRegex(Token::$reservedTopLevel));
$this->regexReservedNewLine = \str_replace(' ', '\\s+', $this->initRegex(Token::$reservedNewLine));
}
/**
* @param $variable
*
* @return string
*/
protected function initRegex($variable)
{
return '('.implode('|', \array_map(array($this, 'quoteRegex'), $variable)).')';
}
/**
* Takes a SQL string and breaks it into tokens.
* Each token is an associative array with type and value.
*
* @param string $string
*
* @return array
*/
public function tokenize($string)
{
return (\strlen($string) > 0) ? $this->processTokens($string) : [];
}
/**
* @param string $string
*
* @return array
*/
protected function processTokens($string)
{
$this->tokens = [];
$this->previousToken = '';
$this->currentStringLength = \strlen($string);
$this->oldStringLength = \strlen($string) + 1;
while ($this->currentStringLength >= 0) {
if ($this->oldStringLength <= $this->currentStringLength) {
break;
}
$string = $this->processOneToken($string);
}
return $this->tokens;
}
/**
* @param string $string
*
* @return string
*/
protected function processOneToken($string)
{
$token = $this->getToken($string, $this->currentStringLength, $this->previousToken);
$this->tokens[] = $token;
$this->tokenLength = \strlen($token[self::TOKEN_VALUE]);
$this->previousToken = $token;
$this->oldStringLength = $this->currentStringLength;
$this->currentStringLength -= $this->tokenLength;
return \substr($string, $this->tokenLength);
}
/**
* @param string $string
* @param int $currentStringLength
* @param string string
*
* @return array|mixed
*/
protected function getToken($string, $currentStringLength, $previousToken)
{
$cacheKey = $this->useTokenCache($string, $currentStringLength);
if (!empty($cacheKey) && isset($this->tokenCache[$cacheKey])) {
return $this->getNextTokenFromCache($cacheKey);
}
return $this->getNextTokenFromString($string, $previousToken, $cacheKey);
}
/**
* @param string $string
* @param int $currentStringLength
*
* @return string
*/
protected function useTokenCache($string, $currentStringLength)
{
$cacheKey = '';
if ($currentStringLength >= $this->maxCacheKeySize) {
$cacheKey = \substr($string, 0, $this->maxCacheKeySize);
}
return $cacheKey;
}
/**
* @param string $cacheKey
*
* @return mixed
*/
protected function getNextTokenFromCache($cacheKey)
{
return $this->tokenCache[$cacheKey];
}
/**
* Get the next token and the token type and store it in cache.
*
* @param string $string
* @param string $token
* @param string $cacheKey
*
* @return array
*/
protected function getNextTokenFromString($string, $token, $cacheKey)
{
$token = $this->parseNextToken($string, $token);
if ($cacheKey && \strlen($token[self::TOKEN_VALUE]) < $this->maxCacheKeySize) {
$this->tokenCache[$cacheKey] = $token;
}
return $token;
}
/**
* Return the next token and token type in a SQL string.
* Quoted strings, comments, reserved words, whitespace, and punctuation are all their own tokens.
*
* @param string $string The SQL string
* @param array $previous The result of the previous parseNextToken() call
*
* @return array An associative array containing the type and value of the token.
*/
protected function parseNextToken($string, $previous = null)
{
$matches = [];
$this->nextToken = [];
WhiteSpace::isWhiteSpace($this, $string, $matches);
Comment::isComment($this, $string);
Quoted::isQuoted($this, $string);
UserDefined::isUserDefinedVariable($this, $string);
Numeral::isNumeral($this, $string, $matches);
Boundary::isBoundary($this, $string, $matches);
Reserved::isReserved($this, $string, $previous);
LiteralString::isFunction($this, $string, $matches);
LiteralString::getNonReservedString($this, $string, $matches);
return $this->nextToken;
}
/**
* @return array
*/
public function getNextToken()
{
return $this->nextToken;
}
/**
* @param array $nextToken
*
* @return $this
*/
public function setNextToken($nextToken)
{
$this->nextToken = $nextToken;
return $this;
}
/**
* @return string
*/
public function getRegexBoundaries()
{
return $this->regexBoundaries;
}
/**
* @return string
*/
public function getRegexFunction()
{
return $this->regexFunction;
}
/**
* @return string
*/
public function getRegexReserved()
{
return $this->regexReserved;
}
/**
* @return string
*/
public function getRegexReservedNewLine()
{
return $this->regexReservedNewLine;
}
/**
* @return string
*/
public function getRegexReservedTopLevel()
{
return $this->regexReservedTopLevel;
}
/**
* Helper function for building regular expressions for reserved words and boundary characters.
*
* @param string $string
*
* @return string
*/
protected function quoteRegex($string)
{
return \preg_quote($string, '/');
}
}

View File

@ -0,0 +1,73 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/26/14
* Time: 9:11 PM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryFormatter;
use NilPortugues\Sql\QueryFormatter\Formatter;
/**
* Class FormatterTest.
*/
class FormatterTest extends \PHPUnit_Framework_TestCase
{
/**
* @var string
*/
private $querySeparator = "----------SEPARATOR----------\n";
/**
* @var string
*/
private $expectedResultFilePath = '/Resources/expectedQueries.sql';
/**
* @return array
*/
private function readExpectedQueryFile()
{
$expectedQueryArray = \explode(
$this->querySeparator,
\file_get_contents(\realpath(\dirname(__FILE__)).$this->expectedResultFilePath)
);
$expectedQueryArray = \array_filter($expectedQueryArray);
return $expectedQueryArray;
}
/**
* Data provider reading the test Queries.
*/
public function sqlQueryDataProvider()
{
$expectedQueryArray = $this->readExpectedQueryFile();
$queryTestSet = array();
foreach ($expectedQueryArray as $expectedQuery) {
$queryTestSet[] = array(\preg_replace('/\\s+/', ' ', $expectedQuery), $expectedQuery);
}
return $queryTestSet;
}
/**
* @test
* @dataProvider sqlQueryDataProvider
*
* @param $notIndented
* @param $indented
*/
public function itShouldReformatNoIndentQueriesToIndentedVersions($notIndented, $indented)
{
$formatter = new Formatter();
$result = $formatter->format($notIndented);
$this->assertSame($indented, $result);
}
}

View File

@ -0,0 +1,808 @@
SELECT
customer_id,
customer_name,
COUNT(order_id) AS total
FROM
customers
INNER JOIN orders ON customers.customer_id = orders.customer_id
GROUP BY
customer_id,
customer_name
HAVING
COUNT(order_id) > 5
ORDER BY
COUNT(order_id) DESC;
----------SEPARATOR----------
UPDATE
customers
SET
totalorders = ordersummary.total
FROM
(
SELECT
customer_id,
count(order_id) As total
FROM
orders
GROUP BY
customer_id
) As ordersummary
WHERE
customers.customer_id = ordersummary.customer_id
----------SEPARATOR----------
SELECT
*
FROM
sometable
UNION ALL
SELECT
*
FROM
someothertable;
----------SEPARATOR----------
SET
NAMES 'utf8';
----------SEPARATOR----------
CREATE TABLE `PREFIX_address` (
`id_address` int(10) unsigned NOT NULL auto_increment,
`id_country` int(10) unsigned NOT NULL,
`id_state` int(10) unsigned default NULL,
`id_customer` int(10) unsigned NOT NULL default '0',
`id_manufacturer` int(10) unsigned NOT NULL default '0',
`id_supplier` int(10) unsigned NOT NULL default '0',
`id_warehouse` int(10) unsigned NOT NULL default '0',
`alias` varchar(32) NOT NULL,
`company` varchar(64) default NULL,
`lastname` varchar(32) NOT NULL,
`firstname` varchar(32) NOT NULL,
`address1` varchar(128) NOT NULL,
`address2` varchar(128) default NULL,
`postcode` varchar(12) default NULL,
`city` varchar(64) NOT NULL,
`other` text,
`phone` varchar(16) default NULL,
`phone_mobile` varchar(16) default NULL,
`vat_number` varchar(32) default NULL,
`dni` varchar(16) DEFAULT NULL,
`date_add` datetime NOT NULL,
`date_upd` datetime NOT NULL,
`active` tinyint(1) unsigned NOT NULL default '1',
`deleted` tinyint(1) unsigned NOT NULL default '0',
PRIMARY KEY (`id_address`),
KEY `address_customer` (`id_customer`),
KEY `id_country` (`id_country`),
KEY `id_state` (`id_state`),
KEY `id_manufacturer` (`id_manufacturer`),
KEY `id_supplier` (`id_supplier`),
KEY `id_warehouse` (`id_warehouse`)
) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8
----------SEPARATOR----------
CREATE TABLE `PREFIX_alias` (
`id_alias` int(10) unsigned NOT NULL auto_increment,
`alias` varchar(255) NOT NULL,
`search` varchar(255) NOT NULL,
`active` tinyint(1) NOT NULL default '1',
PRIMARY KEY (`id_alias`),
UNIQUE KEY `alias` (`alias`)
) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8
----------SEPARATOR----------
CREATE TABLE `PREFIX_carrier` (
`id_carrier` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_reference` int(10) unsigned NOT NULL,
`id_tax_rules_group` int(10) unsigned DEFAULT '0',
`name` varchar(64) NOT NULL,
`url` varchar(255) DEFAULT NULL,
`active` tinyint(1) unsigned NOT NULL DEFAULT '0',
`deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
`shipping_handling` tinyint(1) unsigned NOT NULL DEFAULT '1',
`range_behavior` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_module` tinyint(1) unsigned NOT NULL DEFAULT '0',
`is_free` tinyint(1) unsigned NOT NULL DEFAULT '0',
`shipping_external` tinyint(1) unsigned NOT NULL DEFAULT '0',
`need_range` tinyint(1) unsigned NOT NULL DEFAULT '0',
`external_module_name` varchar(64) DEFAULT NULL,
`shipping_method` int(2) NOT NULL DEFAULT '0',
`position` int(10) unsigned NOT NULL default '0',
`max_width` int(10) DEFAULT 0,
`max_height` int(10) DEFAULT 0,
`max_depth` int(10) DEFAULT 0,
`max_weight` int(10) DEFAULT 0,
`grade` int(10) DEFAULT 0,
PRIMARY KEY (`id_carrier`),
KEY `deleted` (`deleted`, `active`),
KEY `id_tax_rules_group` (`id_tax_rules_group`)
) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8
----------SEPARATOR----------
CREATE TABLE IF NOT EXISTS `PREFIX_specific_price_rule` (
`id_specific_price_rule` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`id_shop` int(11) unsigned NOT NULL DEFAULT '1',
`id_currency` int(10) unsigned NOT NULL,
`id_country` int(10) unsigned NOT NULL,
`id_group` int(10) unsigned NOT NULL,
`from_quantity` mediumint(8) unsigned NOT NULL,
`price` DECIMAL(20, 6),
`reduction` decimal(20, 6) NOT NULL,
`reduction_type` enum('amount', 'percentage') NOT NULL,
`from` datetime NOT NULL,
`to` datetime NOT NULL,
PRIMARY KEY (`id_specific_price_rule`),
KEY `id_product` (
`id_shop`, `id_currency`, `id_country`,
`id_group`, `from_quantity`, `from`,
`to`
)
) ENGINE = ENGINE_TYPE DEFAULT CHARSET = utf8
----------SEPARATOR----------
UPDATE
`PREFIX_configuration`
SET
value = '6'
WHERE
name = 'PS_SEARCH_WEIGHT_PNAME'
----------SEPARATOR----------
UPDATE
`PREFIX_hook_module`
SET
position = 1
WHERE
id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayPayment'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'cheque'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayPaymentReturn'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'cheque'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayHome'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'homeslider'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'actionAuthentication'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'statsdata'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'actionShopDataDuplication'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'homeslider'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayTop'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'blocklanguages'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'actionCustomerAccountAdd'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'statsdata'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayCustomerAccount'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'favoriteproducts'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayAdminStatsModules'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'statsvisits'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayAdminStatsGraphEngine'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'graphvisifire'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayAdminStatsGridEngine'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'gridhtml'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayLeftColumnProduct'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'blocksharefb'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'actionSearch'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'statssearch'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'actionCategoryAdd'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'blockcategories'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'actionCategoryUpdate'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'blockcategories'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'actionCategoryDelete'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'blockcategories'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'actionAdminMetaSave'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'blockcategories'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayMyAccountBlock'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'favoriteproducts'
)
OR id_hook = (
SELECT
id_hook
FROM
`PREFIX_hook`
WHERE
name = 'displayFooter'
)
AND id_module = (
SELECT
id_module
FROM
`PREFIX_module`
WHERE
name = 'blockreinsurance'
)
----------SEPARATOR----------
ALTER TABLE
`PREFIX_employee`
ADD
`bo_color` varchar(32) default NULL
AFTER
`stats_date_to`
----------SEPARATOR----------
INSERT INTO `PREFIX_cms_category_lang`
VALUES
(
1, 3, 'Inicio', '', 'home', NULL, NULL,
NULL
)
----------SEPARATOR----------
INSERT INTO `PREFIX_cms_category`
VALUES
(1, 0, 0, 1, NOW(), NOW(), 0)
----------SEPARATOR----------
UPDATE
`PREFIX_cms_category`
SET
`position` = 0
----------SEPARATOR----------
ALTER TABLE
`PREFIX_customer`
ADD
`note` text
AFTER
`secure_key`
----------SEPARATOR----------
ALTER TABLE
`PREFIX_contact`
ADD
`customer_service` tinyint(1) NOT NULL DEFAULT 0
AFTER
`email`
----------SEPARATOR----------
INSERT INTO `PREFIX_specific_price` (
`id_product`, `id_shop`, `id_currency`,
`id_country`, `id_group`, `priority`,
`price`, `from_quantity`, `reduction`,
`reduction_type`, `from`, `to`
) (
SELECT
dq.`id_product`,
1,
1,
0,
1,
0,
0.00,
dq.`quantity`,
IF(
dq.`id_discount_type` = 2, dq.`value`,
dq.`value` / 100
),
IF (
dq.`id_discount_type` = 2, 'amount',
'percentage'
),
'0000-00-00 00:00:00',
'0000-00-00 00:00:00'
FROM
`PREFIX_discount_quantity` dq
INNER JOIN `PREFIX_product` p ON (p.`id_product` = dq.`id_product`)
)
----------SEPARATOR----------
DROP
TABLE `PREFIX_discount_quantity`
----------SEPARATOR----------
INSERT INTO `PREFIX_specific_price` (
`id_product`, `id_shop`, `id_currency`,
`id_country`, `id_group`, `priority`,
`price`, `from_quantity`, `reduction`,
`reduction_type`, `from`, `to`
) (
SELECT
p.`id_product`,
1,
0,
0,
0,
0,
0.00,
1,
IF(
p.`reduction_price` > 0, p.`reduction_price`,
p.`reduction_percent` / 100
),
IF(
p.`reduction_price` > 0, 'amount',
'percentage'
),
IF (
p.`reduction_from` = p.`reduction_to`,
'0000-00-00 00:00:00', p.`reduction_from`
),
IF (
p.`reduction_from` = p.`reduction_to`,
'0000-00-00 00:00:00', p.`reduction_to`
)
FROM
`PREFIX_product` p
WHERE
p.`reduction_price`
OR p.`reduction_percent`
)
----------SEPARATOR----------
ALTER TABLE
`PREFIX_product`
DROP
`reduction_price`,
DROP
`reduction_percent`,
DROP
`reduction_from`,
DROP
`reduction_to`
----------SEPARATOR----------
INSERT INTO `PREFIX_configuration` (
`name`, `value`, `date_add`, `date_upd`
)
VALUES
(
'PS_SPECIFIC_PRICE_PRIORITIES',
'id_shop;id_currency;id_country;id_group',
NOW(), NOW()
),
('PS_TAX_DISPLAY', 0, NOW(), NOW()),
(
'PS_SMARTY_FORCE_COMPILE', 1, NOW(),
NOW()
),
(
'PS_DISTANCE_UNIT', 'km', NOW(), NOW()
),
(
'PS_STORES_DISPLAY_CMS', 0, NOW(),
NOW()
),
(
'PS_STORES_DISPLAY_FOOTER', 0, NOW(),
NOW()
),
(
'PS_STORES_SIMPLIFIED', 0, NOW(),
NOW()
),
(
'PS_STATSDATA_CUSTOMER_PAGESVIEWS',
1, NOW(), NOW()
),
(
'PS_STATSDATA_PAGESVIEWS', 1, NOW(),
NOW()
),
(
'PS_STATSDATA_PLUGINS', 1, NOW(),
NOW()
)
----------SEPARATOR----------
INSERT INTO `PREFIX_configuration` (
`name`, `value`, `date_add`, `date_upd`
)
VALUES
(
'PS_CONDITIONS_CMS_ID',
IFNULL(
(
SELECT
`id_cms`
FROM
`PREFIX_cms`
WHERE
`id_cms` = 3
),
0
),
NOW(),
NOW()
)
----------SEPARATOR----------
CREATE TEMPORARY TABLE `PREFIX_configuration_tmp` (`value` text)
----------SEPARATOR----------
SET
@defaultOOS = (
SELECT
value
FROM
`PREFIX_configuration`
WHERE
name = 'PS_ORDER_OUT_OF_STOCK'
)
----------SEPARATOR----------
UPDATE
`PREFIX_product` p
SET
`cache_default_attribute` = 0
WHERE
`id_product` NOT IN (
SELECT
`id_product`
FROM
`PREFIX_product_attribute`
)
----------SEPARATOR----------
INSERT INTO `PREFIX_hook` (
`name`, `title`, `description`, `position`
)
VALUES
(
'processCarrier', 'Carrier Process',
NULL, 0
)
----------SEPARATOR----------
INSERT INTO `PREFIX_stock_mvt_reason_lang` (
`id_stock_mvt_reason`, `id_lang`,
`name`
)
VALUES
(1, 1, 'Order'),
(1, 2, 'Commande'),
(2, 1, 'Missing Stock Movement'),
(
2, 2, 'Mouvement de stock manquant'
),
(3, 1, 'Restocking'),
(3, 2, 'Réssort')
----------SEPARATOR----------
INSERT INTO `PREFIX_meta_lang` (
`id_lang`, `id_meta`, `title`, `url_rewrite`
)
VALUES
(
1,
(
SELECT
`id_meta`
FROM
`PREFIX_meta`
WHERE
`page` = 'authentication'
),
'Authentication',
'authentication'
),
(
2,
(
SELECT
`id_meta`
FROM
`PREFIX_meta`
WHERE
`page` = 'authentication'
),
'Authentification',
'authentification'
),
(
3,
(
SELECT
`id_meta`
FROM
`PREFIX_meta`
WHERE
`page` = 'authentication'
),
'Autenticación',
'autenticacion'
)
----------SEPARATOR----------
LOCK TABLES `admin_assert` WRITE
----------SEPARATOR----------
UNLOCK TABLES
----------SEPARATOR----------
DROP
TABLE IF EXISTS `admin_role`
----------SEPARATOR----------
SELECT
-- This is a test
----------SEPARATOR----------
SELECT
*
LIMIT
1;
SELECT
a,
b,
c,
d
FROM
e
LIMIT
1, 2;
SELECT
1,
2,
3
WHERE
a in (1, 2, 3, 4, 5)
AND b = 5;
----------SEPARATOR----------
SELECT
count - 50
WHERE
a - 50 = b
WHERE
1
AND -50
WHERE
-50 = a
WHERE
a = -50
WHERE
1
/*test*/
-50
WHERE
1
AND -50;
----------SEPARATOR----------
SELECT
@"weird variable name";
----------SEPARATOR----------
SELECT
"no closing quote
----------SEPARATOR----------
SELECT
[sqlserver]
FROM
[escap[e]]d style];
----------SEPARATOR----------
SELECT
count(*) AS all_columns,
`Column1`,
`Testing`,
`Testing Three`
FROM
`Table1`
WHERE
(Column1 = :v1)
AND (`Column2` = `Column3`)
AND (
`Column2` = `Column3`
OR Column4 >= NOW()
)
GROUP BY
Column1
ORDER BY
Column3 DESC
LIMIT
:v2,
:v3

View File

@ -0,0 +1,453 @@
<?php
/**
* Author: Nil Portugués Calderó <contact@nilportugues.com>
* Date: 6/26/14
* Time: 2:19 AM.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace NilPortugues\Tests\Sql\QueryFormatter\Tokenizer;
use NilPortugues\Sql\QueryFormatter\Tokenizer\Tokenizer;
/**
* Class TokenizerTest.
*/
class TokenizerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var Tokenizer
*/
private $tokenizer;
/**
*
*/
protected function setUp()
{
$this->tokenizer = new Tokenizer();
}
/**
*
*/
protected function tearDown()
{
$this->tokenizer = null;
}
/**
* @test
*/
public function itShouldTokenizeWhiteSpace()
{
$sql = <<<SQL
SELECT * FROM users;
SQL;
$result = $this->tokenizer->tokenize($sql);
$whiteSpacesFound = false;
foreach ($result as $token) {
if (' ' === $token[Tokenizer::TOKEN_VALUE]) {
$whiteSpacesFound = true;
break;
}
}
$this->assertTrue($whiteSpacesFound);
}
/**
* Comment starting with # will be treated as tokens representing the whole comment.
*
* @test
*/
public function itShouldTokenizeCommentStartingWithSymbolNumber()
{
$sql = <<<SQL
SELECT * FROM users # A comment
WHERE user_id = 2;
SQL;
$result = $this->tokenizer->tokenize($sql);
$commentFound = false;
foreach ($result as $token) {
if ('# A comment' === $token[Tokenizer::TOKEN_VALUE]) {
$commentFound = true;
break;
}
}
$this->assertTrue($commentFound);
}
/**
* Comment starting with -- will be treated as tokens representing the whole comment.
*
* @test
*/
public function itShouldTokenizeCommentStartingWithDash()
{
$sql = <<<SQL
SELECT * FROM
-- This is another comment
users
SQL;
$result = $this->tokenizer->tokenize($sql);
$commentFound = false;
foreach ($result as $token) {
if ('-- This is another comment' === $token[Tokenizer::TOKEN_VALUE]) {
$commentFound = true;
break;
}
}
$this->assertTrue($commentFound);
}
/**
* Comment blocks in SQL Server fashion will be treated as tokens representing the whole comment.
*
* @test
*/
public function itShouldTokenizeCommentWithBlockComment()
{
$sql = <<<SQL
SELECT * FROM /* This is a block comment */ WHERE 1 = 2;
SQL;
$result = $this->tokenizer->tokenize($sql);
$commentFound = false;
foreach ($result as $token) {
if ('/* This is a block comment */' === $token[Tokenizer::TOKEN_VALUE]) {
$commentFound = true;
break;
}
}
$this->assertTrue($commentFound);
}
/**
* Unterminated comment block will be processed incorrectly by the tokenizer,
* yet we should be able to detect this error in order to handle the resulting situation.
*
* @test
*/
public function itShouldTokenizeCommentWithUnterminatedBlockComment()
{
$sql = <<<SQL
SELECT * FROM /* This is a block comment WHERE 1 = 2;
SQL;
$result = $this->tokenizer->tokenize($sql);
$commentStartFound = false;
foreach ($result as $token) {
if ('/*' === $token[Tokenizer::TOKEN_VALUE]) {
$commentStartFound = true;
break;
}
}
$this->assertTrue($commentStartFound);
}
/**
* @test
*/
public function itShouldTokenizeQuoted()
{
$sql = <<<SQL
UPDATE `PREFIX_cms_category` SET `position` = 0
SQL;
$result = $this->tokenizer->tokenize($sql);
$quotedFound = false;
foreach ($result as $token) {
if ('`position`' === $token[Tokenizer::TOKEN_VALUE]) {
$quotedFound = true;
break;
}
}
$this->assertTrue($quotedFound);
}
/**
* SQL Server syntax for defining custom variables.
*
* @test
*/
public function itShouldTokenizeUserDefinedVariableWithQuotations()
{
$sql = <<<SQL
SELECT au_lname, au_fname, phone FROM authors WHERE au_lname LIKE @find;
SQL;
$result = $this->tokenizer->tokenize($sql);
$quotedFound = false;
foreach ($result as $token) {
if ('@find' === $token[Tokenizer::TOKEN_VALUE]) {
$quotedFound = true;
break;
}
}
$this->assertTrue($quotedFound);
}
/**
* This validates Microsoft SQL Server syntax for user variables.
*
* @test
*/
public function itShouldTokenizeUserDefinedVariableWithAtSymbol()
{
$sql = <<<SQL
SELECT @"weird variable name";
SQL;
$result = $this->tokenizer->tokenize($sql);
$this->assertNotEmpty($result);
}
/**
* This test is an edge case.
*
* Given the provided statement, will loop forever if no condition is checking total amount
* of the input string processed. This will happen because the tokenizer has no matching case
* and string processing will not progress.
*
* @test
*/
public function itShouldTokenizeUserDefinedVariableNoProgressTokenizer()
{
$sql = <<<SQL
SELECT @ and b; /* Edge case */
SQL;
$result = $this->tokenizer->tokenize($sql);
$userVariableNotFound = true;
foreach ($result as $token) {
if ('@' === $token[Tokenizer::TOKEN_VALUE]) {
$userVariableNotFound = false;
break;
}
}
$this->assertTrue($userVariableNotFound);
}
/**
* @test
*/
public function itShouldTokenizeNumeralInteger()
{
$sql = <<<SQL
SELECT user_id FROM user WHERE user_id = 1;
SQL;
$result = $this->tokenizer->tokenize($sql);
$numeralIntegerFound = false;
foreach ($result as $token) {
if ('1' == $token[Tokenizer::TOKEN_VALUE]) {
$numeralIntegerFound = true;
break;
}
}
$this->assertTrue($numeralIntegerFound);
}
/**
* @test
*/
public function itShouldTokenizeNumeralNegativeIntegerAsPositiveInteger()
{
$sql = <<<SQL
SELECT user_id FROM user WHERE user_id = -1;
SQL;
$result = $this->tokenizer->tokenize($sql);
$numeralIntegerFound = false;
foreach ($result as $token) {
if ('1' == $token[Tokenizer::TOKEN_VALUE]) {
$numeralIntegerFound = true;
break;
}
}
$this->assertTrue($numeralIntegerFound);
}
/**
* @test
*/
public function itShouldTokenizeNumeralFloat()
{
$sql = <<<SQL
SELECT user_id FROM user WHERE user_id = 3.14;
SQL;
$result = $this->tokenizer->tokenize($sql);
$numeralIntegerFound = false;
foreach ($result as $token) {
if ('3.14' == $token[Tokenizer::TOKEN_VALUE]) {
$numeralIntegerFound = true;
break;
}
}
$this->assertTrue($numeralIntegerFound);
}
/**
* @test
*/
public function itShouldTokenizeNumeralNegativeFloatAsPositiveFloat()
{
$sql = <<<SQL
SELECT user_id FROM user WHERE user_id = -3.14;
SQL;
$result = $this->tokenizer->tokenize($sql);
$numeralIntegerFound = false;
foreach ($result as $token) {
if ('3.14' == $token[Tokenizer::TOKEN_VALUE]) {
$numeralIntegerFound = true;
break;
}
}
$this->assertTrue($numeralIntegerFound);
}
/**
* Check if boundary characters are in array. Boundary characters are: ;:)(.=<>+-\/!^%|&#.
*
* @test
*/
public function itShouldTokenizeBoundaryCharacter()
{
$sql = 'SELECT id_user, name FROM users';
$result = $this->tokenizer->tokenize($sql);
$boundaryFound = false;
foreach ($result as $token) {
if (',' === $token[Tokenizer::TOKEN_VALUE]) {
$boundaryFound = true;
break;
}
}
$this->assertTrue($boundaryFound);
}
/**
* Tokenize columns should not be a problem, even if using a reserved word as a column name.
* Example: users.user_id
* Example of edge cases: users.desc, user.from.
*
* @test
*/
public function itShouldTokenizeReservedWordPrecededByDotCharacter()
{
$sql = <<<SQL
SELECT users.desc as userId FROM users;
SQL;
$result = $this->tokenizer->tokenize($sql);
$reservedTokenFound = false;
foreach ($result as $token) {
if ('desc' == $token[Tokenizer::TOKEN_VALUE]) {
$reservedTokenFound = true;
break;
}
}
$this->assertTrue($reservedTokenFound);
}
/**
* @test
*/
public function itShouldTokenizeReservedTopLevel()
{
$sql = 'SELECT id_user, name FROM users';
$result = $this->tokenizer->tokenize($sql);
$reservedTopLevelTokenFound = false;
foreach ($result as $token) {
if ('FROM' === $token[Tokenizer::TOKEN_VALUE]) {
$reservedTopLevelTokenFound = true;
break;
}
}
$this->assertTrue($reservedTopLevelTokenFound);
}
/**
* @test
*/
public function itShouldTokenizeReservedNewLine()
{
$sql = <<<SQL
UPDATE users SET registration_date = "0000-00-00" WHERE id_user = 1 OR id_user = 2;
SQL;
$result = $this->tokenizer->tokenize($sql);
$reservedNewLineTokenFound = false;
foreach ($result as $token) {
if ('OR' === $token[Tokenizer::TOKEN_VALUE]) {
$reservedNewLineTokenFound = true;
break;
}
}
$this->assertTrue($reservedNewLineTokenFound);
}
/**
* @test
*/
public function itShouldTokenizeReserved()
{
$sql = <<<SQL
SELECT customer_id, customer_name, COUNT(order_id) as total
FROM customers INNER JOIN orders ON customers.customer_id = orders.customer_id
GROUP BY customer_id, customer_name
HAVING COUNT(order_id) > 5
ORDER BY COUNT(order_id) DESC;
SQL;
$result = $this->tokenizer->tokenize($sql);
$reservedTokenFound = false;
foreach ($result as $token) {
if ('INNER JOIN' === $token[Tokenizer::TOKEN_VALUE]) {
$reservedTokenFound = true;
break;
}
}
$this->assertTrue($reservedTokenFound);
}
/**
* @test
*/
public function itShouldTokenizeFunction()
{
$sql = <<<SQL
SELECT customer_id, customer_name, COUNT(order_id) as total FROM customers GROUP BY customer_id, customer_name
HAVING COUNT(order_id) > 5 ORDER BY COUNT(order_id) DESC;
SQL;
$result = $this->tokenizer->tokenize($sql);
$functionFound = false;
foreach ($result as $token) {
if ('COUNT' === $token[Tokenizer::TOKEN_VALUE]) {
$functionFound = true;
break;
}
}
$this->assertTrue($functionFound);
}
}