Skip to content

Commit 53e9641

Browse files
committed
Refactor for new squirrel connection dependency
1 parent b3e433c commit 53e9641

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1369
-2637
lines changed

.editorconfig

+1-6
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,4 @@ insert_final_newline = false
1111

1212
[*.php]
1313

14-
end_of_line = lf
15-
charset = utf-8
16-
indent_style = space
17-
indent_size = 4
18-
insert_final_newline = true
19-
trim_trailing_whitespace = true
14+
insert_final_newline = true

.gitattributes

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1+
/bin export-ignore
2+
/build export-ignore
13
/tests export-ignore
4+
/tools export-ignore
25
/examples export-ignore
36
/docker export-ignore
47
/vendor-bin export-ignore
58
/.editorconfig export-ignore
69
/.gitattributes export-ignore
710
/.gitignore export-ignore
8-
/.travis.yml export-ignore
9-
/captainhook.json export-ignore
10-
/phpstan.neon export-ignore
11-
/phpstan-baseline.neon export-ignore
12-
/phpunit.xml.dist export-ignore
13-
/psalm.xml export-ignore
14-
/psalm-baseline.xml export-ignore
15-
/ruleset.xml export-ignore
11+
/.travis.yml export-ignore

.gitignore

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
/vendor
44
/vendor-bin/**/vendor
55
/vendor-bin/**/composer.lock
6-
/.phpunit.result.cache
7-
/.phpcs-cache
86
/tests/_output
97
/tests/_reports
10-
/build
8+
/build
9+
/tools/cache/*
10+
!/tools/cache/.gitkeep

README.md

+66-65
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ Squirrel Queries
55

66
Provides a slimmed down concise interface for low level database queries and transactions (DBInterface) as well as a query builder to make it easier and more expressive to create queries (DBBuilderInterface). The interfaces are limited to avoid confusion/misuse and encourage fail-safe usage.
77

8-
Doctrine DBAL is used for the underlying connection (and abstraction) handling, what we add are an insertOrUpdate functionality (known as UPSERT), structured queries which are easier to write and read (and for which the query builder can be used), and the possibility to layer database concerns (like actual implementation, connections retries, performance measurements, logging, etc.). This library also smoothes over some differences between MySQL, Postgres and SQLite. While DBAL is a dependency for now, when using this library you only need to configure/create the necessary DBAL connections in your code, no other parts of DBAL are relevant.
8+
[squirrelphp/connection](https://github.com/squirrelphp/connection) is used for the underlying connection (and abstraction) handling starting with v2.0 (v1.3 and below had Doctrine DBAL as a dependency), what we add are an insertOrUpdate functionality (known as UPSERT), structured queries which are easier to write and read (and for which the query builder can be used), and the possibility to layer database concerns (like actual implementation, connections retries, performance measurements, logging, etc.). This library also smoothes over some differences between MySQL, Postgres and SQLite.
99

10-
By default this library provides two layers, one dealing with Doctrine DBAL (passing the queries, processing and returning the results) and one dealing with errors (DBErrorHandler). DBErrorHandler catches deadlocks and connection problems and tries to repeat the query or transaction, and it unifies the exceptions coming from DBAL so the originating call to DBInterface is provided and the error can easily be found.
10+
By default this library provides two layers, one dealing with the actual database connection (passing the queries, processing and returning the results) and one dealing with errors (DBErrorHandler). DBErrorHandler catches deadlocks and connection problems and tries to repeat the query or transaction, and it unifies the exceptions coming from the connection so the originating call to DBInterface is provided and the error can easily be found.
1111

1212
Installation
1313
------------
@@ -27,79 +27,80 @@ Table of contents
2727
Setting up
2828
----------
2929

30-
Use Squirrel\Queries\DBInterface as a type hint in your services for the low-level interface, and/or Squirrel\Queries\DBBuilderInterface for the query builder. The low-level interface options are based upon Doctrine and PDO with some tweaks, and the builder interface is an expressive way to write structured (and not too complex) queries.
30+
Use Squirrel\Queries\DBInterface as a type hint in your services for the low-level interface, and/or Squirrel\Queries\DBBuilderInterface for the query builder - the query builder is an expressive way to write structured (and not too complex) queries.
3131

32-
If you know Doctrine or PDO you should be able to use this library easily. You should especially have an extra look at structured queries and UPSERT, as these are additions to the low-level interface, helping you to make readable queries and taking care of your column field names and parameters automatically, making it easier to write secure queries.
32+
If you know Doctrine DBAL or PDO you should be able to use this library easily, while avoiding some of their complexities. You should especially have an extra look at structured queries and UPSERT, as these are additions to the low-level interface, helping you to make readable queries and taking care of your column field names and parameters automatically, making it easier to write secure queries.
3333

3434
For a solution which integrates easily with the Symfony framework, check out [squirrelphp/queries-bundle](https://github.com/squirrelphp/queries-bundle), and for entity and repository support check out [squirrelphp/entities](https://github.com/squirrelphp/entities) and [squirrelphp/entities-bundle](https://github.com/squirrelphp/entities-bundle).
3535

36-
If you want to assemble a DBInterface object yourself, something like the following code can be a start:
37-
38-
use Doctrine\DBAL\DriverManager;
39-
use Squirrel\Queries\DBBuilderInterface;
40-
use Squirrel\Queries\DBInterface;
41-
use Squirrel\Queries\Doctrine\DBErrorHandler;
42-
use Squirrel\Queries\Doctrine\DBMySQLImplementation;
43-
44-
// Create a doctrine connection
45-
$dbalConnection = DriverManager::getConnection([
46-
'url' => 'mysql://user:secret@localhost/mydb',
47-
'driverOptions' => [
48-
\PDO::ATTR_EMULATE_PREPARES => false, // Separates query and values
49-
\PDO::MYSQL_ATTR_FOUND_ROWS => true, // important so MySQL behaves like Postgres/SQLite
50-
\PDO::MYSQL_ATTR_MULTI_STATEMENTS => false,
51-
],
52-
]);
36+
If you want to assemble a DBInterface and DBBuilder yourself (even though you will likely want to use integration bundles instead), something like the following code can be a start:
5337

54-
// Create a MySQL implementation layer
55-
$implementationLayer = new DBMySQLImplementation($dbalConnection);
38+
```php
39+
use Squirrel\Connection\Config\Mysql;
40+
use Squirrel\Connection\PDO\ConnectionPDO;
41+
use Squirrel\Queries\DBBuilder;
42+
use Squirrel\Queries\DBInterface;
43+
use Squirrel\Queries\DB\ErrorHandler;
44+
use Squirrel\Queries\DB\MySQLImplementation;
5645

57-
// Create an error handler layer
58-
$errorLayer = new DBErrorHandler();
46+
// Create a squirrel connection
47+
$connection = new ConnectionPDO(
48+
new Mysql(
49+
host: 'localhost',
50+
user: 'user',
51+
password: 'password',
52+
dbname: 'mydb',
53+
),
54+
);
5955

60-
// Set implementation layer beneath the error layer
61-
$errorLayer->setLowerLayer($implementationLayer);
56+
// Create a MySQL implementation layer
57+
$implementationLayer = new MySQLImplementation($connection);
6258

63-
// Rename our layered service - this is now our database object
64-
$db = $errorLayer;
59+
// Create an error handler layer
60+
$errorLayer = new ErrorHandler();
6561

66-
// $db is now useable and can be injected
67-
// anywhere you need it. Typehint it with
68-
// \Squirrel\Queries\DBInterface
62+
// Set implementation layer beneath the error layer
63+
$errorLayer->setLowerLayer($implementationLayer);
6964

70-
$fetchEntry = function(DBInterface $db): array {
71-
return $db->fetchOne('SELECT * FROM table');
72-
};
65+
// Rename our layered service - this is now our database object
66+
$db = $errorLayer;
7367

74-
$fetchEntry($db);
68+
// $db is now useable and can be injected
69+
// anywhere you need it. Typehint it with
70+
// \Squirrel\Queries\DBInterface
71+
$fetchEntry = function(DBInterface $db): array {
72+
return $db->fetchOne('SELECT * FROM table');
73+
};
7574

76-
// A builder just needs a DBInterface to be created:
75+
$fetchEntry($db);
7776

78-
$queryBuilder = new DBBuilderInterface($db);
77+
// A builder just needs a DBInterface to be created:
78+
$queryBuilder = new DBBuilder($db);
7979

80-
// The query builder generates more readable queries, and
81-
// helps your IDE in terms of type hints / possible options
82-
// depending on the query you are doing
83-
$entries = $queryBuilder
84-
->select()
85-
->fields([
86-
'id',
87-
'name',
88-
])
89-
->where([
90-
'name' => 'Robert',
91-
])
92-
->getAllEntries();
80+
// The query builder generates more readable queries, and
81+
// helps your IDE in terms of type hints / possible options
82+
// depending on the query you are doing
83+
$entries = $queryBuilder
84+
->select()
85+
->fields([
86+
'id',
87+
'name',
88+
])
89+
->where([
90+
'name' => 'Robert',
91+
])
92+
->getAllEntries();
9393

94-
// If you want to add more layers, you can create a
95-
// class which implements DBRawInterface and includes
96-
// the DBPassToLowerLayer trait and then just overwrite
97-
// the functions you want to change, and then connect
98-
// it to the other layers through setLowerLayer
94+
// If you want to add more layers, you can create a
95+
// class which implements DBRawInterface and includes
96+
// the DBPassToLowerLayer trait and then just overwrite
97+
// the functions you want to change, and then connect
98+
// it to the other layers through setLowerLayer
9999

100-
// It is also a good idea to catch \Squirrel\Queries\DBException
101-
// in your application in case of a DB error so it
102-
// can be handled gracefully
100+
// It is also a good idea to catch \Squirrel\Queries\DBException
101+
// in your application in case of a DB error so it
102+
// can be handled gracefully
103+
```
103104

104105
Database support
105106
----------------
@@ -294,13 +295,13 @@ with the values `5`, `Henry` and `Liam`.
294295

295296
UPSERT (update-or-insert) queries are an addition to SQL, known under different queries in different database systems:
296297

297-
- MySQL implemented them as "INSERT ... ON DUPLICATE KEY UPDATE"
298+
- MySQL and MariaDB implemented them as "INSERT ... ON DUPLICATE KEY UPDATE"
298299
- PostgreSQL and SQLite as "INSERT ... ON CONFLICT (index) DO UPDATE"
299300
- The ANSI standard knows them as MERGE queries, although those can be a bit different
300301

301302
In this library we call this type of query `insertOrUpdate`. Such a query tries to insert a row, but if the row already exists it does an update instead, and all of this is done as one atomic operation in the database. If implemented without an UPSERT query you would need at least an UPDATE and then possibly an INSERT query within a transaction to do the same. UPSERT exists to be a faster and easier solution.
302303

303-
PostgreSQL and SQLite need the specific column names which form a unique index in the table which is used to determine if an entry already exists or if a new entry is inserted. MySQL does this automatically, but for all database systems it is important to have a unique index involved in an UPSERT query.
304+
PostgreSQL and SQLite need the specific column names which form a unique index in the table which is used to determine if an entry already exists or if a new entry is inserted. MySQL/MariaDB do this automatically, but for all database systems it is important to have a unique index involved in an UPSERT query.
304305

305306
#### Usage and examples
306307

@@ -319,7 +320,7 @@ $db->insertOrUpdate('users_visits', [
319320
]);
320321
```
321322

322-
For MySQL, this query would be converted to:
323+
For MySQL/MariaDB, this query would be converted to:
323324

324325
```php
325326
$db->change('INSERT INTO `users_visits` (`userId`,`visit`) VALUES (?,?) ON DUPLICATE KEY UPDATE `visit` = `visit` + 1', [5, 1]);
@@ -344,7 +345,7 @@ $db->insertOrUpdate('users_names', [
344345
]);
345346
```
346347

347-
This would INSERT with userId and firstName, but if the row already exists, it would just update firstName to Jane, so for MySQL it would be converted to:
348+
This would INSERT with userId and firstName, but if the row already exists, it would just update firstName to Jane, so for MySQL/MariaDB it would be converted to:
348349

349350
```php
350351
$db->change('INSERT INTO `users_names` (`userId`,`firstName`) VALUES (?,?) ON DUPLICATE KEY UPDATE `firstName`=?, [5, 'Jane', 'Jane']);
@@ -816,12 +817,12 @@ BLOB handling for Postgres
816817
For MySQL and SQLite retrieving or inserting/updating BLOBs (Binary Large Objects) works just the same as with shorter/non-binary string fields. Postgres needs some adjustments, but these are streamlined by this library:
817818

818819
- For SELECT queries, streams returned by Postgres are automatically converted into strings, mimicking how MySQL and SQLite are doing it
819-
- For INSERT/UPDATE queries, you need to wrap BLOB values with an instance of LargeObject provided by this library.
820+
- For INSERT/UPDATE queries, you need to wrap BLOB values with an instance of LargeObject provided by `squirrelphp/connection`.
820821

821822
So the following works if `file_data` is a BYTEA field in Postgres:
822823

823824
```php
824-
use Squirrel\Queries\LargeObject;
825+
use Squirrel\Connection\LargeObject;
825826

826827
$rowsAffected = $dbBuilder
827828
->update()

bin/vendorbin

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/usr/bin/env php
2+
<?php
3+
error_reporting(E_ALL); // Report everything, even notices
4+
set_time_limit(0); // No time limit for console commands
5+
6+
$projectDir = dirname(__DIR__);
7+
8+
$composerRunType = $_SERVER['argv'][1] ?? 'outdated';
9+
10+
require $projectDir.'/vendor/autoload.php';
11+
12+
$sourceFinder = new \Symfony\Component\Finder\Finder();
13+
$sourceFinder->in($projectDir . '/vendor-bin')->directories()->depth(0)->sortByName();
14+
15+
/** @var array<string, \Symfony\Component\Process\Process> $tools */
16+
$tools = [];
17+
18+
foreach ($sourceFinder as $directory) {
19+
$toolName = $directory->getFilename();
20+
21+
$options = [
22+
'--ansi',
23+
];
24+
25+
if ($composerRunType === 'update') {
26+
$options[] = '--no-progress';
27+
}
28+
29+
$process = new \Symfony\Component\Process\Process(['composer', $composerRunType, ...$options]);
30+
if (isset($_SERVER['COMPOSER_CACHE_DIR'])) {
31+
$process->setEnv(['COMPOSER_CACHE_DIR' => $_SERVER['COMPOSER_CACHE_DIR']]);
32+
}
33+
$process->setWorkingDirectory($projectDir . '/vendor-bin/' . $toolName);
34+
$process->start();
35+
$process->wait();
36+
37+
echo 'Running composer ' . $composerRunType . ' for ' . $toolName . ' ...' . "\n";
38+
39+
$processOutput = \trim($process->getOutput());
40+
41+
if ($composerRunType === 'update') {
42+
$processOutput = \trim($processOutput . "\n" . $process->getErrorOutput());
43+
}
44+
45+
if (\strlen($processOutput) > 0) {
46+
echo $processOutput . "\n";
47+
}
48+
}

