From 92aba3e20baec30aa18f830c1db4ba9031f29cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sat, 25 Nov 2017 01:03:08 +0100 Subject: [PATCH 1/5] fix: bad usage of join function --- src/Pecee/Pixie/QueryBuilder/QueryObject.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Pecee/Pixie/QueryBuilder/QueryObject.php b/src/Pecee/Pixie/QueryBuilder/QueryObject.php index 6874e94..14967a6 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryObject.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryObject.php @@ -39,14 +39,6 @@ public function __construct($sql, array $bindings, \PDO $pdo) $this->pdo = $pdo; } - /** - * @return string - */ - public function getSql() - { - return $this->sql; - } - /** * @return array */ @@ -65,6 +57,14 @@ public function getRawSql() return $this->interpolateQuery($this->sql, $this->bindings); } + /** + * @return string + */ + public function getSql() + { + return $this->sql; + } + /** * Replaces any parameter placeholders in a query with the value of that * parameter. Useful for debugging. Assumes anonymous parameters from @@ -95,7 +95,7 @@ protected function interpolateQuery($query, $params) } if (is_array($value)) { - $values[$key] = join(',', $this->pdo->quote($value)); + $values[$key] = $this->pdo->quote(join(',', $value)); } if ($value === null) { From 0557ca86279849521e677363b9f323a1ac5227e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Pude=C5=82ek?= Date: Sat, 25 Nov 2017 01:09:02 +0100 Subject: [PATCH 2/5] The special ::class constant are available for PHP >=5.5.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 6c19420..1643124 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ } ], "require": { - "php": ">=5.4.0", + "php": ">=5.5.0", "usmanhalalit/viocon": "1.0.1" }, "require-dev": { From eac1e8a07aa8d8b41ab246a75de07e2ae8af4f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sat, 25 Nov 2017 18:00:32 +0100 Subject: [PATCH 3/5] LOTS of optimization - Added more PHP-docs and fixed types in many of the existing. - Changed code style to comply with PSR-2. - Fixed homepage-url in `composer.json`. - Cleanup and optimizations - should be faster and more memory efficient. - Added better data-type handling for PDO parameters. - Updated documentation. --- README.md | 695 ++++++++++++------ composer.json | 2 +- src/Pecee/Pixie/Connection.php | 9 +- .../Pixie/ConnectionAdapters/BaseAdapter.php | 6 +- src/Pecee/Pixie/ConnectionAdapters/Mysql.php | 11 +- src/Pecee/Pixie/ConnectionAdapters/Pgsql.php | 11 +- src/Pecee/Pixie/ConnectionAdapters/Sqlite.php | 7 +- src/Pecee/Pixie/EventHandler.php | 38 +- src/Pecee/Pixie/Exception.php | 1 + .../QueryBuilder/Adapters/BaseAdapter.php | 243 +++--- .../Pixie/QueryBuilder/Adapters/Mysql.php | 1 + .../Pixie/QueryBuilder/Adapters/Pgsql.php | 1 + src/Pecee/Pixie/QueryBuilder/JoinBuilder.php | 27 +- .../Pixie/QueryBuilder/NestedCriteria.php | 11 +- .../QueryBuilder/QueryBuilderHandler.php | 508 +++++++------ src/Pecee/Pixie/QueryBuilder/QueryObject.php | 31 +- src/Pecee/Pixie/QueryBuilder/Transaction.php | 5 +- .../QueryBuilder/TransactionHaltException.php | 1 + tests/Pecee/Pixie/NoTableSubQueryTest.php | 2 +- .../Pecee/Pixie/QueryBuilderBehaviorTest.php | 4 +- 20 files changed, 975 insertions(+), 639 deletions(-) diff --git a/README.md b/README.md index 527ee85..5c074b9 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,41 @@ # pecee/pixie: Advanced lightweight querybuilder + A lightweight, expressive, framework agnostic query builder for PHP it can also be referred as a Database Abstraction Layer. Pixie supports MySQL, SQLite and PostgreSQL and it takes care of query sanitization, table prefixing and many other things with a unified API. At least PHP 5.3 is required. The syntax is quite similar to Laravel's query builder. +#### Ideas and issues + +If you want a great new feature or experience any issues what-so-ever, please feel free to leave an issue and i'll look into it whenever possible. + +##### Issues guidelines + +- Please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue. +For example: if you are expiriencing issues, you should provide the nessesary steps to reproduct the error within your description. + +- We love to hear out any ideas or feedback to the library. + +##### Contribution development guidelines + +- Please try to follow the PSR-2 codestyle guidelines. + +- Please create your pull requests to the development base that matches the version number you want to change. +For example when pushing changes to version 3, the pull request should use the `v3-development` base/branch. + +- Create detailed descriptions for your commits, as these will be used in the changelog for new releases. + +- When changing existing functionality, please ensure that the unit-tests working. + +- When adding new stuff, please remember to add new unit-tests for the functionality. + #### Credits This project is based on the original [Pixie project by usmanhalalit](https://github.com/usmanhalalit/pixie) but has some extra features like: -- Easier sub-queries -- Custom prefix/aliases for tables (prefix.`table`) -- Support for not defining table -- Better handling of `Raw` objects in `where` statements -- Tons of bugfixes and performance optimisations +- Easier sub-queries. +- Custom prefix/aliases for tables (prefix.`table`). +- Support for not defining table. +- Better handling of `Raw` objects in `where` statements. +- Tons of bugfixes and performance optimisations. - Much more... **Including all the original features like:** @@ -27,7 +52,8 @@ Older versions prior to 3.x are available [https://github.com/skipperbent/pixie] #### Note -`AliasFacade` (custom class-alias) support has been removed to increase performance. If you need this feature, please add this yourself. +`AliasFacade` used for calling the database-connection as a fixed constant has been removed to increase performance. +If this feature is required in your setup we encourage you to implement your own solution. ## Example ```php @@ -50,20 +76,20 @@ $config = array( ), ); -$qb = (new \Pecee\Pixie\Connection('mysql', $config))->getQueryBuilder(); +$queryBuilder = (new \Pecee\Pixie\Connection('mysql', $config))->getQueryBuilder(); ``` **Simple Query:** The query below returns the row where id = 3, null if no rows. ```PHP -$row = $qb->table('my_table')->find(3); +$row = $queryBuilder->table('my_table')->find(3); ``` **Full Queries:** ```PHP -$query = $qb->table('my_table')->where('name', '=', 'Sana'); +$query = $queryBuilder->table('my_table')->where('name', '=', 'Sana'); // Get result $query->get(); @@ -74,7 +100,7 @@ $query->get(); After the code below, every time a select query occurs on `users` table, it will add this where criteria, so banned users don't get access. ```PHP -$qb->registerEvent('before-select', 'users', function($qb) +$queryBuilder->registerEvent('before-select', 'users', function(QueryBuilderHandler $qb) { $qb->where('status', '!=', 'banned'); }); @@ -142,60 +168,75 @@ composer install pecee/pixie ___ ## Connection -Pixie supports three database drivers, MySQL, SQLite and PostgreSQL. You can specify the driver during connection and the associated configuration when creating a new connection. You can also create multiple connections, but you can use alias for only one connection at a time.; -```PHP +Pixie supports three database drivers, MySQL, SQLite and PostgreSQL. +You can specify the driver during connection and the associated configuration when creating a new connection. You can also create multiple connections, but you can use alias for only one connection at a time.; + +```php // Make sure you have Composer's autoload file included require 'vendor/autoload.php'; -$config = array( - 'driver' => 'mysql', // Db driver - 'host' => 'localhost', - 'database' => 'your-database', - 'username' => 'root', - 'password' => 'your-password', - 'charset' => 'utf8', // Optional - 'collation' => 'utf8_unicode_ci', // Optional - 'prefix' => 'cb_', // Table prefix, optional - ); +$config = [ + 'driver' => 'mysql', // Db driver + 'host' => 'localhost', + 'database' => 'your-database', + 'username' => 'root', + 'password' => 'your-password', + 'charset' => 'utf8', // Optional + 'collation' => 'utf8_unicode_ci', // Optional + 'prefix' => 'cb_', // Table prefix, optional +]; -$qb = (new \Pecee\Pixie\Connection('mysql', $config))->getQueryBuilder(); +// Creates new connection +$connection = new \Pecee\Pixie\Connection('mysql', $config); + +// Get the query-builder object +$queryBuilder = $connection->getQueryBuilder(); // Run query -$query = $qb->table('my_table')->where('name', '=', 'Sana'); +$person = $queryBuilder + ->table('persons') + ->where('name', '=', 'Bobby') + ->first(); ``` `$connection` here is optional, if not given it will always associate itself to the first connection, but it can be useful when you have multiple database connections. ### SQLite and PostgreSQL Config Sample -```PHP -$qb = new \Pecee\Pixie\Connection('sqlite', array( - 'driver' => 'sqlite', - 'database' => 'your-file.sqlite', - 'prefix' => 'cb_', - )); + +The example below is for use with sqlite-databases. + +```php +$queryBuilder = new \Pecee\Pixie\Connection('sqlite', [ + 'driver' => 'sqlite', + 'database' => 'your-file.sqlite', + 'prefix' => 'cb_', +]); ``` -```PHP -$qb = new \Pecee\Pixie\Connection('pgsql', array( - 'driver' => 'pgsql', - 'host' => 'localhost', - 'database' => 'your-database', - 'username' => 'postgres', - 'password' => 'your-password', - 'charset' => 'utf8', - 'prefix' => 'cb_', - 'schema' => 'public', - )); +The example below is for pgsql databases. + +```php +$queryBuilder = new \Pecee\Pixie\Connection('pgsql', [ + 'driver' => 'pgsql', + 'host' => 'localhost', + 'database' => 'your-database', + 'username' => 'postgres', + 'password' => 'your-password', + 'charset' => 'utf8', + 'prefix' => 'cb_', + 'schema' => 'public', +]); ``` ## Query + It is recommend to use `table()` method before every query, except raw `query()`. To select from multiple tables just pass an array. However this is not required. -```PHP -$qb->table(array('mytable1', 'mytable2')); +```php +$queryBuilder->table(array('mytable1', 'mytable2')); ``` ### Table alias @@ -203,139 +244,214 @@ $qb->table(array('mytable1', 'mytable2')); You can easily set the table alias by using ```php -$qb -->table(['table1' => 'foo1']) -->join('table2', 'table2.person_id', '=', 'foo1.id'); +$queryBuilder + ->table(['table1' => 'foo1']) + ->join('table2', 'table2.person_id', '=', 'foo1.id'); ``` You can change the alias anytime by using ```php -$qb->alias($table, $alias); +$queryBuilder->alias($table, $alias); ``` Output: -``` -SELECT * FROM `table1` AS foo1 INNER JOIN `cb_table2` ON `cb_table2`.`person_id` = `cb_foo1`.`id` +```sql +SELECT * +FROM `table1` AS foo1 +INNER JOIN `cb_table2` ON `cb_table2`.`person_id` = `cb_foo1`.`id` ``` ### Get Easily + The query below returns the (first) row where id = 3, null if no rows. -```PHP -$row = $qb->table('my_table')->find(3); + +```php +$row = $queryBuilder + ->table('my_table') + ->find(3); ``` -Access your row like, `echo $row->name`. If your field name is not `id` then pass the field name as second parameter `$qb->table('my_table')->find(3, 'person_id');`. + +Access your row like, `echo $row->name`. If your field name is not `id` then pass the field name as second parameter `$queryBuilder->table('my_table')->find(3, 'person_id');`. The query below returns the all rows where name = 'Sana', null if no rows. -```PHP -$result = $qb->table('my_table')->findAll('name', 'Sana'); + +```php +$result = $queryBuilder + ->table('my_table') + ->findAll('name', 'Sana'); ``` ### Select -```PHP -$query = $qb->table('my_table')->select('*'); + +#### Select single field + +```php +$queryBuilder->table('my_table')->select('*'); ``` -#### Multiple Selects -```PHP -->select(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3')); +#### Select multiple fields + +```php +$queryBuilder->table('my_table')->select(array('field1', 'field2')); +``` + +#### Multiple selects + +```php +$queryBuilder + ->select( + [ + 'mytable.myfield1', + 'mytable.myfield2', + 'another_table.myfield3' + ] + ); ``` Using select method multiple times `select('a')->select('b')` will also select `a` and `b`. Can be useful if you want to do conditional selects (within a PHP `if`). -#### Select Distinct -```PHP -->selectDistinct(array('mytable.myfield1', 'mytable.myfield2')); +#### Select distinct + +```php +$queryBuilder->selectDistinct(array('mytable.myfield1', 'mytable.myfield2')); ``` -#### Get All -Return an array. -```PHP -$query = $qb->table('my_table')->where('name', '=', 'Sana'); -$result = $query->get(); +#### Get all + +Returns an array. + +```php +$results = $queryBuilder + ->table('my_table') + ->where('name', '=', 'Sana') + ->get(); ``` + You can loop through it like: -```PHP -foreach ($result as $row) { + +```php +foreach ($results as $row) { echo $row->name; } ``` #### Get First Row -```PHP -$query = $qb->table('my_table')->where('name', '=', 'Sana'); -$row = $query->first(); + +```php +$row = $queryBuilder + ->table('my_table') + ->where('name', '=', 'Sana') + ->first(); ``` Returns the first row, or null if there is no record. Using this method you can also make sure if a record exists. Access these like `echo $row->name`. #### Get Rows Count -```PHP -$query = $qb->table('my_table')->where('name', '=', 'Sana'); -$query->count(); + +```php +$queryBuilder + ->table('my_table') + ->where('name', '=', 'Sana') + ->count(); ``` #### Select With Sub-Queries -```PHP -$subQuery1 = $this->builder->table('mail')->select($this->builder->raw('COUNT(*)')); -$subQuery2 = $this->builder->table('event_message')->select($this->builder->raw('COUNT(*)')); +```php +// Creates the first sub-query -$count = $this->builder->select($this->builder->subQuery($subQuery1, 'row1'), $this->builder->subQuery($subQuery2, 'row2'))->first(); +$subQuery1 = + $queryBuilder + ->table('mail') + ->select( + $queryBuilder->raw('COUNT(*)') + ); + +// Create the second sub-query + +$subQuery2 = + $queryBuilder + ->table('event_message') + ->select( + $queryBuilder->raw('COUNT(*)') + ); + +// Executes the query which uses the subqueries as fields +$count = + $queryBuilder + ->select( + $queryBuilder->subQuery($subQuery1, 'row1'), + $queryBuilder->subQuery($subQuery2, 'row2') + ) + ->first(); ``` -Will produce the following query: +The example above will output a SQL-query like this: ```sql -SELECT (SELECT COUNT(*) FROM `cb_mail`) as row1, (SELECT COUNT(*) FROM `cb_event_message`) as row2 LIMIT 1 +SELECT + (SELECT COUNT(*) + FROM `cb_mail`) AS row1, + + (SELECT COUNT(*) + FROM `cb_event_message`) AS row2 +LIMIT 1 ``` You can also easily create a subjquery within the `where` statement: ```php -$qb->where($qb->subQuery($subQuery), '!=', 'value') +$queryBuilder + ->where($queryBuilder->subQuery($subQuery), '!=', 'value'); ``` ### Where + Basic syntax is `(fieldname, operator, value)`, if you give two parameters then `=` operator is assumed. So `where('name', 'usman')` and `where('name', '=', 'usman')` is the same. -```PHP -$qb->table('my_table') +```php +$queryBuilder + ->table('my_table') ->where('name', '=', 'usman') ->whereNot('age', '>', 25) ->orWhere('type', '=', 'admin') - ->orWhereNot('description', 'LIKE', '%query%') - ; + ->orWhereNot('description', 'LIKE', '%query%'); ``` #### Where In -```PHP -$qb->table('my_table') + +```php +$queryBuilder + ->table('my_table') ->whereIn('name', array('usman', 'sana')) - ->orWhereIn('name', array('heera', 'dalim')) - ; + ->orWhereIn('name', array('heera', 'dalim')); -$qb->table('my_table') +$queryBuilder + ->table('my_table') ->whereNotIn('name', array('heera', 'dalim')) - ->orWhereNotIn('name', array('usman', 'sana')) - ; + ->orWhereNotIn('name', array('usman', 'sana')); ``` #### Where Between -```PHP -$qb->table('my_table') + +```php +$queryBuilder + ->table('my_table') ->whereBetween('id', 10, 100) ->orWhereBetween('status', 5, 8); ``` #### Where Null -```PHP -$qb->table('my_table') + +```php +$queryBuilder + ->table('my_table') ->whereNull('modified') ->orWhereNull('field2') ->whereNotNull('field3') @@ -343,52 +459,64 @@ $qb->table('my_table') ``` #### Grouped Where + Sometimes queries get complex, where you need grouped criteria, for example `WHERE age = 10 and (name like '%usman%' or description LIKE '%usman%')`. Pixie allows you to do so, you can nest as many closures as you need, like below. -```PHP -$qb->table('my_table') - ->where('my_table.age', 10) - ->where(function($q) - { - $q->where('name', 'LIKE', '%usman%'); - // You can provide a closure on these wheres too, to nest further. - $q->orWhere('description', 'LIKE', '%usman%'); - }); + +```php +$queryBuilder + ->table('my_table') + ->where('my_table.age', 10) + ->where(function(QueryBuilderHandler $qb) { + $qb->where('name', 'LIKE', '%pecee%'); + + // You can provide a closure on these wheres too, to nest further. + $qb->orWhere('description', 'LIKE', '%usman%'); + }); ``` ### Group By and Order By -```PHP -$query = $qb->table('my_table')->groupBy('age')->orderBy('created_at', 'ASC'); + +```php +$query = $queryBuilder + ->table('my_table') + ->groupBy('age') + ->orderBy('created_at', 'ASC'); ``` #### Multiple Group By -```PHP -->groupBy(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3')); -->orderBy(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3')); +```php +$queryBuilder + ->groupBy(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3')); + ->orderBy(array('mytable.myfield1', 'mytable.myfield2', 'another_table.myfield3')); ``` Using `groupBy()` or `orderBy()` methods multiple times `groupBy('a')->groupBy('b')` will also group by first `a` and than `b`. Can be useful if you want to do conditional grouping (within a PHP `if`). Same applies to `orderBy()`. ### Having -```PHP -->having('total_count', '>', 2) -->orHaving('type', '=', 'admin'); + +```php +$queryBuilder + ->having('total_count', '>', 2) + ->orHaving('type', '=', 'admin'); ``` ### Limit and Offset -```PHP -->limit(30); -->offset(10); +```php +$queryBuilder + ->limit(30); + ->offset(10); ``` ### Join -```PHP -$qb->table('my_table') - ->join('another_table', 'another_table.person_id', '=', 'my_table.id') +```php +$queryBuilder + ->table('my_table') + ->join('another_table', 'another_table.person_id', '=', 'my_table.id') ``` Available methods, @@ -398,63 +526,79 @@ Available methods, - rightJoin() If you need `FULL OUTER` join or any other join, just pass it as 5th parameter of `join` method. -```PHP -->join('another_table', 'another_table.person_id', '=', 'my_table.id', 'FULL OUTER') + +```php +$queryBuilder + ->join('another_table', 'another_table.person_id', '=', 'my_table.id', 'FULL OUTER') ``` #### Multiple Join Criteria + If you need more than one criterion to join a table then pass a closure as second parameter. -```PHP -->join('another_table', function($table) +```php +$queryBuilder + ->join('another_table', function($table) { $table->on('another_table.person_id', '=', 'my_table.id'); $table->on('another_table.person_id2', '=', 'my_table.id2'); - $table->orOn('another_table.age', '>', $qb->raw(1)); + $table->orOn('another_table.age', '>', $queryBuilder->raw(1)); }) ``` ### Raw Query -You can always use raw queries if you need, -```PHP -$query = $qb->query('select * from cb_my_table where age = 12'); -var_dump($query->get()); +You can always perform raw queries, if needed. + +```php +$query = $queryBuilder->query('SELECT * FROM persons WHERE age = 12'); + +$kids = $query->get(); ``` -You can also pass your bindings -```PHP -$qb->query('select * from cb_my_table where age = ? and name = ?', array(10, 'usman')); +You can also pass custom bindings + +```php +$queryBuilder + ->query('SELECT * FROM persons WHERE age = ? AND name = ?', array(10, 'usman')); ``` #### Raw Expressions When you wrap an expression with `raw()` method, Pixie doesn't try to sanitize these. -```PHP -$qb->table('my_table') - ->select($qb->raw('count(cb_my_table.id) as tot')) - ->where('value', '=', 'Ifrah') - ->where($qb->raw('DATE(?)', 'now')) + +```php +$queryBuilder + ->table('my_table') + ->select($queryBuilder->raw('count(cb_my_table.id) as tot')) + ->where('value', '=', 'Ifrah') + ->where($queryBuilder->raw('DATE(?)', 'now')) ``` ___ + **NOTE:** Queries that run through `query()` method are not sanitized until you pass all values through bindings. Queries that run through `raw()` method are not sanitized either, you have to do it yourself. And of course these don't add table prefix too, but you can use the `addTablePrefix()` method. ### Insert -```PHP -$data = array( + +```php +$data = [ 'name' => 'Sana', 'description' => 'Blah' -); -$insertId = $qb->table('my_table')->insert($data); +]; + +$insertId = $queryBuilder + ->table('my_table') + ->insert($data); ``` `insert()` method returns the insert id. #### Batch Insert -```PHP -$data = array( + +```php +$data = [ array( 'name' => 'Sana', 'description' => 'Blah' @@ -463,41 +607,60 @@ $data = array( 'name' => 'Usman', 'description' => 'Blah' ), -); -$insertIds = $qb->table('my_table')->insert($data); +]; + +$insertIds = $queryBuilder + ->table('my_table') + ->insert($data); ``` In case of batch insert, it will return an array of insert ids. #### Insert with ON DUPLICATE KEY statement -```PHP -$data = array( + +```php +$data = [ 'name' => 'Sana', 'counter' => 1 -); -$dataUpdate = array( +]; + +$dataUpdate = [ 'name' => 'Sana', 'counter' => 2 -); -$insertId = $qb->table('my_table')->onDuplicateKeyUpdate($dataUpdate)->insert($data); +]; + +$insertId = + $queryBuilder + ->table('my_table') + ->onDuplicateKeyUpdate($dataUpdate) + ->insert($data); ``` ### Update -```PHP -$data = array( + +```php +$data = [ 'name' => 'Sana', 'description' => 'Blah' -); +]; -$qb->table('my_table')->where('id', 5)->update($data); +$queryBuilder + ->table('my_table') + ->where('id', 5) + ->update($data); ``` Will update the name field to Sana and description field to Blah where id = 5. ### Delete -```PHP -$qb->table('my_table')->where('id', '>', 5)->delete(); + +```php +$queryBuilder + ->table('my_table') + ->where('id', '>', 5) + ->delete(); ``` + Will delete all the rows where id is greater than 5. ### Transactions @@ -509,17 +672,21 @@ are made. Here's a basic transaction: -```PHP -$qb->transaction(function ($qb) { - $qb->table('my_table')->insert(array( - 'name' => 'Test', - 'url' => 'example.com' - )); +```php +$queryBuilder->transaction(function (QueryBuilderHandler $qb) { + $qb + ->table('my_table') + ->insert(array( + 'name' => 'Test', + 'url' => 'example.com' + ); - $qb->table('my_table')->insert(array( - 'name' => 'Test2', - 'url' => 'example.com' - )); + $qb + ->table('my_table') + ->insert(array( + 'name' => 'Test2', + 'url' => 'example.com' + )); }); ``` @@ -530,80 +697,150 @@ be successfully saved. If you wish to manually commit or rollback your changes, you can use the `commit()` and `rollback()` methods accordingly: -```PHP -$qb->transaction(function (qb) { - $qb->table('my_table')->insert(array(/* data... */)); +```php +$queryBuilder + ->transaction(function (qb) + { + $queryBuilder + ->table('my_table') + ->insert($data); - $qb->commit(); // to commit the changes (data would be saved) - $qb->rollback(); // to rollback the changes (data would be rejected) -}); + // Commit changes (data will be saved) + + $queryBuilder->commit(); + + // Rollback changes (data would be rejected) + $queryBuilder->rollback(); + } + ); ``` ### Get Built Query -Sometimes you may need to get the query string, its possible. -```PHP -$query = $qb->table('my_table')->where('id', '=', 3); -$queryObj = $query->getQuery(); + +Sometimes you may need to get the query string, it's possible. + +```php +$queryHandler = + $queryBuilder + ->table('my_table') + ->where('id', '=', 3) + ->getQuery(); ``` -`getQuery()` will return a query object, from this you can get sql, bindings or raw sql. +`getQuery()` will return a `QueryBuilderHandler` object. -```PHP -$queryObj->getSql(); -// Returns: SELECT * FROM my_table where `id` = ? +You can use this to get the SQL, bindings or raw SQL. + +```php +$queryHandler->getSql(); ``` -```PHP -$queryObj->getBindings(); -// Returns: array(3) + +Calling `getSql()` will return the SQL-query without any processing. + +```sql +SELECT * FROM my_table where `id` = ? ``` -```PHP -$queryObj->getRawSql(); -// Returns: SELECT * FROM my_table where `id` = 3 +You can easily get any bindings on the query by calling the `getBindings()`method. + +**Example:** + +```php +$queryHandler->getBindings(); +``` + +You can also get the raw SQL-query directly by calling the `getRawSql()` method. + +**Example:** + +```php +$queryHandler->getRawSql(); +``` + +Calling the `getRawSql()` method will return a query including bindings like this. + +```sql +SELECT * FROM my_table where `id` = 3 ``` ### Sub Queries and Nested Queries -Rarely but you may need to do sub queries or nested queries. Pixie is powerful enough to do this for you. You can create different query objects and use the `$qb->subQuery()` method. -```PHP -$subQuery = $qb->table('person_details')->select('details')->where('person_id', '=', 3); +Rarely but you may need to do sub queries or nested queries. Pixie is powerful enough to do this for you. You can create different query objects and use the `$queryBuilder->subQuery()` method. +```php +$subQuery = + $queryBuilder + ->table('person_details') + ->select('details') + ->where('person_id', '=', 3); + + +$query = + $queryBuilder + ->table('my_table') + ->select('my_table.*') + ->select( + $queryBuilder->subQuery($subQuery, 'table_alias1') + ); -$query = $qb->table('my_table') - ->select('my_table.*') - ->select($qb->subQuery($subQuery, 'table_alias1')); +$nestedQuery = + $queryBuilder + ->table( + $queryBuilder->subQuery($query, 'table_alias2') + ) + ->select('*'); -$nestedQuery = $qb->table($qb->subQuery($query, 'table_alias2'))->select('*'); -$nestedQuery->get(); +// Execute query +$result = $nestedQuery->get(); ``` This will produce a query like this: - SELECT * FROM (SELECT `cb_my_table`.*, (SELECT `details` FROM `cb_person_details` WHERE `person_id` = 3) as table_alias1 FROM `cb_my_table`) as table_alias2 +```sql +SELECT * +FROM + (SELECT `cb_my_table`.*, + + (SELECT `details` + FROM `cb_person_details` + WHERE `person_id` = 3) AS table_alias1 + FROM `cb_my_table`) AS table_alias2 +``` + +**NOTE:** -**NOTE:** Pixie doesn't use bindings for sub queries and nested queries. It quotes values with PDO's `quote()` method. +Pixie doesn't use bindings for sub queries and nested queries. It quotes values with PDO's `quote()` method. ### Get PDO Instance + If you need to get the PDO instance you can do so. -```PHP -$qb->pdo(); +```php +$queryBuilder->getConnection()->getPdoInstance(); ``` ### Fetch results as objects of specified class + Simply call `asObject` query's method. -```PHP -$qb->table('my_table')->asObject('SomeClass', array('ctor', 'args'))->first(); +```php +$queryBuilder + ->table('my_table') + ->asObject('SomeClass', array('ctor', 'args')) + ->first(); ``` Furthermore, you may fine-tune fetching mode by calling `setFetchMode` method. -```PHP -$qb->table('my_table')->setFetchMode(PDO::FETCH_COLUMN|PDO::FETCH_UNIQUE)->get(); +```php +$queryBuilder + ->table('my_table') + ->setFetchMode(PDO::FETCH_COLUMN|PDO::FETCH_UNIQUE) + ->get(); ``` ### Query Events + Pixie comes with powerful query events to supercharge your application. These events are like database triggers, you can perform some actions when an event occurs, for example you can hook `after-delete` event of a table and delete related data from another table. #### Available Events @@ -621,8 +858,8 @@ Pixie comes with powerful query events to supercharge your application. These ev #### Registering Events -```PHP -$qb->registerEvent('before-select', 'users', function($qb) +```php +$queryBuilder->registerEvent('before-select', 'users', function(QueryBuilderHandler $qb) { $qb->where('status', '!=', 'banned'); }); @@ -636,33 +873,46 @@ If you want the event to be performed when **any table is being queried**, provi **Other examples:** After inserting data into `my_table`, details will be inserted into another table -```PHP -$qb->registerEvent('after-insert', 'my_table', function($queryBuilder, $insertId) + +```php +$queryBuilder->registerEvent('after-insert', 'my_table', function(QueryBuilderHandler $qb, $insertId) { - $data = array('person_id' => $insertId, 'details' => 'Meh', 'age' => 5); - $queryBuilder->table('person_details')->insert($data); + $qb + ->table('person_details')->insert(array( + 'person_id' => $insertId, + 'details' => 'Meh', + 'age' => 5 + )); }); ``` Whenever data is inserted into `person_details` table, set the timestamp field `created_at`, so we don't have to specify it everywhere: -```PHP -$qb->registerEvent('after-insert', 'person_details', function($queryBuilder, $insertId) + +```php +$queryBuilder->registerEvent('after-insert', 'person_details', function(QueryBuilderHandler $qb, $insertId) { - $queryBuilder->table('person_details')->where('id', $insertId)->update(array('created_at' => date('Y-m-d H:i:s'))); + $qb + ->table('person_details') + ->where('id', $insertId) + ->update([ + 'created_at' => date('Y-m-d H:i:s') + ]); }); ``` After deleting from `my_table` delete the relations: -```PHP -$qb->registerEvent('after-delete', 'my_table', function($queryBuilder, $queryObject) + +```php +$queryBuilder->registerEvent('after-delete', 'my_table', function(QueryBuilderHandler $qb, $queryObject) { $bindings = $queryObject->getBindings(); - $queryBuilder->table('person_details')->where('person_id', $binding[0])->delete(); + $qb + ->table('person_details') + ->where('person_id', $binding[0]) + ->delete(); }); ``` - - Pixie passes the current instance of query builder as first parameter of your closure so you can build queries with this object, you can do anything like usual query builder (`QB`). If something other than `null` is returned from the `before-*` query handler, the value will be result of execution and DB will not be actually queried (and thus, corresponding `after-*` handler will not be called ether). @@ -675,8 +925,9 @@ Only on `after-*` events you get three parameters: **first** is the query builde - On `after-update` you get the [query object](#get-built-query) like `after-delete`. #### Removing Events -```PHP -$qb->removeEvent('event-name', 'table-name'); + +```php +$queryBuilder->removeEvent('event-name', 'table-name'); ``` #### Some Use Cases @@ -700,6 +951,8 @@ Here are some cases where Query Events can be extremely helpful: ___ If you find any typo then please edit and send a pull request. -© 2016 [Muhammad Usman](http://usman.it/), [Muhammad Usman](http://pecee.dk/) pecee. +© 2016 [Muhammad Usman](http://usman.it/), [Muhammad Usman] + +© 2016 Simon Sessingø - [Pecee.dk](http://pecee.dk/). -Licensed under MIT license. +Licensed under MIT license. \ No newline at end of file diff --git a/composer.json b/composer.json index 6c19420..6630b9c 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "pecee/pixie", "description": "Lightweight, fast query-builder for PHP based on Laravel Eloquent but with less overhead.", - "homepage": "https://github.com/skipperbent/pixie", + "homepage": "https://github.com/skipperbent/pecee-pixie", "keywords": [ "pecee", "querybuilder", diff --git a/src/Pecee/Pixie/Connection.php b/src/Pecee/Pixie/Connection.php index cdf7654..7e36f22 100644 --- a/src/Pecee/Pixie/Connection.php +++ b/src/Pecee/Pixie/Connection.php @@ -45,8 +45,8 @@ class Connection protected $eventHandler; /** - * @param string $adapter - * @param array $adapterConfig + * @param string $adapter + * @param array $adapterConfig * @param Container|null $container */ public function __construct($adapter, array $adapterConfig, Container $container = null) @@ -84,14 +84,13 @@ protected function connect() $this->setPdoInstance($pdo); // Preserve the first database connection with a static property - if (!static::$storedConnection) { + if (static::$storedConnection === null) { static::$storedConnection = $this; } } /** * @param \PDO $pdo - * * @return static */ public function setPdoInstance(\PDO $pdo) @@ -111,7 +110,6 @@ public function getPdoInstance() /** * @param string $adapter - * * @return static */ public function setAdapter($adapter) @@ -131,7 +129,6 @@ public function getAdapter() /** * @param array $adapterConfig - * * @return static */ public function setAdapterConfig(array $adapterConfig) diff --git a/src/Pecee/Pixie/ConnectionAdapters/BaseAdapter.php b/src/Pecee/Pixie/ConnectionAdapters/BaseAdapter.php index 72b7858..81266dc 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/BaseAdapter.php +++ b/src/Pecee/Pixie/ConnectionAdapters/BaseAdapter.php @@ -26,7 +26,6 @@ public function __construct(Container $container) /** * @param $config - * * @return \PDO */ public function connect($config) @@ -39,9 +38,8 @@ public function connect($config) } /** - * @param $config - * + * @param array $config * @return mixed */ - abstract protected function doConnect($config); + abstract protected function doConnect(array $config); } diff --git a/src/Pecee/Pixie/ConnectionAdapters/Mysql.php b/src/Pecee/Pixie/ConnectionAdapters/Mysql.php index 87fb58e..ed19207 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Mysql.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Mysql.php @@ -11,27 +11,26 @@ class Mysql extends BaseAdapter { /** * @param array $config - * * @return mixed * @throws Exception */ - protected function doConnect($config) + protected function doConnect(array $config) { - if (!extension_loaded('pdo_mysql')) { + if (extension_loaded('pdo_mysql') === false) { throw new Exception(sprintf('%s library not loaded', 'pdo_mysql')); } $connectionString = "mysql:dbname={$config['database']}"; - if (isset($config['host'])) { + if (isset($config['host']) === true) { $connectionString .= ";host={$config['host']}"; } - if (isset($config['port'])) { + if (isset($config['port']) === true) { $connectionString .= ";port={$config['port']}"; } - if (isset($config['unix_socket'])) { + if (isset($config['unix_socket']) === true) { $connectionString .= ";unix_socket={$config['unix_socket']}"; } diff --git a/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php b/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php index b6f420d..3dbee8c 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Pgsql.php @@ -11,19 +11,18 @@ class Pgsql extends BaseAdapter { /** * @param array $config - * * @return mixed * @throws Exception */ - protected function doConnect($config) + protected function doConnect(array $config) { - if (!extension_loaded('pdo_pgsql')) { + if (extension_loaded('pdo_pgsql') === false) { throw new Exception(sprintf('%s library not loaded', 'pdo_pgsql')); } $connectionString = "pgsql:host={$config['host']};dbname={$config['database']}"; - if (isset($config['port'])) { + if (isset($config['port']) === true) { $connectionString .= ";port={$config['port']}"; } @@ -32,11 +31,11 @@ protected function doConnect($config) [$connectionString, $config['username'], $config['password'], $config['options']] ); - if (isset($config['charset'])) { + if (isset($config['charset']) === true) { $connection->prepare("SET NAMES '{$config['charset']}'")->execute(); } - if (isset($config['schema'])) { + if (isset($config['schema']) === true) { $connection->prepare("SET search_path TO '{$config['schema']}'")->execute(); } diff --git a/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php b/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php index ff63e10..9b8454d 100644 --- a/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php +++ b/src/Pecee/Pixie/ConnectionAdapters/Sqlite.php @@ -10,14 +10,13 @@ class Sqlite extends BaseAdapter { /** - * @param $config - * + * @param array $config * @return mixed * @throws Exception */ - public function doConnect($config) + public function doConnect(array $config) { - if (!extension_loaded('pdo_sqlite')) { + if (extension_loaded('pdo_sqlite') === false) { throw new Exception(sprintf('%s library not loaded', 'pdo_sqlite')); } diff --git a/src/Pecee/Pixie/EventHandler.php b/src/Pecee/Pixie/EventHandler.php index d528b8f..ed82776 100644 --- a/src/Pecee/Pixie/EventHandler.php +++ b/src/Pecee/Pixie/EventHandler.php @@ -31,9 +31,8 @@ public function getEvents() } /** - * @param string $event + * @param string $event * @param string|null $table - * * @return callable|null */ public function getEvent($event, $table = null) @@ -45,8 +44,8 @@ public function getEvent($event, $table = null) } // Find event with * - if (isset($this->events[$table])) { - foreach ($this->events[$table] as $name => $e) { + if (isset($this->events[$table]) === true) { + foreach ((array)$this->events[$table] as $name => $e) { if (strpos($name, '*') !== false) { $name = substr($name, 0, strpos($name, '*')); if (stripos($event, $name) !== false) { @@ -60,10 +59,9 @@ public function getEvent($event, $table = null) } /** - * @param string $event - * @param string $table + * @param string $event + * @param string $table * @param \Closure $action - * * @return void */ public function registerEvent($event, $table, \Closure $action) @@ -76,7 +74,6 @@ public function registerEvent($event, $table, \Closure $action) /** * @param string $event * @param string $table - * * @return void */ public function removeEvent($event, $table = null) @@ -87,36 +84,33 @@ public function removeEvent($event, $table = null) /** * @param QueryBuilderHandler $queryBuilder - * @param string $event - * - * @return mixed + * @param string $event + * @return mixed|null */ public function fireEvents($queryBuilder, $event) { - /** - * @var $table string - */ - $statements = $queryBuilder->getStatements(); - $tables = isset($statements['tables']) ? $statements['tables'] : []; + $tables = isset($statements['tables']) ? $statements['tables'] : []; // Events added with :any will be fired in case of any table, // we are adding :any as a fake table at the beginning. array_unshift($tables, ':any'); + $handlerParams = func_get_args(); + unset($handlerParams[1]); + // Fire all events foreach ($tables as $table) { // Fire before events for :any table - if ($action = $this->getEvent($event, $table)) { + $action = $this->getEvent($event, $table); + if ($action !== null) { + // Make an event id, with event type and table $eventId = $event . $table; - // Fire event - $handlerParams = func_get_args(); - unset($handlerParams[1]); // we do not need $event - // Add to fired list + // Fire event and add to fired list $this->firedEvents[] = $eventId; - $result = call_user_func_array($action, $handlerParams); + $result = call_user_func_array($action, $handlerParams); if ($result !== null) { return $result; } diff --git a/src/Pecee/Pixie/Exception.php b/src/Pecee/Pixie/Exception.php index de6650f..0e19686 100644 --- a/src/Pecee/Pixie/Exception.php +++ b/src/Pecee/Pixie/Exception.php @@ -1,4 +1,5 @@ connection = $connection; - $this->container = $this->connection->getContainer(); + $this->container = $this->connection->getContainer(); } /** * Build select query string and bindings * - * @param $statements - * + * @param array $statements * @throws Exception * @return array */ - public function select($statements) + public function select(array $statements) { - if (!array_key_exists('selects', $statements)) { + if (array_key_exists('selects', $statements) === false) { $statements['selects'][] = '*'; } // From $fromEnabled = false; - $tables = ''; + $tables = ''; - if (isset($statements['tables'])) { + if (isset($statements['tables']) === true) { $tables = []; - foreach ($statements['tables'] as $table) { + foreach ((array)$statements['tables'] as $table) { $prefix = isset($statements['aliases'][$table]) ? $statements['aliases'][$table] : null; @@ -74,22 +73,23 @@ public function select($statements) $tables[] = $t; } - $tables = join(',', $tables); + $tables = implode(',', $tables); $fromEnabled = true; } - // Select + + // SELECT $selects = $this->arrayStr($statements['selects'], ', '); - // Wheres + // WHERE list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); - // Group bys - $groupBys = ''; - if (isset($statements['groupBys']) && $groupBys = $this->arrayStr($statements['groupBys'], ', ')) { + // GROUP BY + $groupBys = $this->arrayStr($statements['groupBys'], ', '); + if ($groupBys !== '' && isset($statements['groupBys']) === true) { $groupBys = 'GROUP BY ' . $groupBys; } - // Order bys + // ORDER BY $orderBys = ''; if (isset($statements['orderBys']) && is_array($statements['orderBys'])) { foreach ($statements['orderBys'] as $orderBy) { @@ -101,14 +101,14 @@ public function select($statements) } } - // Limit and offset - $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; + // LIMIT AND OFFSET + $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; $offset = isset($statements['offset']) ? 'OFFSET ' . $statements['offset'] : ''; - // Having + // HAVING list($havingCriteria, $havingBindings) = $this->buildCriteriaWithType($statements, 'havings', 'HAVING'); - // Joins + // JOINS $joinString = $this->buildJoin($statements); $sqlArray = [ @@ -138,15 +138,15 @@ public function select($statements) /** * Build just criteria part of the query * - * @param $statements + * @param array $statements * @param bool $bindValues - * * @return array + * @throws Exception */ - public function criteriaOnly($statements, $bindValues = true) + public function criteriaOnly(array $statements, $bindValues = true) { $sql = $bindings = []; - if (!isset($statements['criteria'])) { + if (isset($statements['criteria']) === false) { return compact('sql', 'bindings'); } @@ -158,14 +158,13 @@ public function criteriaOnly($statements, $bindValues = true) /** * Build a generic insert/ignore/replace query * - * @param array $statements - * @param array $data + * @param array $statements + * @param array $data * @param string $type - * * @return array * @throws Exception */ - private function doInsert($statements, array $data, $type) + private function doInsert(array $statements, array $data, $type) { $table = end($statements['tables']); @@ -176,7 +175,7 @@ private function doInsert($statements, array $data, $type) if ($value instanceof Raw) { $values[] = (string)$value; } else { - $values[] = '?'; + $values[] = '?'; $bindings[] = $value; } } @@ -189,13 +188,16 @@ private function doInsert($statements, array $data, $type) '(' . $this->arrayStr($values, ',', false) . ')', ]; - if (isset($statements['onduplicate'])) { + if (isset($statements['onduplicate']) === true) { + if (count($statements['onduplicate']) < 1) { throw new Exception('No data given.', 4); } + list($updateStatement, $updateBindings) = $this->getUpdateStatement($statements['onduplicate']); $sqlArray[] = 'ON DUPLICATE KEY UPDATE ' . $updateStatement; - $bindings = array_merge($bindings, $updateBindings); + $bindings = array_merge($bindings, $updateBindings); + } $sql = $this->concatenateQuery($sqlArray); @@ -204,43 +206,40 @@ private function doInsert($statements, array $data, $type) } /** - * Build Insert query + * Build insert query * - * @param $statements + * @param array $statements * @param array $data - * * @return array * @throws Exception */ - public function insert($statements, array $data) + public function insert(array $statements, array $data) { return $this->doInsert($statements, $data, 'INSERT'); } /** - * Build Insert Ignore query + * Build insert and ignore query * - * @param $statements + * @param array $statements * @param array $data - * * @return array * @throws Exception */ - public function insertIgnore($statements, array $data) + public function insertIgnore(array $statements, array $data) { return $this->doInsert($statements, $data, 'INSERT IGNORE'); } /** - * Build Insert Ignore query + * Build replace query * - * @param $statements + * @param array $statements * @param array $data - * * @return array * @throws Exception */ - public function replace($statements, array $data) + public function replace(array $statements, array $data) { return $this->doInsert($statements, $data, 'REPLACE'); } @@ -249,19 +248,21 @@ public function replace($statements, array $data) * Build fields assignment part of SET ... or ON DUBLICATE KEY UPDATE ... statements * * @param array $data - * * @return array */ - private function getUpdateStatement($data) + private function getUpdateStatement(array $data) { - $bindings = []; + $bindings = []; $statement = ''; foreach ($data as $key => $value) { + + $statement .= $this->wrapSanitizer($key) . '='; + if ($value instanceof Raw) { - $statement .= $this->wrapSanitizer($key) . '=' . $value . ','; + $statement .= $value . ','; } else { - $statement .= $this->wrapSanitizer($key) . '=?,'; + $statement .= '?,'; $bindings[] = $value; } } @@ -274,13 +275,12 @@ private function getUpdateStatement($data) /** * Build update query * - * @param $statements + * @param array $statements * @param array $data - * * @return array * @throws Exception */ - public function update($statements, array $data) + public function update(array $statements, array $data) { if (count($data) < 1) { throw new Exception('No data given.', 4); @@ -288,13 +288,13 @@ public function update($statements, array $data) $table = end($statements['tables']); - // Update statement + // UPDATE list($updateStatement, $bindings) = $this->getUpdateStatement($data); - // Wheres + // WHERE list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); - // Limit + // LIMIT $limit = isset($statements['limit']) ? 'LIMIT ' . $statements['limit'] : ''; $sqlArray = [ @@ -315,20 +315,19 @@ public function update($statements, array $data) /** * Build delete query * - * @param $statements - * + * @param array $statements * @return array * @throws Exception */ - public function delete($statements) + public function delete(array $statements) { $table = end($statements['tables']); - // Wheres + // WHERE list($whereCriteria, $whereBindings) = $this->buildCriteriaWithType($statements, 'wheres', 'WHERE'); $sqlArray = ['DELETE FROM', $this->wrapSanitizer($table), $whereCriteria]; - $sql = $this->concatenateQuery($sqlArray); + $sql = $this->concatenateQuery($sqlArray); $bindings = $whereBindings; return compact('sql', 'bindings'); @@ -339,20 +338,19 @@ public function delete($statements) * But it does wrap sanitizer and trims last glue * * @param array $pieces - * @param $glue - * @param bool $wrapSanitizer - * + * @param string $glue + * @param bool $wrapSanitizer * @return string */ - protected function arrayStr(array $pieces, $glue, $wrapSanitizer = true) + protected function arrayStr(array $pieces, $glue = ',', $wrapSanitizer = true) { $str = ''; foreach ($pieces as $key => $piece) { - if ($wrapSanitizer) { + if ($wrapSanitizer === true) { $piece = $this->wrapSanitizer($piece); } - if (!is_int($key)) { + if (is_int($key) === false) { $piece = ($wrapSanitizer ? $this->wrapSanitizer($key) : $key) . ' AS ' . $piece; } @@ -366,7 +364,6 @@ protected function arrayStr(array $pieces, $glue, $wrapSanitizer = true) * Join different part of queries with a space. * * @param array $pieces - * * @return string */ protected function concatenateQuery(array $pieces) @@ -383,91 +380,107 @@ protected function concatenateQuery(array $pieces) * Build generic criteria string and bindings from statements, like "a = b and c = ?" * * @param array $statements - * @param bool $bindValues - * + * @param bool $bindValues * @throws Exception - * * @return array */ - protected function buildCriteria($statements, $bindValues = true) + protected function buildCriteria(array $statements, $bindValues = true) { $criteria = ''; - $bindings = []; + $bindings = [[]]; + foreach ($statements as $statement) { - $key = $this->wrapSanitizer($statement['key']); + + $key = $this->wrapSanitizer($statement['key']); $value = $statement['value']; if ($value === null && $key instanceof \Closure) { - // We have a closure, a nested criteria - // Build a new NestedCriteria class, keep it by reference so any changes made - // in the closure should reflect here + /** + * We have a closure, a nested criteria + * Build a new NestedCriteria class, keep it by reference so any changes made in the closure should reflect here + */ + /* @var $nestedCriteria NestedCriteria */ $nestedCriteria = $this->container->build( NestedCriteria::class, [$this->connection] ); - //$nestedCriteria = &$nestedCriteria; - // Call the closure with our new nestedCriteria object $key($nestedCriteria); + // Get the criteria only query from the nestedCriteria object $queryObject = $nestedCriteria->getQuery('criteriaOnly', true); + // Merge the bindings we get from nestedCriteria object - $bindings = array_merge($bindings, $queryObject->getBindings()); + $bindings[] = $queryObject->getBindings(); + // Append the sql we get from the nestedCriteria object $criteria .= $statement['joiner'] . ' (' . $queryObject->getSql() . ') '; - } else if (is_array($value)) { + + continue; + } + if (is_array($value)) { // where_in or between like query $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator']; if ($statement['operator'] === 'BETWEEN') { - $bindings = array_merge($bindings, $statement['value']); + $bindings[] = (array)$statement['value']; $criteria .= ' ? AND ? '; } else { $valuePlaceholder = ''; - foreach ($statement['value'] as $subValue) { + foreach ((array)$statement['value'] as $subValue) { $valuePlaceholder .= '?, '; - $bindings[] = $subValue; + $bindings[] = (array)$subValue; } $valuePlaceholder = trim($valuePlaceholder, ', '); - $criteria .= ' (' . $valuePlaceholder . ') '; + $criteria .= ' (' . $valuePlaceholder . ') '; } - } else if ($value instanceof Raw) { + continue; + + } + + if ($value instanceof Raw) { $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} $value "; } else { - // Usual where like criteria - if (!$bindValues) { - // Specially for joins + // Usual where like criteria + if ($bindValues === false) { - // We are not binding values, lets sanitize then - $value = $this->wrapSanitizer($value); + // Specially for joins - we are not binding values, lets sanitize then + $value = $this->wrapSanitizer($value); $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $value . ' '; - } else if ($statement['key'] instanceof Raw) { + + continue; + } + + if ($statement['key'] instanceof Raw) { if ($statement['operator'] !== null) { - $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} ? "; - $bindings = array_merge($bindings, $statement['key']->getBindings()); - $bindings[] = $value; + $criteria .= "{$statement['joiner']} {$key} {$statement['operator']} ? "; + $bindings[] = (array)$statement['key']->getBindings(); + $bindings[] = (array)$value; } else { $criteria .= $statement['joiner'] . ' ' . $key . ' '; - $bindings = array_merge($bindings, $statement['key']->getBindings()); + $bindings[] = (array)$statement['key']->getBindings(); } - } else { - // For wheres + continue; - $valuePlaceholder = '?'; - $bindings[] = $value; - $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $valuePlaceholder . ' '; } + + // WHERE + $valuePlaceholder = '?'; + $bindings[] = (array)$value; + $criteria .= $statement['joiner'] . ' ' . $key . ' ' . $statement['operator'] . ' ' . $valuePlaceholder . ' '; } } + $bindings = array_merge(...$bindings); + // Clear all white spaces, and, or from beginning and white spaces from ending $criteria = preg_replace('/^(\s?AND ?|\s?OR ?)|\s$/i', '', $criteria); @@ -477,8 +490,7 @@ protected function buildCriteria($statements, $bindValues = true) /** * Wrap values with adapter's sanitizer like, '`' * - * @param $value - * + * @param string|Raw|\Closure $value * @return string */ public function wrapSanitizer($value) @@ -507,23 +519,23 @@ public function wrapSanitizer($value) /** * Build criteria string and binding with various types added, like WHERE and Having * - * @param $statements - * @param $key - * @param $type + * @param array $statements + * @param string $key + * @param string $type * @param bool $bindValues - * * @return array + * @throws Exception */ - protected function buildCriteriaWithType($statements, $key, $type, $bindValues = true) + protected function buildCriteriaWithType(array $statements, $key, $type, $bindValues = true) { $criteria = ''; $bindings = []; - if (isset($statements[$key])) { + if (isset($statements[$key]) === true) { // Get the generic/adapter agnostic criteria string from parent list($criteria, $bindings) = $this->buildCriteria($statements[$key], $bindValues); - if ($criteria) { + if ($criteria !== null) { $criteria = $type . ' ' . $criteria; } } @@ -534,27 +546,24 @@ protected function buildCriteriaWithType($statements, $key, $type, $bindValues = /** * Build join string * - * @param $statements - * - * @throws Exception + * @param array $statements * @return string + * @throws Exception */ - protected function buildJoin($statements) + protected function buildJoin(array $statements) { $sql = ''; - if (!array_key_exists('joins', $statements) || !is_array($statements['joins'])) { + if (array_key_exists('joins', $statements) === false || count($statements['joins']) === 0) { return $sql; } - foreach ($statements['joins'] as $joinArr) { - if (is_array($joinArr['table'])) { + foreach ((array)$statements['joins'] as $joinArr) { + if (is_array($joinArr['table']) === true) { list($mainTable, $aliasTable) = $joinArr['table']; $table = $this->wrapSanitizer($mainTable) . ' AS ' . $this->wrapSanitizer($aliasTable); } else { - $table = $joinArr['table'] instanceof Raw ? - (string)$joinArr['table'] : - $this->wrapSanitizer($joinArr['table']); + $table = $joinArr['table'] instanceof Raw ? (string)$joinArr['table'] : $this->wrapSanitizer($joinArr['table']); } /* @var $joinBuilder \Pecee\Pixie\QueryBuilder\QueryBuilderHandler */ diff --git a/src/Pecee/Pixie/QueryBuilder/Adapters/Mysql.php b/src/Pecee/Pixie/QueryBuilder/Adapters/Mysql.php index 81ef40c..9c7da48 100644 --- a/src/Pecee/Pixie/QueryBuilder/Adapters/Mysql.php +++ b/src/Pecee/Pixie/QueryBuilder/Adapters/Mysql.php @@ -1,4 +1,5 @@ addTablePrefix($key); - $value = $this->addTablePrefix($value); + $key = $this->addTablePrefix($key); + $value = $this->addTablePrefix($value); $this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner'); return $this; diff --git a/src/Pecee/Pixie/QueryBuilder/NestedCriteria.php b/src/Pecee/Pixie/QueryBuilder/NestedCriteria.php index 57184f8..1941d99 100644 --- a/src/Pecee/Pixie/QueryBuilder/NestedCriteria.php +++ b/src/Pecee/Pixie/QueryBuilder/NestedCriteria.php @@ -10,16 +10,15 @@ class NestedCriteria extends QueryBuilderHandler { /** - * @param string $key - * @param string|null $operator - * @param string|int|float|null $value - * @param string $joiner - * + * @param string|Raw|\Closure $key + * @param string|Raw|\Closure|null $operator + * @param string|Raw|\Closure|null $value + * @param string $joiner * @return static */ protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND') { - $key = $this->addTablePrefix($key); + $key = $this->addTablePrefix($key); $this->statements['criteria'][] = compact('key', 'operator', 'value', 'joiner'); return $this; diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index a6f75c4..f5086f5 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -28,7 +28,7 @@ class QueryBuilderHandler * @var array */ protected $statements = [ - 'groupBys' => array() + 'groupBys' => [], ]; /** @@ -57,10 +57,12 @@ class QueryBuilderHandler * @var array */ protected $fetchParameters = [\PDO::FETCH_OBJ]; + /** * @var string */ protected $adapter; + /** * @var array */ @@ -68,7 +70,6 @@ class QueryBuilderHandler /** * @param \Pecee\Pixie\Connection|null $connection - * * @throws \Pecee\Pixie\Exception */ public function __construct(Connection $connection = null) @@ -77,13 +78,13 @@ public function __construct(Connection $connection = null) throw new Exception('No database connection found.', 1); } - $this->connection = $connection; - $this->container = $this->connection->getContainer(); - $this->pdo = $this->connection->getPdoInstance(); - $this->adapter = $this->connection->getAdapter(); + $this->connection = $connection; + $this->container = $this->connection->getContainer(); + $this->pdo = $this->connection->getPdoInstance(); + $this->adapter = $this->connection->getAdapter(); $this->adapterConfig = $this->connection->getAdapterConfig(); - if (isset($this->adapterConfig['prefix'])) { + if (isset($this->adapterConfig['prefix']) === true) { $this->tablePrefix = $this->adapterConfig['prefix']; } @@ -97,11 +98,12 @@ public function __construct(Connection $connection = null) } /** - * Set the fetch mode + * Add fetch parameters to the PDO-query. * + * @param mixed $parameters ... * @return static */ - public function setFetchMode() + public function setFetchMode($parameters = null) { $this->fetchParameters = func_get_args(); @@ -112,8 +114,7 @@ public function setFetchMode() * Fetch query results as object of specified type * * @param string $className - * @param array $constructorArgs - * + * @param array $constructorArgs * @return QueryBuilderHandler */ public function asObject($className, array $constructorArgs = []) @@ -122,8 +123,9 @@ public function asObject($className, array $constructorArgs = []) } /** - * @param \Pecee\Pixie\Connection|null $connection + * Creates and returns new query. * + * @param \Pecee\Pixie\Connection|null $connection * @throws \Pecee\Pixie\Exception * @return static */ @@ -137,9 +139,10 @@ public function newQuery(Connection $connection = null) } /** - * @param string $sql - * @param array $bindings + * Performs query. * + * @param string $sql + * @param array $bindings * @return static */ public function query($sql, array $bindings = []) @@ -155,7 +158,6 @@ public function query($sql, array $bindings = []) * * @param string $table * @param string $alias - * * @return QueryBuilderHandler */ public function alias($table, $alias) @@ -167,14 +169,13 @@ public function alias($table, $alias) /** * Add or change table alias + * * Example: table AS alias * * @deprecated This method will be removed in the near future, please use QueryBuilderHandler::alias instead. - * @see QueryBuilderHandler::alias - * + * @see QueryBuilderHandler::alias * @param string $table * @param string $alias - * * @return QueryBuilderHandler */ public function prefix($table, $alias) @@ -183,25 +184,45 @@ public function prefix($table, $alias) } /** - * @param string $sql - * @param array $bindings + * Parses correct data-type for PDO parameter. * + * @param mixed $value + * @return int PDO-parameter type + */ + protected function parseDataType($value) + { + if (is_int($value) === true) { + return PDO::PARAM_INT; + } + + if (is_bool($value) === true) { + return PDO::PARAM_BOOL; + } + + return PDO::PARAM_STR; + } + + /** + * Execute statement + * + * @param string $sql + * @param array $bindings * @return array PDOStatement and execution time as float */ public function statement($sql, $bindings = []) { - /** - * @var $start float - */ - $start = microtime(true); + $start = microtime(true); + $pdoStatement = $this->pdo->prepare($sql); + foreach ($bindings as $key => $value) { $pdoStatement->bindValue( is_int($key) ? $key + 1 : $key, $value, - is_int($value) || is_bool($value) ? PDO::PARAM_INT : PDO::PARAM_STR + $this->parseDataType($value) ); } + $pdoStatement->execute(); return [$pdoStatement, microtime(true) - $start]; @@ -215,12 +236,9 @@ public function statement($sql, $bindings = []) */ public function get() { - /** - * @var $executionTime float - * @var $start float - */ - $queryObject = null; + $queryObject = null; $executionTime = 0; + if ($this->pdoStatement === null) { $queryObject = $this->getQuery('select'); list($this->pdoStatement, $executionTime) = $this->statement( @@ -231,8 +249,8 @@ public function get() $start = microtime(true); $this->fireEvents('before-select', $queryObject); - $result = call_user_func_array([$this->pdoStatement, 'fetchAll'], $this->fetchParameters); - $executionTime += microtime(true) - $start; + $result = call_user_func_array([$this->pdoStatement, 'fetchAll'], $this->fetchParameters); + $executionTime += microtime(true) - $start; $this->pdoStatement = null; $this->fireEvents('after-select', $queryObject, $result, $executionTime); @@ -240,44 +258,42 @@ public function get() } /** - * Get first row + * Returns the first row * * @throws Exception * @return \stdClass|null */ public function first() { - $this->limit(1); - $result = $this->get(); + $result = $this->limit(1)->get(); - return empty($result) ? null : $result[0]; + return ($result !== null && count($result) > 0) ? $result[0] : null; } /** - * @param string $fieldName - * @param string|int|float $value + * Find all by field name and value * + * @param string $fieldName + * @param string|int|float $value + * @throws Exception * @return \stdClass[] */ public function findAll($fieldName, $value) { - $this->where($fieldName, '=', $value); - - return $this->get(); + return $this->where($fieldName, '=', $value)->get(); } /** - * @param string|int|float $value - * @param string $fieldName + * Find by value and field name. * + * @param string|int|float $value + * @param string $fieldName * @throws Exception * @return null|\stdClass */ public function find($value, $fieldName = 'id') { - $this->where($fieldName, '=', $value); - - return $this->first(); + return $this->where($fieldName, '=', $value)->first(); } /** @@ -293,15 +309,16 @@ public function count() unset($this->statements['orderBys'], $this->statements['limit'], $this->statements['offset']); - $count = $this->aggregate('count'); + $count = $this->aggregate('count'); $this->statements = $originalStatements; return $count; } /** - * @param string $type + * Performs special queries like COUNT, SUM etc based on the current query. * + * @param string $type * @throws Exception * @return int */ @@ -309,19 +326,20 @@ protected function aggregate($type) { // Get the current selects $mainSelects = isset($this->statements['selects']) ? $this->statements['selects'] : null; + // Replace select with a scalar value like `count` - $this->statements['selects'] = [$this->raw($type . '(*) as field')]; - $row = $this->get(); + $this->statements['selects'] = [$this->raw($type . '(*) AS `field`')]; + $row = $this->get(); // Set the select as it was - if ($mainSelects) { + if ($mainSelects !== null) { $this->statements['selects'] = $mainSelects; } else { unset($this->statements['selects']); } - if (isset($row[0])) { - if (is_array($row[0])) { + if (isset($row[0]) === true) { + if (is_array($row[0]) === true) { return (int)$row[0]['field']; } if (is_object($row[0])) { @@ -333,15 +351,25 @@ protected function aggregate($type) } /** - * @param string $type - * @param array|bool $dataToBePassed + * Returns Query-object. * + * @param string $type + * @param array|bool $dataToBePassed * @return QueryObject * @throws Exception */ public function getQuery($type = 'select', $dataToBePassed = []) { - $allowedTypes = ['select', 'insert', 'insertignore', 'replace', 'delete', 'update', 'criteriaonly']; + $allowedTypes = [ + 'select', + 'insert', + 'insertignore', + 'replace', + 'delete', + 'update', + 'criteriaonly', + ]; + if (in_array(strtolower($type), $allowedTypes, true) === false) { throw new Exception($type . ' is not a known type.', 2); } @@ -355,33 +383,34 @@ public function getQuery($type = 'select', $dataToBePassed = []) } /** - * @param QueryBuilderHandler $queryBuilder - * @param string|null $alias + * Performs new sub-query. + * Call this method when you want to add a new sub-query in your where etc. * + * @param QueryBuilderHandler $queryBuilder + * @param string|null $alias * @throws Exception * @return Raw */ public function subQuery(QueryBuilderHandler $queryBuilder, $alias = null) { $sql = '(' . $queryBuilder->getQuery()->getRawSql() . ')'; - if ($alias) { - $sql = $sql . ' as ' . $alias; + if ($alias !== null) { + $sql = $sql . ' AS ' . $this->adapterInstance->wrapSanitizer($alias); } return $queryBuilder->raw($sql); } /** - * @param array $data - * @param string $type + * Performs insert * + * @param array $data + * @param string $type * @throws Exception * @return array|string */ private function doInsert($data, $type) { - $queryObject = null; - // If first value is not an array - it's not a batch insert if (is_array(current($data)) === false) { $queryObject = $this->getQuery($type, $data); @@ -391,27 +420,30 @@ private function doInsert($data, $type) $return = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; $this->fireEvents('after-insert', $queryObject, $return, $executionTime); - } else { - // Its a batch insert - $return = []; - foreach ($data as $subData) { - $queryObject = $this->getQuery($type, $subData); + return $return; + } - $this->fireEvents('before-insert', $queryObject); - list($result, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); - $result = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; - $this->fireEvents('after-insert', $queryObject, $result, $executionTime); + // Perform batch insert - $return[] = $result; - } + $return = []; + foreach ($data as $subData) { + $queryObject = $this->getQuery($type, $subData); + + $this->fireEvents('before-insert', $queryObject); + list($result, $executionTime) = $this->statement($queryObject->getSql(), $queryObject->getBindings()); + $result = $result->rowCount() === 1 ? $this->pdo->lastInsertId() : null; + $this->fireEvents('after-insert', $queryObject, $result, $executionTime); + + $return[] = $result; } return $return; } /** - * @param array $data + * Insert key/value array * + * @param array $data * @throws Exception * @return array|string */ @@ -421,8 +453,9 @@ public function insert($data) } /** - * @param array $data + * Insert with ignore key/value array * + * @param array $data * @throws Exception * @return array|string */ @@ -432,8 +465,9 @@ public function insertIgnore($data) } /** - * @param array $data + * Replace key/value array * + * @param array $data * @throws Exception * @return array|string */ @@ -443,16 +477,16 @@ public function replace($data) } /** - * @param array $data + * Update key/value array * + * @param array $data * @throws Exception * @return \PDOStatement */ public function update($data) { /** - * @var $response \PDOStatement - * @var $executionTime float + * @var $response \PDOStatement */ $queryObject = $this->getQuery('update', $data); @@ -465,13 +499,15 @@ public function update($data) } /** - * @param array $data + * Update or insert key/value array * + * @param array $data * @return array|\PDOStatement|string + * @throws Exception */ public function updateOrInsert($data) { - if ($this->first()) { + if ($this->first() !== null) { return $this->update($data); } @@ -479,8 +515,9 @@ public function updateOrInsert($data) } /** - * @param string $data + * Add on duplicate key statement. * + * @param string $data * @return static */ public function onDuplicateKeyUpdate($data) @@ -491,14 +528,14 @@ public function onDuplicateKeyUpdate($data) } /** + * Forms delete on the current query. + * * @return \PDOStatement + * @throws Exception */ public function delete() { - /** - * @var $response \PDOStatement - * @var $executionTime float - */ + /* @var $response \PDOStatement */ $queryObject = $this->getQuery('delete'); $this->fireEvents('before-delete', $queryObject); @@ -510,29 +547,30 @@ public function delete() } /** - * @param $tables string|string[] Single table or multiple tables as an array or as multiple parameters + * Sets the table that the query is using * + * @param string|array $tables Single table or multiple tables as an array or as multiple parameters * @throws Exception * @return static */ public function table($tables) { - if (!is_array($tables)) { - // because a single table is converted to an array anyways, - // this makes sense. + if (is_array($tables) === false) { + // Because a single table is converted to an array anyways, this makes sense. $tables = func_get_args(); } $instance = new static($this->connection); - $tables = $this->addTablePrefix($tables, false); + $tables = $this->addTablePrefix($tables, false); $instance->addStatement('tables', $tables); return $instance; } /** - * @param string|string[] $tables + * Adds FROM statement to the current query. * + * @param string|array $tables * @return static */ public function from($tables) @@ -548,13 +586,18 @@ public function from($tables) } /** - * @param string|string[] $fields,... + * Adds fields to select on the current query (defaults is all). + * You can use key/value array to create alias. + * Sub-queries and raw-objects are also supported. * + * Example: ['field' => 'alias'] will become `field` AS `alias` + * + * @param string|array $fields,... * @return static */ public function select($fields) { - if (!is_array($fields)) { + if (is_array($fields) === false) { $fields = func_get_args(); } @@ -565,8 +608,9 @@ public function select($fields) } /** - * @param array|string $fields + * Performs select distinct on the current query. * + * @param string|Raw|\Closure|array $fields * @return static */ public function selectDistinct($fields) @@ -578,17 +622,18 @@ public function selectDistinct($fields) } /** - * @param string|array $field + * Adds GROUP BY to the current query. * + * @param string|Raw|\Closure|array $field * @return static */ public function groupBy($field) { - if (!$field instanceof Raw) { + if (($field instanceof Raw) === false) { $field = $this->addTablePrefix($field); } - if(is_array($field) === true) { + if (is_array($field) === true) { $this->statements['groupBys'] = array_merge($this->statements['groupBys'], $field); } else { $this->statements['groupBys'][] = $field; @@ -598,27 +643,31 @@ public function groupBy($field) } /** - * @param string|array $fields - * @param string $defaultDirection + * Adds ORDER BY statement to the current query. * + * @param string|Raw|\Closure|array $fields + * @param string $defaultDirection * @return static */ public function orderBy($fields, $defaultDirection = 'ASC') { - if (!is_array($fields)) { + if (is_array($fields) === false) { $fields = [$fields]; } foreach ((array)$fields as $key => $value) { $field = $key; - $type = $value; - if (is_int($key)) { + $type = $value; + + if (is_int($key) === true) { $field = $value; - $type = $defaultDirection; + $type = $defaultDirection; } - if (!$field instanceof Raw) { + + if (($field instanceof Raw) === false) { $field = $this->addTablePrefix($field); } + $this->statements['orderBys'][] = compact('field', 'type'); } @@ -626,8 +675,9 @@ public function orderBy($fields, $defaultDirection = 'ASC') } /** - * @param int $limit + * Adds LIMIT statement to the current query. * + * @param int $limit * @return static */ public function limit($limit) @@ -638,8 +688,9 @@ public function limit($limit) } /** - * @param int $offset + * Adds OFFSET statement to the current query. * + * @param int $offset * @return static $this */ public function offset($offset) @@ -650,26 +701,28 @@ public function offset($offset) } /** - * @param string $key + * Adds HAVING statement to the current query. + * + * @param string|Raw|\Closure $key * @param string|mixed $operator * @param string|mixed $value - * @param string $joiner - * + * @param string $joiner * @return static */ public function having($key, $operator, $value, $joiner = 'AND') { - $key = $this->addTablePrefix($key); + $key = $this->addTablePrefix($key); $this->statements['havings'][] = compact('key', 'operator', 'value', 'joiner'); return $this; } /** - * @param string $key - * @param string|mixed $operator - * @param string|mixed $value + * Adds OR HAVING statement to the current query. * + * @param string|Raw|\Closure $key + * @param string|Raw|\Closure $operator + * @param mixed|Raw|\Closure|null $value * @return static */ public function orHaving($key, $operator, $value) @@ -678,21 +731,22 @@ public function orHaving($key, $operator, $value) } /** - * @param string $key - * @param string|mixed $operator - * @param string|mixed $value + * Adds WHERE statement to the current query. * + * @param string|Raw|\Closure $key + * @param string|Raw|\Closure|null $operator + * @param mixed|Raw|\Closure|null $value * @return static */ public function where($key, $operator = null, $value = null) { // If two params are given then assume operator is = if (func_num_args() === 2) { - $value = $operator; + $value = $operator; $operator = '='; } - if (is_bool($value)) { + if (is_bool($value) === true) { $value = (int)$value; } @@ -700,17 +754,18 @@ public function where($key, $operator = null, $value = null) } /** - * @param string $key - * @param string|mixed $operator - * @param string|mixed $value + * Adds OR WHERE statement to the current query. * + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param mixed|Raw|\Closure|null $value * @return static */ public function orWhere($key, $operator = null, $value = null) { // If two params are given then assume operator is = if (func_num_args() === 2) { - $value = $operator; + $value = $operator; $operator = '='; } @@ -718,17 +773,18 @@ public function orWhere($key, $operator = null, $value = null) } /** - * @param string $key - * @param string|mixed $operator - * @param string|mixed $value + * Adds WHERE NOT statement to the current query. * + * @param string|Raw|\Closure $key + * @param string|array|Raw|\Closure|null $operator + * @param mixed|Raw|\Closure|null $value * @return static */ public function whereNot($key, $operator = null, $value = null) { // If two params are given then assume operator is = if (func_num_args() === 2) { - $value = $operator; + $value = $operator; $operator = '='; } @@ -736,17 +792,18 @@ public function whereNot($key, $operator = null, $value = null) } /** - * @param string $key - * @param string|mixed $operator - * @param string|mixed $value + * Adds OR WHERE NOT statement to the current query. * + * @param string|Raw|\Closure $key + * @param string|array|Raw|\Closure|null $operator + * @param mixed|Raw|\Closure|null $value * @return static */ public function orWhereNot($key, $operator = null, $value = null) { // If two params are given then assume operator is = if (func_num_args() === 2) { - $value = $operator; + $value = $operator; $operator = '='; } @@ -754,9 +811,10 @@ public function orWhereNot($key, $operator = null, $value = null) } /** - * @param string $key - * @param string[]|int[]|float[] $values + * Adds WHERE IN statement to the current query. * + * @param string|Raw|\Closure $key + * @param array|Raw|\Closure $values * @return static */ public function whereIn($key, $values) @@ -765,9 +823,10 @@ public function whereIn($key, $values) } /** - * @param string $key - * @param string[]|integer[]|float[] $values + * Adds OR WHERE NOT IN statement to the current query. * + * @param string|Raw|\Closure $key + * @param array|Raw|\Closure $values * @return static */ public function whereNotIn($key, $values) @@ -776,9 +835,10 @@ public function whereNotIn($key, $values) } /** - * @param string $key - * @param string[]|integer[]|float[] $values + * Adds OR WHERE IN statement to the current query. * + * @param string|Raw|\Closure $key + * @param array|Raw|\Closure $values * @return static */ public function orWhereIn($key, $values) @@ -787,9 +847,10 @@ public function orWhereIn($key, $values) } /** - * @param string $key - * @param string[]|integer[]|float[] $values + * Adds or WHERE NOT IN statement to the current query. * + * @param string|Raw|\Closure $key + * @param array|Raw|\Closure $values * @return static */ public function orWhereNotIn($key, $values) @@ -798,10 +859,11 @@ public function orWhereNotIn($key, $values) } /** - * @param string $key + * Adds WHERE BETWEEN statement to the current query. + * + * @param string|Raw|\Closure $key * @param string|integer|float $valueFrom * @param string|integer|float $valueTo - * * @return static */ public function whereBetween($key, $valueFrom, $valueTo) @@ -810,10 +872,11 @@ public function whereBetween($key, $valueFrom, $valueTo) } /** - * @param string $key + * Adds OR WHERE BETWEEN statement to the current query. + * + * @param string|Raw|\Closure $key * @param string|integer|float $valueFrom * @param string|integer|float $valueTo - * * @return static */ public function orWhereBetween($key, $valueFrom, $valueTo) @@ -822,8 +885,9 @@ public function orWhereBetween($key, $valueFrom, $valueTo) } /** - * @param string $key + * Adds WHERE NULL statement to the current query. * + * @param string|Raw|\Closure $key * @return QueryBuilderHandler */ public function whereNull($key) @@ -832,8 +896,9 @@ public function whereNull($key) } /** - * @param string $key + * Adds WHERE NOT NULL statement to the current query. * + * @param string|Raw|\Closure $key * @return QueryBuilderHandler */ public function whereNotNull($key) @@ -842,8 +907,9 @@ public function whereNotNull($key) } /** - * @param string $key + * Adds OR WHERE NULL statement to the current query. * + * @param string|Raw|\Closure $key * @return QueryBuilderHandler */ public function orWhereNull($key) @@ -852,8 +918,9 @@ public function orWhereNull($key) } /** - * @param string $key + * Adds OR WHERE NOT NULL statement to the current query. * + * @param string|Raw|\Closure $key * @return QueryBuilderHandler */ public function orWhereNotNull($key) @@ -862,10 +929,11 @@ public function orWhereNotNull($key) } /** - * @param string $key + * Handles WHERE NULL statements. + * + * @param string|Raw|\Closure $key * @param string $prefix * @param string $operator - * * @return mixed */ protected function whereNullHandler($key, $prefix = '', $operator = '') @@ -876,12 +944,13 @@ protected function whereNullHandler($key, $prefix = '', $operator = '') } /** - * @param string $table - * @param string $key - * @param string|null $operator - * @param string|null $value - * @param string $type + * Adds new JOIN statement to the current query. * + * @param string|Raw|\Closure $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure $value + * @param string $type * @return static */ public function join($table, $key, $operator = null, $value = null, $type = 'inner') @@ -892,14 +961,17 @@ public function join($table, $key, $operator = null, $value = null, $type = 'inn }; } - // Build a new JoinBuilder class, keep it by reference so any changes made - // in the closure should reflect here + /** + * Build a new JoinBuilder class, keep it by reference so any changes made + * in the closure should reflect here + */ + $joinBuilder = $this->container->build(JoinBuilder::class, [$this->connection]); - //$joinBuilder = &$joinBuilder; // Call the closure with our new joinBuilder object $key($joinBuilder); $table = $this->addTablePrefix($table, false); + // Get the criteria only query from the joinBuilder object $this->statements['joins'][] = compact('type', 'table', 'joinBuilder'); @@ -907,10 +979,9 @@ public function join($table, $key, $operator = null, $value = null, $type = 'inn } /** - * Runs a transaction + * Performs the transaction * * @param \Closure $callback - * * @return static */ public function transaction(\Closure $callback) @@ -925,8 +996,7 @@ public function transaction(\Closure $callback) // Call closure $callback($transaction); - // If no errors have been thrown or the transaction wasn't completed within - // the closure, commit the changes + // If no errors have been thrown or the transaction wasn't completed within the closure, commit the changes $this->pdo->commit(); return $this; @@ -942,37 +1012,40 @@ public function transaction(\Closure $callback) } /** - * @param string $table - * @param string $key - * @param string|null $operator - * @param string|null $value + * Adds new right join statement to the current query. * + * @param string|Raw|\Closure $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure|null $value * @return static */ - public function leftJoin($table, $key, $operator = null, $value = null) + public function rightJoin($table, $key, $operator = null, $value = null) { - return $this->join($table, $key, $operator, $value, 'left'); + return $this->join($table, $key, $operator, $value, 'right'); } /** - * @param string $table - * @param string $key - * @param string|null $operator - * @param string|null $value + * Adds new LEFT JOIN statement to the current query. * + * @param string|Raw|\Closure $table + * @param string|Raw|\Closure $key + * @param string|null $operator + * @param string|Raw|\Closure|null $value * @return static */ - public function rightJoin($table, $key, $operator = null, $value = null) + public function leftJoin($table, $key, $operator = null, $value = null) { - return $this->join($table, $key, $operator, $value, 'right'); + return $this->join($table, $key, $operator, $value, 'left'); } /** - * @param string $table - * @param string $key - * @param string|mixed $operator - * @param string|mixed $value + * Adds new INNER JOIN statement to the current query. * + * @param string|Raw|\Closure $table + * @param string|Raw|\Closure $key + * @param string|mixed|null $operator + * @param string|Raw|\Closure|null $value * @return static */ public function innerJoin($table, $key, $operator = null, $value = null) @@ -981,11 +1054,14 @@ public function innerJoin($table, $key, $operator = null, $value = null) } /** - * Add a raw query + * Adds a raw string to the current query. + * This query will be ignored from any parsing or formatting by the Query builder + * and should be used in conjuction with other statements in the query. * - * @param string $value - * @param array $bindings + * For example: $qb->where('result', '>', $qb->raw('COUNT(`score`))); * + * @param string $value + * @param array $bindings * @return Raw */ public function raw($value, $bindings = []) @@ -1004,8 +1080,9 @@ public function pdo() } /** - * @param Connection $connection + * Set connection object * + * @param Connection $connection * @return static */ public function setConnection(Connection $connection) @@ -1016,6 +1093,8 @@ public function setConnection(Connection $connection) } /** + * Get connection object + * * @return Connection */ public function getConnection() @@ -1024,16 +1103,17 @@ public function getConnection() } /** - * @param string $key - * @param string|mixed $operator - * @param string|integer|float $value - * @param string $joiner + * Handles where statements * + * @param string|Raw|\Closure $key + * @param string|Raw|\Closure|null $operator + * @param string|Raw|\Closure|null $value + * @param string $joiner * @return static */ protected function whereHandler($key, $operator = null, $value = null, $joiner = 'AND') { - $key = $this->addTablePrefix($key); + $key = $this->addTablePrefix($key); $this->statements['wheres'][] = compact('key', 'operator', 'value', 'joiner'); return $this; @@ -1042,9 +1122,8 @@ protected function whereHandler($key, $operator = null, $value = null, $joiner = /** * Add table prefix (if given) on given string. * - * @param $values + * @param string|array|Raw|\Closure $values * @param bool $tableFieldMix If we have mixes of field and table names with a "." - * * @return array|string */ public function addTablePrefix($values, $tableFieldMix = true) @@ -1054,11 +1133,12 @@ public function addTablePrefix($values, $tableFieldMix = true) } // $value will be an array and we will add prefix to all table names - // If supplied value is not an array then make it one + $single = false; - if (!is_array($values)) { + if (is_array($values) === false) { $values = [$values]; + // We had single value, so should return a single value $single = true; } @@ -1074,6 +1154,7 @@ public function addTablePrefix($values, $tableFieldMix = true) // If key is not integer, it is likely a alias mapping, so we need to change prefix target $target = &$value; + if (is_int($key) === false) { $target = &$key; } @@ -1090,8 +1171,10 @@ public function addTablePrefix($values, $tableFieldMix = true) } /** + * Add new statement to statement-list + * * @param string $key - * @param mixed $value + * @param mixed $value */ protected function addStatement($key, $value) { @@ -1103,45 +1186,50 @@ protected function addStatement($key, $value) } /** - * @param string $event - * @param string|null $table + * Get event by event name * + * @param string $name + * @param string|null $table * @return \Closure|null */ - public function getEvent($event, $table = null) + public function getEvent($name, $table = null) { - return $this->connection->getEventHandler()->getEvent($event, $table); + return $this->connection->getEventHandler()->getEvent($name, $table); } /** - * @param string $event - * @param string|null $table - * @param \Closure $action + * Register new event * + * @param string $name + * @param string|null $table + * @param \Closure $action * @return void */ - public function registerEvent($event, $table = null, \Closure $action) + public function registerEvent($name, $table = null, \Closure $action) { - $this->connection->getEventHandler()->registerEvent($event, $table, $action); + $this->connection->getEventHandler()->registerEvent($name, $table, $action); } /** - * @param string $event - * @param string|null $table + * Remove event by event-name and/or table * + * @param string $name + * @param string|null $table * @return void */ - public function removeEvent($event, $table = null) + public function removeEvent($name, $table = null) { - $this->connection->getEventHandler()->removeEvent($event, $table); + $this->connection->getEventHandler()->removeEvent($name, $table); } /** - * @param $event + * Fires event by given event name * - * @return mixed + * @param string $name + * @param ... $parameters + * @return mixed|null */ - public function fireEvents($event) + public function fireEvents($name, $parameters = null) { $params = func_get_args(); array_unshift($params, $this); @@ -1150,6 +1238,8 @@ public function fireEvents($event) } /** + * Returns statements + * * @return array */ public function getStatements() diff --git a/src/Pecee/Pixie/QueryBuilder/QueryObject.php b/src/Pecee/Pixie/QueryBuilder/QueryObject.php index 6874e94..cdfaf51 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryObject.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryObject.php @@ -29,14 +29,14 @@ class QueryObject * QueryObject constructor. * * @param string $sql - * @param array $bindings - * @param \PDO $pdo + * @param array $bindings + * @param \PDO $pdo */ public function __construct($sql, array $bindings, \PDO $pdo) { - $this->sql = (string)$sql; + $this->sql = (string)$sql; $this->bindings = $bindings; - $this->pdo = $pdo; + $this->pdo = $pdo; } /** @@ -57,7 +57,6 @@ public function getBindings() /** * Get the raw/bound sql - * * @return string */ public function getRawSql() @@ -72,34 +71,32 @@ public function getRawSql() * * Reference: http://stackoverflow.com/a/1376838/656489 * - * @param string $query The sql query with parameter placeholders - * @param array $params The array of substitution parameters - * + * @param string $query The sql query with parameter placeholders + * @param array $params The array of substitution parameters * @return string The interpolated query */ protected function interpolateQuery($query, $params) { - $keys = []; + $keys = []; $values = $params; # build a regular expression for each parameter foreach ($params as $key => $value) { - if (is_string($key)) { - $keys[] = '/:' . $key . '/'; - } else { - $keys[] = '/[?]/'; - } + $keys[] = '/' . (is_string($key) ? ':' . $key : '[?]') . '/'; - if (is_string($value)) { + if (is_string($value) === true) { $values[$key] = $this->pdo->quote($value); + continue; } - if (is_array($value)) { - $values[$key] = join(',', $this->pdo->quote($value)); + if (is_array($value) === true) { + $values[$key] = implode(',', $this->pdo->quote($value)); + continue; } if ($value === null) { $values[$key] = 'NULL'; + continue; } } diff --git a/src/Pecee/Pixie/QueryBuilder/Transaction.php b/src/Pecee/Pixie/QueryBuilder/Transaction.php index f0d04fe..ce86f82 100644 --- a/src/Pecee/Pixie/QueryBuilder/Transaction.php +++ b/src/Pecee/Pixie/QueryBuilder/Transaction.php @@ -1,4 +1,5 @@ pdo->commit(); - throw new TransactionHaltException(); + throw new TransactionHaltException('Commit'); } /** @@ -26,6 +27,6 @@ public function commit() public function rollback() { $this->pdo->rollBack(); - throw new TransactionHaltException(); + throw new TransactionHaltException('Rollback'); } } diff --git a/src/Pecee/Pixie/QueryBuilder/TransactionHaltException.php b/src/Pecee/Pixie/QueryBuilder/TransactionHaltException.php index 8b0f191..8c42b6e 100644 --- a/src/Pecee/Pixie/QueryBuilder/TransactionHaltException.php +++ b/src/Pecee/Pixie/QueryBuilder/TransactionHaltException.php @@ -1,4 +1,5 @@ builder->select($this->builder->subQuery($subQuery1, 'row1'), $this->builder->subQuery($subQuery2, 'row2'))->first(); - $this->assertEquals('SELECT (SELECT COUNT(*) FROM `cb_mail`) as row1, (SELECT COUNT(*) FROM `cb_event_message`) as row2 LIMIT 1', $count); + $this->assertEquals('SELECT (SELECT COUNT(*) FROM `cb_mail`) AS `row1`, (SELECT COUNT(*) FROM `cb_event_message`) AS `row2` LIMIT 1', $count); } diff --git a/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php b/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php index 36e5a46..182aea9 100644 --- a/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php +++ b/tests/Pecee/Pixie/QueryBuilderBehaviorTest.php @@ -49,7 +49,7 @@ public function testSelectQuery() $query = $this->builder->table('my_table') ->select('my_table.*') - ->select(array($this->builder->raw('count(cb_my_table.id) as tot'), $this->builder->subQuery($subQuery, 'pop'))) + ->select(array($this->builder->raw('count(cb_my_table.id) AS `tot`'), $this->builder->subQuery($subQuery, 'pop'))) ->where('value', '=', 'Ifrah') ->whereNot('my_table.id', -1) ->orWhereNot('my_table.id', -2) @@ -69,7 +69,7 @@ public function testSelectQuery() ; $nestedQuery = $this->builder->table($this->builder->subQuery($query, 'bb'))->select('*'); - $this->assertEquals("SELECT * FROM (SELECT `cb_my_table`.*, count(cb_my_table.id) as tot, (SELECT `details` FROM `cb_person_details` WHERE `person_id` = 3) as pop FROM `cb_my_table` INNER JOIN `cb_person_details` ON `cb_person_details`.`person_id` = `cb_my_table`.`id` WHERE `value` = 'Ifrah' AND NOT `cb_my_table`.`id` = -1 OR NOT `cb_my_table`.`id` = -2 OR `cb_my_table`.`id` IN (1, 2) GROUP BY `value`, `cb_my_table`.`id`, `cb_person_details`.`id` HAVING `tot` < 2 ORDER BY `cb_my_table`.`id` DESC, `value` ASC LIMIT 1 OFFSET 0) as bb" + $this->assertEquals("SELECT * FROM (SELECT `cb_my_table`.*, count(cb_my_table.id) AS `tot`, (SELECT `details` FROM `cb_person_details` WHERE `person_id` = 3) AS `pop` FROM `cb_my_table` INNER JOIN `cb_person_details` ON `cb_person_details`.`person_id` = `cb_my_table`.`id` WHERE `value` = 'Ifrah' AND NOT `cb_my_table`.`id` = -1 OR NOT `cb_my_table`.`id` = -2 OR `cb_my_table`.`id` IN (1, 2) GROUP BY `value`, `cb_my_table`.`id`, `cb_person_details`.`id` HAVING `tot` < 2 ORDER BY `cb_my_table`.`id` DESC, `value` ASC LIMIT 1 OFFSET 0) AS `bb`" , $nestedQuery->getQuery()->getRawSql()); } From 5262bf24d5d2fc93e5096f5836a638a3b95c65c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sat, 25 Nov 2017 18:37:20 +0100 Subject: [PATCH 4/5] Development - Added License to README. - More optimizations. --- LICENSE | 2 +- README.md | 58 +++++++++++++++---- .../QueryBuilder/Adapters/BaseAdapter.php | 2 +- src/Pecee/Pixie/QueryBuilder/JoinBuilder.php | 2 +- .../QueryBuilder/QueryBuilderHandler.php | 27 +++++---- src/Pecee/Pixie/QueryBuilder/QueryObject.php | 2 +- src/Pecee/Pixie/QueryBuilder/Raw.php | 2 +- 7 files changed, 68 insertions(+), 27 deletions(-) diff --git a/LICENSE b/LICENSE index f79f2c5..3d6c887 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016 Muhammad Usman, pecee +Copyright (c) 2016 Muhammad Usman, Simon Sessingø (Pecee) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 5c074b9..6ef3496 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,30 @@ # pecee/pixie: Advanced lightweight querybuilder -A lightweight, expressive, framework agnostic query builder for PHP it can also be referred as a Database Abstraction Layer. Pixie supports MySQL, SQLite and PostgreSQL and it takes care of query sanitization, table prefixing and many other things with a unified API. At least PHP 5.3 is required. +A lightweight, expressive, framework agnostic query builder for PHP it can also be referred as a Database Abstraction Layer. Pixie supports MySQL, SQLite and PostgreSQL and it takes care of query sanitization, table prefixing and many other things with a unified API. At least PHP 5.4 is required. -The syntax is quite similar to Laravel's query builder. +The syntax is similar to Laravel's query builder "Eloquent", but with less overhead. -#### Ideas and issues +This library is stable, maintained and are used by many sites, including: -If you want a great new feature or experience any issues what-so-ever, please feel free to leave an issue and i'll look into it whenever possible. +- [Holla.dk](https://holla.dk) +- [Dscuz.com](https://dscuz.com) +- [NinjaImg.com](https://ninjaimg.com) +- [BookAndBegin.com](https://bookandbegin.com) -##### Issues guidelines +#### Feedback and development + +If you are missing a feature, experience problems or have ideas or feedback that you want us to hear, please feel free to create an issue. + +###### Issues guidelines - Please be as detailed as possible in the description when creating a new issue. This will help others to more easily understand- and solve your issue. -For example: if you are expiriencing issues, you should provide the nessesary steps to reproduct the error within your description. +For example: if you are experiencing issues, you should provide the necessary steps to reproduce the error within your description. - We love to hear out any ideas or feedback to the library. -##### Contribution development guidelines +[Create a new issue here](https://github.com/skipperbent/pecee-pixie/issues) + +###### Contribution development guidelines - Please try to follow the PSR-2 codestyle guidelines. @@ -29,13 +38,15 @@ For example when pushing changes to version 3, the pull request should use the ` - When adding new stuff, please remember to add new unit-tests for the functionality. #### Credits + This project is based on the original [Pixie project by usmanhalalit](https://github.com/usmanhalalit/pixie) but has some extra features like: - Easier sub-queries. - Custom prefix/aliases for tables (prefix.`table`). - Support for not defining table. - Better handling of `Raw` objects in `where` statements. -- Tons of bugfixes and performance optimisations. +- Performance optimisations. +- Tons of bug fixes. - Much more... **Including all the original features like:** @@ -46,6 +57,8 @@ This project is based on the original [Pixie project by usmanhalalit](https://gi - Nested Queries - Multiple Database Connections. +Most importantly this project is used on many live-sites and maintained. + #### Versions prior to 3.x Older versions prior to 3.x are available [https://github.com/skipperbent/pixie](https://github.com/skipperbent/pixie). @@ -406,8 +419,7 @@ LIMIT 1 You can also easily create a subjquery within the `where` statement: ```php -$queryBuilder - ->where($queryBuilder->subQuery($subQuery), '!=', 'value'); +$queryBuilder->where($queryBuilder->subQuery($subQuery), '!=', 'value'); ``` ### Where @@ -955,4 +967,28 @@ If you find any typo then please edit and send a pull request. © 2016 Simon Sessingø - [Pecee.dk](http://pecee.dk/). -Licensed under MIT license. \ No newline at end of file +## Licence + +Licensed under the MIT licence. + +### The MIT License (MIT) + +Copyright (c) 2016 Simon Sessingø / simple-php-router + +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. \ No newline at end of file diff --git a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php index ae77dd9..25efdec 100644 --- a/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php +++ b/src/Pecee/Pixie/QueryBuilder/Adapters/BaseAdapter.php @@ -183,7 +183,7 @@ private function doInsert(array $statements, array $data, $type) $sqlArray = [ $type . ' INTO', $this->wrapSanitizer($table), - '(' . $this->arrayStr($keys, ',') . ')', + '(' . $this->arrayStr($keys) . ')', 'VALUES', '(' . $this->arrayStr($values, ',', false) . ')', ]; diff --git a/src/Pecee/Pixie/QueryBuilder/JoinBuilder.php b/src/Pecee/Pixie/QueryBuilder/JoinBuilder.php index e5b0ebe..d5ca41a 100644 --- a/src/Pecee/Pixie/QueryBuilder/JoinBuilder.php +++ b/src/Pecee/Pixie/QueryBuilder/JoinBuilder.php @@ -17,7 +17,7 @@ class JoinBuilder extends QueryBuilderHandler */ public function on($key, $operator, $value) { - return $this->joinHandler($key, $operator, $value, 'AND'); + return $this->joinHandler($key, $operator, $value); } /** diff --git a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php index f5086f5..50d289c 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryBuilderHandler.php @@ -209,7 +209,7 @@ protected function parseDataType($value) * @param array $bindings * @return array PDOStatement and execution time as float */ - public function statement($sql, $bindings = []) + public function statement($sql, array $bindings = []) { $start = microtime(true); @@ -240,7 +240,7 @@ public function get() $executionTime = 0; if ($this->pdoStatement === null) { - $queryObject = $this->getQuery('select'); + $queryObject = $this->getQuery(); list($this->pdoStatement, $executionTime) = $this->statement( $queryObject->getSql(), $queryObject->getBindings() @@ -354,11 +354,11 @@ protected function aggregate($type) * Returns Query-object. * * @param string $type - * @param array|bool $dataToBePassed + * @param array|mixed|null $dataToBePassed * @return QueryObject * @throws Exception */ - public function getQuery($type = 'select', $dataToBePassed = []) + public function getQuery($type = 'select', $dataToBePassed = null) { $allowedTypes = [ 'select', @@ -819,7 +819,7 @@ public function orWhereNot($key, $operator = null, $value = null) */ public function whereIn($key, $values) { - return $this->whereHandler($key, 'IN', $values, 'AND'); + return $this->whereHandler($key, 'IN', $values); } /** @@ -831,7 +831,7 @@ public function whereIn($key, $values) */ public function whereNotIn($key, $values) { - return $this->whereHandler($key, 'NOT IN', $values, 'AND'); + return $this->whereHandler($key, 'NOT IN', $values); } /** @@ -868,7 +868,7 @@ public function orWhereNotIn($key, $values) */ public function whereBetween($key, $valueFrom, $valueTo) { - return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo], 'AND'); + return $this->whereHandler($key, 'BETWEEN', [$valueFrom, $valueTo]); } /** @@ -1050,22 +1050,27 @@ public function leftJoin($table, $key, $operator = null, $value = null) */ public function innerJoin($table, $key, $operator = null, $value = null) { - return $this->join($table, $key, $operator, $value, 'inner'); + return $this->join($table, $key, $operator, $value); } /** * Adds a raw string to the current query. * This query will be ignored from any parsing or formatting by the Query builder - * and should be used in conjuction with other statements in the query. + * and should be used in conjunction with other statements in the query. * * For example: $qb->where('result', '>', $qb->raw('COUNT(`score`))); * * @param string $value - * @param array $bindings + * @param array|null|mixed $bindings ... * @return Raw */ - public function raw($value, $bindings = []) + public function raw($value, $bindings = null) { + if(is_array($bindings) === false) { + $bindings = func_get_args(); + array_shift($bindings); + } + return $this->container->build(Raw::class, [$value, $bindings]); } diff --git a/src/Pecee/Pixie/QueryBuilder/QueryObject.php b/src/Pecee/Pixie/QueryBuilder/QueryObject.php index cdfaf51..58f59e6 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryObject.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryObject.php @@ -90,7 +90,7 @@ protected function interpolateQuery($query, $params) } if (is_array($value) === true) { - $values[$key] = implode(',', $this->pdo->quote($value)); + $values[$key] = implode(',', (array)$this->pdo->quote($value)); continue; } diff --git a/src/Pecee/Pixie/QueryBuilder/Raw.php b/src/Pecee/Pixie/QueryBuilder/Raw.php index 94f97cd..ad5dded 100644 --- a/src/Pecee/Pixie/QueryBuilder/Raw.php +++ b/src/Pecee/Pixie/QueryBuilder/Raw.php @@ -24,7 +24,7 @@ class Raw * @param string $value * @param array|string $bindings */ - public function __construct($value, $bindings = []) + public function __construct($value, array $bindings = []) { $this->value = (string)$value; $this->bindings = (array)$bindings; From faaaecf963f79c2b32433c6c096cdc7ffd712257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Sessing=C3=B8?= Date: Sat, 25 Nov 2017 18:46:48 +0100 Subject: [PATCH 5/5] Updated PHP version in documentation + join error. --- README.md | 6 +++++- src/Pecee/Pixie/QueryBuilder/QueryObject.php | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6ef3496..d8595d2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # pecee/pixie: Advanced lightweight querybuilder -A lightweight, expressive, framework agnostic query builder for PHP it can also be referred as a Database Abstraction Layer. Pixie supports MySQL, SQLite and PostgreSQL and it takes care of query sanitization, table prefixing and many other things with a unified API. At least PHP 5.4 is required. +A lightweight, expressive, framework agnostic query builder for PHP it can also be referred as a Database Abstraction Layer. +Pixie supports MySQL, SQLite and PostgreSQL will handle all your query sanitization, table prefixing among many other things, with a unified API. The syntax is similar to Laravel's query builder "Eloquent", but with less overhead. @@ -11,6 +12,9 @@ This library is stable, maintained and are used by many sites, including: - [NinjaImg.com](https://ninjaimg.com) - [BookAndBegin.com](https://bookandbegin.com) +**Requirements:** +- PHP version 5.5 or higher is required. + #### Feedback and development If you are missing a feature, experience problems or have ideas or feedback that you want us to hear, please feel free to create an issue. diff --git a/src/Pecee/Pixie/QueryBuilder/QueryObject.php b/src/Pecee/Pixie/QueryBuilder/QueryObject.php index 58f59e6..3f482f6 100644 --- a/src/Pecee/Pixie/QueryBuilder/QueryObject.php +++ b/src/Pecee/Pixie/QueryBuilder/QueryObject.php @@ -80,7 +80,7 @@ protected function interpolateQuery($query, $params) $keys = []; $values = $params; - # build a regular expression for each parameter + // build a regular expression for each parameter foreach ($params as $key => $value) { $keys[] = '/' . (is_string($key) ? ':' . $key : '[?]') . '/'; @@ -90,7 +90,7 @@ protected function interpolateQuery($query, $params) } if (is_array($value) === true) { - $values[$key] = implode(',', (array)$this->pdo->quote($value)); + $values[$key] = $this->pdo->quote(implode(',', $value)); continue; }