Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs #28

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Docs #28

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 117 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,127 @@
[![PHP Support](https://img.shields.io/badge/php-%3E%3D%205.6-777BB4?logo=PHP)](https://php.net/)
![Build Status](https://github.com/Icinga/ipl-sql/workflows/PHP%20Tests/badge.svg?branch=master)

The package `ipl\Sql` provides a [database connection
abstraction](#sql-connection) and an [SQL query abstraction layer](#sql-queries)
for building SQL queries via an OOP API.

For details please see the [features](doc/01-Features.md) description.
The package `ipl\Sql` provides a [database connection abstraction](#connection)
and an [SQL query abstraction layer](#queries) for building SQL queries via an OOP API.

## Installation

The recommended way to install this library is via [Composer](https://getcomposer.org):
The recommended way to install this package is via [Composer](https://getcomposer.org):

```
composer require ipl/sql
```

## Connection <a id="connection"></a>

`ipl\Sql\Connection` is an extension to the native [PDO](https://www.php.net/PDO)
and adds the following features on top:

**Lazy connection**

`ipl\Sql\Connection` connects to database only if you make a query or start a transaction.

**Exceptions enabled by default**

`ipl\Sql\Connection` starts in the `ERRMODE_EXCEPTION` mode for error reporting instead of `ERRMODE_SILENT`.

**New methods for common actions**

The `prepexec()` method acts like [PDO::query()](https://www.php.net/manual/en/pdo.query.php)
but automatically creates a prepared statement and binds values to that as part of the call.

The `fetch*()` methods support common fetch actions and combine preparing the statement, binding values, execution and
the actual fetch from the prepared statement into a single function call.

The `yield*()` methods act like their `fetch*()` equivalents but yield results instead of returning them.

**Straightforward construction**

With `PDO`, you have to do formalities such as assembling the platform-dependent DSN string.
With `ipl\Sql\Connection`, you just do your thing straightforward:

```php
$connection = new Connection([
'db' => 'mysql',
'host' => '193.20.23.148',
'dbname' => 'icinga',
'username' => 'icinga',
'password' => 'secret',
'charset' => 'utf8mb4',
'attributes' => [
PDO::MYSQL_ATTR_SSL_CA => '/etc/acme/mysql/ca.pem',
PDO::MYSQL_ATTR_SSL_CERT => '/etc/acme/mysql/cert.pem',
PDO::MYSQL_ATTR_SSL_KEY => '/etc/acme/mysql/key.pem'
]
]);
```

**Transaction wrapper**

Use the `transaction()` method which accepts a callback in order to wrap your statements in a transaction, e.g.

```php
$connection->transaction(function ($connection) use ($table, $data) {
$connection->insert($table, $data);
});
```

You may still use the unified methods for transaction handling `beginTransaction()`, `commitTransaction()` and
`rollBackTransaction()` on your own.

**Prepared statements only**

In order to protect from SQL injection and prevent worrying about value quoting,
`ipl/Sql` uses prepared statements only.
That means that the query and the data are sent to the database server separately.

**No automatic identifier quoting**

Since automatic identifier quoting is prone to errors and superfluous in most cases,
you have to apply identifier quoting as needed by using `Connection::quoteIdentifier()`.
Be aware that it is a must to quote identifiers if you allow user input for field names or
if you are using special field names, e.g. reserved keywords for your DBMS.

**Automatic array quoting**

Throughout `ipl/Sql` you can bind an array of values to a placeholder used within an `IN (?)` condition for example.
Placeholders having array values will be expanded automatically.

## Queries <a id="connection"></a>

`ipl/Sql` is capable to build queries for MySQL, PostgreSQL, MSSQL and SQLite. (Oracle and IBM will follow).
Building queries is independent of any particular database connection and there are no database-specific classes to use.

The following examples should give you an idea about what's possible and how to use the OOP API:

```php
$connection->prepexec(
(new Insert())
->into('customer')
->values([
'id' => 42,
'name' => 'John Doe'
])
);
$connection->prepexec(
(new Select())
->columns(['name'])
->from('customer')
->where(['id IN (?)' => [42]])
)->fetchAll();
$connection->prepexec(
(new Update())
->table('customer')
->set(['name' => 'John Doe'])
->where(['id = ?' => 42])
);
$connection->prepexec(
(new Delete())
->from('customer')
->where(['id = ?' => 42])
);
```

Granted, the query objects look a bit overkill here and you may use `Connection::insert()`, `Connection::update()`
and `Connection::delete()` for simple tasks instead. But when it comes to `INSERT INTO ... SELECT`,
complex `WHERE` clauses, CTEs, ... and reusable and parameterised queries, you'll love the flexiblity.
20 changes: 10 additions & 10 deletions doc/01-Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ $connection = new Connection([ // (1)

$connection->connect(); // optional (12)

var_dump($connection->exec(
var_dump($connection->prepexec(
'SELECT * FROM customer WHERE id = ?;', [42] // (13)
)->fetchRow());

Expand All @@ -45,7 +45,7 @@ the SSL certificates (9-11) are likely not to be neccessary depending on your
database driver (2). Explicit initialization (12) and disconnecting (14) are
always optional.

`$connection->exec()` returns a
`$connection->prepexec()` returns a
[PDOStatement](https://secure.php.net/manual/en/class.pdostatement.php) - see
its documentation for details.

Expand All @@ -61,30 +61,30 @@ use ipl\Sql\Insert;
use ipl\Sql\Select;
use ipl\Sql\Update;

$connection->exec(
$connection->prepexec(
(new Insert())
->into('customer')
->values([
'id' => 42,
'name' => 'John Deo'
'name' => 'John Doe'
])
);

$connection->exec(
$connection->prepexec(
(new Select())
->columns(['name'])
->from('customer')
->where(['id = ?' => 42])
->where(['id IN (?)' => [42]])
)->fetchAll();

$connection->exec(
$connection->prepexec(
(new Update())
->table('customer')
->set(['name' => 'John Doe'])
->where(['id = ?' => 42])
);

$connection->exec(
$connection->prepexec(
(new Delete())
->from('customer')
->where(['id = ?' => 42])
Expand All @@ -100,12 +100,12 @@ Insert data into a table, provided either explicitly ...
->into('customer')
->values([
'id' => 42,
'name' => 'John Deo'
'name' => 'John Doe'
])
```

```mysql
INSERT INTO customer (id,name) VALUES(42,'John Deo')
INSERT INTO customer (id,name) VALUES(42,'John Doe')
```

... or by a [select](#sql-select) query:
Expand Down