composer.json

+31-26
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "squirrelphp/queries",
33
"type": "library",
4-
"description": "Slimmed down concise interface and query builder for database queries and transactions which can be layered / decorated.",
4+
"description": "Slimmed down concise interfaces and query builder for database queries and transactions which can be layered / decorated.",
55
"keywords": [
66
"php",
77
"mysql",
@@ -19,17 +19,18 @@
1919
}
2020
],
2121
"require": {
22-
"php": ">=8.0",
23-
"ext-pdo": "*",
24-
"squirrelphp/debug": "^2.0",
25-
"doctrine/dbal": "^3.0"
22+
"php": ">=8.2",
23+
"squirrelphp/connection": "^0.2",
24+
"squirrelphp/debug": "^2.0"
2625
},
2726
"require-dev": {
28-
"bamarni/composer-bin-plugin": "^1.8",
29-
"captainhook/plugin-composer": "^5.0",
30-
"phpunit/phpunit": "^9.0",
27+
"captainhook/captainhook-phar": "^5.0",
28+
"captainhook/hook-installer": "^1.0",
29+
"phpunit/phpunit": "^11.2",
3130
"mockery/mockery": "^1.0",
32-
"squirrelphp/types": "^1.0"
31+
"squirrelphp/types": "^1.0",
32+
"symfony/finder": "^7.0",
33+
"symfony/process": "^7.0"
3334
},
3435
"suggest": {
3536
"squirrelphp/queries-bundle": "Symfony integration of squirrelphp/queries - automatic assembling of decorated connections",
@@ -39,9 +40,13 @@
3940
"config": {
4041
"sort-packages": false,
4142
"allow-plugins": {
42-
"bamarni/composer-bin-plugin": true,
43-
"captainhook/plugin-composer": true,
44-
"composer/package-versions-deprecated": true
43+
"captainhook/captainhook-phar": true,
44+
"captainhook/hook-installer": true
45+
}
46+
},
47+
"extra": {
48+
"captainhook": {
49+
"config": "tools/captainhook.json"
4550
}
4651
},
4752
"autoload": {
@@ -55,19 +60,19 @@
5560
}
5661
},
5762
"scripts": {
58-
"phpstan": "vendor/bin/phpstan analyse",
59-
"phpstan_full": "vendor/bin/phpstan clear-result-cache && vendor/bin/phpstan analyse",
60-
"phpstan_base": "vendor/bin/phpstan analyse --generate-baseline",
61-
"psalm": "vendor/bin/psalm --show-info=false",
62-
"psalm_full": "vendor/bin/psalm --clear-cache && vendor/bin/psalm --show-info=false",
63-
"psalm_base": "vendor/bin/psalm --set-baseline=psalm-baseline.xml",
64-
"phpunit": "vendor/bin/phpunit --colors=always",
65-
"phpunit_clover": "vendor/bin/phpunit --coverage-text --coverage-clover build/logs/clover.xml",
66-
"coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html tests/_reports",
67-
"phpcs": "vendor/bin/phpcs --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --colors src tests",
68-
"phpcsd": "vendor/bin/phpcs -s --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --colors src tests",
69-
"phpcsfix": "vendor/bin/phpcbf --standard=ruleset.xml --extensions=php --cache=.phpcs-cache src tests",
70-
"binupdate": "@composer bin all update --ansi",
71-
"bininstall": "@composer bin all install --ansi"
63+
"phpstan": "vendor-bin/phpstan/vendor/bin/phpstan analyse --configuration=tools/phpstan.neon",
64+
"phpstan_full": "rm -Rf tools/cache/phpstan && vendor-bin/phpstan/vendor/bin/phpstan analyse --configuration=tools/phpstan.neon",
65+
"phpstan_base": "vendor-bin/phpstan/vendor/bin/phpstan analyse --configuration=tools/phpstan.neon --generate-baseline=tools/phpstan-baseline.php",
66+
"psalm": "vendor-bin/psalm/vendor/bin/psalm --config=tools/psalm.xml --show-info=false",
67+
"psalm_full": "vendor-bin/psalm/vendor/bin/psalm --config=tools/psalm.xml --clear-cache && vendor-bin/psalm/vendor/bin/psalm --config=tools/psalm.xml --show-info=false",
68+
"psalm_base": "vendor-bin/psalm/vendor/bin/psalm --config=tools/psalm.xml --set-baseline=tools/psalm-baseline.xml",
69+
"phpunit": "vendor/bin/phpunit --configuration=tools/phpunit.xml.dist --cache-result-file=tools/cache/.phpunit.result.cache --colors=always --testsuite=unit",
70+
"phpunit_clover": "vendor/bin/phpunit --configuration=tools/phpunit.xml.dist --cache-result-file=tools/cache/.phpunit.result.cache --coverage-text --coverage-clover build/logs/clover.xml",
71+
"coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --configuration=tools/phpunit.xml.dist --coverage-html=tests/_reports",
72+
"phpcs": "vendor-bin/phpcs/vendor/bin/phpcs --standard=tools/ruleset.xml --extensions=php --cache=tools/cache/.phpcs-cache --colors src tests",
73+
"phpcs_diff": "vendor-bin/phpcs/vendor/bin/phpcs -s --standard=tools/ruleset.xml --extensions=php --cache=tools/cache/.phpcs-cache --colors src tests",
74+
"phpcs_fix": "vendor-bin/phpcs/vendor/bin/phpcbf --standard=tools/ruleset.xml --extensions=php --cache=tools/cache/.phpcs-cache --colors src tests",
75+
"binupdate": "bin/vendorbin update",
76+
"binoutdated": "bin/vendorbin outdated"
7277
}
7378
}

docker/compose-coverage.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
version: "3.7"
21
services:
32
squirrel_queries_coverage:
4-
image: thecodingmachine/php:8.1-v4-cli
3+
image: thecodingmachine/php:8.3-v4-cli
54
container_name: squirrel_queries_coverage
65
tty: true
76
working_dir: /usr/src/app

0 commit comments

Comments
 (0)