You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+66-65
Original file line number
Diff line number
Diff line change
@@ -5,9 +5,9 @@ Squirrel Queries
5
5
6
6
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.
7
7
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.
9
9
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.
11
11
12
12
Installation
13
13
------------
@@ -27,79 +27,80 @@ Table of contents
27
27
Setting up
28
28
----------
29
29
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.
31
31
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.
33
33
34
34
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).
35
35
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:
53
37
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;
56
45
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
+
);
59
55
60
-
// Set implementation layer beneath the error layer
61
-
$errorLayer->setLowerLayer($implementationLayer);
56
+
// Create a MySQL implementation layer
57
+
$implementationLayer = new MySQLImplementation($connection);
62
58
63
-
// Rename our layered service - this is now our database object
64
-
$db = $errorLayer;
59
+
// Create an error handler layer
60
+
$errorLayer = new ErrorHandler();
65
61
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);
69
64
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;
73
67
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
+
};
75
74
76
-
// A builder just needs a DBInterface to be created:
75
+
$fetchEntry($db);
77
76
78
-
$queryBuilder = new DBBuilderInterface($db);
77
+
// A builder just needs a DBInterface to be created:
78
+
$queryBuilder = new DBBuilder($db);
79
79
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();
93
93
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
99
99
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
+
```
103
104
104
105
Database support
105
106
----------------
@@ -294,13 +295,13 @@ with the values `5`, `Henry` and `Liam`.
294
295
295
296
UPSERT (update-or-insert) queries are an addition to SQL, known under different queries in different database systems:
296
297
297
-
- MySQL implemented them as "INSERT ... ON DUPLICATE KEY UPDATE"
298
+
- MySQL and MariaDB implemented them as "INSERT ... ON DUPLICATE KEY UPDATE"
298
299
- PostgreSQL and SQLite as "INSERT ... ON CONFLICT (index) DO UPDATE"
299
300
- The ANSI standard knows them as MERGE queries, although those can be a bit different
300
301
301
302
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.
302
303
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.
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:
348
349
349
350
```php
350
351
$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
816
817
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:
817
818
818
819
- 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`.
820
821
821
822
So the following works if `file_data` is a BYTEA field in Postgres:
0 commit comments