Skip to content

Commit

Permalink
no issue - progressively decouple bridge in order to hide it as an im…
Browse files Browse the repository at this point in the history
…plementation detail
  • Loading branch information
pounard committed Apr 18, 2024
1 parent 6364fac commit c8ada84
Show file tree
Hide file tree
Showing 45 changed files with 317 additions and 269 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## Next

* [feature] `Bridge::getSchemaManager()` method is moved into the
`DatabaseSession` interface instead. Bridges should now never be used
as a public API for end users.
* [bc] ⚠️ All errors in the `MakinaCorpus\QueryBuilder\Error\Bridge`
namespace are now in the `MakinaCorpus\QueryBuilder\Error\Server` namespace.
* [bc] ⚠️ `FunctionalTestCaseTrait::getBridge()` was renamed to
`FunctionalTestCaseTrait::getDatabaseSession()`.

## 1.4.0

* [feature] ⭐️ Added `DatabaseSession::getVendorName()`, `DatabaseSession::getVendorVersion()`,
Expand Down
8 changes: 4 additions & 4 deletions docs/content/bridges/error.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ They all inherit from `MakinaCorpus\QueryBuilder\Error\QueryBuilderError`.
## Server errors

All server side errors are exception, when a driver error is caught and supported,
it will be converted to a `MakinaCorpus\QueryBuilder\Error\Bridge\*` exception.
it will be converted to a `MakinaCorpus\QueryBuilder\Error\Server\*` exception.

When an error is not supported then the generic
`MakinaCorpus\QueryBuilder\Error\Bridge\ServerError` is raised.
`MakinaCorpus\QueryBuilder\Error\Server\ServerError` is raised.

## Bridge errors passthrough

Expand All @@ -38,10 +38,10 @@ use Doctrine\DBAL\DriverManager;
use MakinaCorpus\QueryBuilder\Bridge\Doctrine\DoctrineQueryBuilder;

$connection = DriverManager::getConnection(['driver' => 'pdo_pgsql', /* ... */]);
$queryBuilder = new DoctrineQueryBuilder($connection);
$bridge = new DoctrineQueryBuilder($connection);

// Here it is:
$queryBuilder->disableErrorConverter();
$bridge->disableErrorConverter();
```

:::info
Expand Down
16 changes: 9 additions & 7 deletions docs/content/converter/converter.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@

**Input value converter** converts user input values **from PHP to SQL**.

It is plugged per default in the `Bridge` or `QueryBuilder` instance then
propagated to the `ArgumentBag` instance prior query execution.
It is plugged per default in the `Bridge` instance, hence for the final user
the `QueryBuilder` and `DatabaseSession` instances, then propagated to the
`ArgumentBag` instance prior query execution.

When `ArgumentBag::getAll()` is called, all values are lazily converted from
PHP to SQL using the types that were specified during query building.
Expand All @@ -32,8 +33,9 @@ See the [legacy data type matrix](../query/datatype) until the documentation is

**Output value converter** converts user input values **from SQL to PHP**.

It is plugged per default in the `Bridge` or `QueryBuilder` instance then
propagated to the `ResultRow` instance after query execution.
It is plugged per default in the `Bridge` instance, hence for the final user
the `QueryBuilder` and `DatabaseSession` instances, then propagated to the
`ResultRow` instance after query execution.

When you decide to iterate over `ResultRow` instances, by calling
`Result::fetchRow()` then the output converter may be used if you specify
Expand Down Expand Up @@ -91,7 +93,7 @@ $escaper = new StandardEscaper();
// Pass here the newly created converter.
$writer = new PostgreSQLWriter($escaper, $converter);

$queryBuilder = new DefaultQueryBuilder();
$session = new DefaultQueryBuilder();
```

### When using a bridge
Expand All @@ -102,10 +104,10 @@ by the bridge, hence the different procedure.
```php
use MakinaCorpus\QueryBuilder\Bridge\AbstractBridge;

assert($bridge instanceof AbstractBridge);
assert($session instanceof AbstractBridge);

// Directly register your implementation into the bridge.
$bridge
$session
->getConverterPluginRegistry
->register(
new MyCustomOutputConverter(),
Expand Down
2 changes: 1 addition & 1 deletion docs/content/introduction/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ While your write your SQL queries, you can, and probably will, provide userland
arbitrary identifiers, such as table names, column names, ... As show below:

```php
$queryBuilder->select('some_table')->column('some_column');
$session->select('some_table')->column('some_column');
```

Those needs escaping, in order to prevent SQL injection or unintential syntax
Expand Down
41 changes: 31 additions & 10 deletions docs/content/introduction/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ Setting it up is easier than standalone setup:
```php
use Doctrine\DBAL\DriverManager;
use MakinaCorpus\QueryBuilder\Bridge\Doctrine\DoctrineQueryBuilder;
use MakinaCorpus\QueryBuilder\DatabaseSession;

// Create or fetch your doctrine/dbal connection.
$connection = DriverManager::getConnection([
Expand All @@ -125,9 +126,19 @@ $connection = DriverManager::getConnection([
]);

// Create the query builder.
$queryBuilder = new DoctrineQueryBuilder($connection);
$session = new DoctrineQueryBuilder($connection);
\assert($session instanceof DatabaseSession);
```

:::warning
For the final user, the *bridge* should be hidden and the
`MakinaCorpus\QueryBuilder\DatabaseSession` exposed instead. The bridge is an
internal component that ties the query builder with a third-party driver.

All useful features are exposed via the `DatabaseSession` whereas the bridge
should remain hidden, as its signature is subject to changes.
:::

:::tip
You don't need to specify the SQL dialect to use, it will be derived from
the `doctrine/dbal` connection automatically, without requiring any extra SQL
Expand All @@ -139,7 +150,7 @@ query to do so.
Now we can write a query and execute it directly:

```php
$result = $queryBuilder
$result = $session
->select('users')
->column('*')
->where('id', '[email protected]')
Expand Down Expand Up @@ -179,14 +190,25 @@ Setting it up is easier than standalone setup:

```php
use MakinaCorpus\QueryBuilder\Bridge\Pdo\PdoQueryBuilder;
use MakinaCorpus\QueryBuilder\DatabaseSession;

// Create or fetch your PDO connection.
$connection = new \PDO('pgsql:...');

// User facade for you to build SQL queries.
$queryBuilder = new PdoQueryBuilder($connection);
$session = new PdoQueryBuilder($connection);
\assert($session instanceof DatabaseSession);
```

:::warning
For the final user, the *bridge* should be hidden and the
`MakinaCorpus\QueryBuilder\DatabaseSession` exposed instead. The bridge is an
internal component that ties the query builder with a third-party driver.

All useful features are exposed via the `DatabaseSession` whereas the bridge
should remain hidden, as its signature is subject to changes.
:::

:::tip
You don't need to specify the SQL dialect to use, it will be derived from
the `PDO` connection automatically.
Expand All @@ -195,7 +217,7 @@ the `PDO` connection automatically.
### 3. Write your query and execute it

```php
$result = $queryBuilder
$result = $session
->select('users')
->column('*')
->where('id', '[email protected]')
Expand Down Expand Up @@ -258,7 +280,6 @@ declare (strict_types=1);

namespace App\Controller;

use MakinaCorpus\QueryBuilder\Bridge\Doctrine\DoctrineQueryBuilder;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
Expand All @@ -267,9 +288,9 @@ class TestingController extends AbstractController
{
#[Route('/testing/query-builder', name: 'testing_query_builder')]
public function testQueryBuilder(
DoctrineQueryBuilder $queryBuilder,
DatabaseSession $session,
): Response {
$result = $queryBuilder
$result = $session
->select('some_table')
->executeQuery()
;
Expand All @@ -295,10 +316,10 @@ If you need to use another connection, please read the next chapter.
### Using a query builder for another connection

You may have configured more than one `doctrine/dbal` connection, this bundle
will register as many `MakinaCorpus\QueryBuilder\Bridge\Doctrine\DoctrineQueryBuilder`
services as doctrine connections being configured.
will register as many `MakinaCorpus\QueryBuilder\DatabaseSession` services as
doctrine connections being configured.

Each service identifier is `query_builder.doctrine.CONNECTION_NAME` where
Each service identifier is `query_builder.session.CONNECTION_NAME` where
`CONNECTION_NAME` is the Doctrine bundle configured connection identifier.

:::tip
Expand Down
3 changes: 2 additions & 1 deletion docs/content/introduction/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ almost every method call, exception being the methods that create new
objects that requires you to manipulate.

:::info
In all this page, The `$queryBuilder` variable will always be an instance of `MakinaCorpus\QueryBuilder\QueryBuilder`.
In all this page, The `$queryBuilder` variable will always be an instance of
`MakinaCorpus\QueryBuilder\DatabaseSession` or `MakinaCorpus\QueryBuilder\QueryBuilder`.
The `$result` variable will always be an instance of `MakinaCorpus\QueryBuilder\Result\Result`.
:::

Expand Down
14 changes: 7 additions & 7 deletions docs/content/query/result.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ you can call `Query::executeQuery()` over your queries, for example:
use Doctrine\DBAL\DriverManager;
use MakinaCorpus\QueryBuilder\Bridge\Doctrine\DoctrineQueryBuilder;

$queryBuilder = new DoctrineQueryBuilder(
$session = new DoctrineQueryBuilder(
DriverManager::getConnection([
'driver' => 'pdo_pgsql',
// ... driver options.
])
);

$result = $queryBuilder
$result = $session
->select('some_table')
->column('*')
->executeQuery()
Expand All @@ -48,14 +48,14 @@ You may also directly execute raw SQL code as such:
use Doctrine\DBAL\DriverManager;
use MakinaCorpus\QueryBuilder\Bridge\Doctrine\DoctrineQueryBuilder;

$queryBuilder = new DoctrineQueryBuilder(
$session = new DoctrineQueryBuilder(
DriverManager::getConnection([
'driver' => 'pdo_pgsql',
// ... driver options.
])
);

$result = $queryBuilder
$result = $session
->executeQuery(
<<<SQL
SELECT * FROM "some_table"
Expand All @@ -82,14 +82,14 @@ use Doctrine\DBAL\DriverManager;
use MakinaCorpus\QueryBuilder\Bridge\Doctrine\DoctrineQueryBuilder;
use MakinaCorpus\QueryBuilder\Result\ResultRow;

$queryBuilder = new DoctrineQueryBuilder(
$session = new DoctrineQueryBuilder(
DriverManager::getConnection([
'driver' => 'pdo_pgsql',
// ... driver options.
])
);

$result = $queryBuilder
$result = $session
->executeQuery(
<<<SQL
SELECT * FROM "some_table"
Expand Down Expand Up @@ -142,7 +142,7 @@ to be passed while iterating.

```php

$result = $queryBuilder
$result = $session
->executeQuery(
<<<SQL
SELECT "id", "name", "email", "created_at" FROM "users"
Expand Down
9 changes: 5 additions & 4 deletions docs/content/query/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ session was connected.
The schema manager is an object that allows you to introspect and alter your
database schema.

Once you have a functional bridge instance, you may fetch the *schema manager* instance:
Once you have a functional database session instance, you may fetch the
*schema manager* instance:

```php
use MakinaCorpus\QueryBuilder\Bridge\Bridge;
use MakinaCorpus\QueryBuilder\DatabaseSession;

\assert($bridge instanceof Bridge);
\assert($session instanceof DatabaseSession);

$schemaManager = $bridge->getSchemaManager();
$schemaManager = $session->getSchemaManager();
```

`$schemaManager` will be an instance of `MakinaCorpus\QueryBuilder\Schema\SchemaManager`.
Expand Down
33 changes: 23 additions & 10 deletions docs/content/query/transaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Introduction

When used in conjunction with a bridge, you may use transactions.
As soon as you have a working `DatabaseSession` instance, you may use transactions.

SQL transaction supports the given level of features:

Expand All @@ -16,13 +16,21 @@ Consider that you are manipulating a brige instance, which name is `$brige`, you
simply start a transaction this way:

```php
$transaction = $bridge->beginTransaction();
use MakinaCorpus\QueryBuilder\DatabaseSession;

assert($session instanceof DatabaseSession);

$transaction = $session->beginTransaction();
```

You may also create the stub object for it, then start it later:

```php
$transaction = $bridge->createTransaction();
use MakinaCorpus\QueryBuilder\DatabaseSession;

assert($session instanceof DatabaseSession);

$transaction = $session->createTransaction();

$transaction->isStarted(); // returns false

Expand All @@ -42,11 +50,14 @@ $transaction->commit();
Error handling is up to you, we advice writing such algorightm to do it right:

```sql
use MakinaCorpus\QueryBuilder\Error\Bridge\ServerError;
use MakinaCorpus\QueryBuilder\DatabaseSession;
use MakinaCorpus\QueryBuilder\Error\Server\ServerError;

assert($session instanceof DatabaseSession);

$transaction = null;
try {
$transaction = $bridge->beginTransaction();
$transaction = $session->beginTransaction();

// ... you SQL statements here ...

Expand All @@ -61,15 +72,17 @@ try {
}
```

Transaction `ROLLBACK` is never issued automatically, one exception stands: if the transaction
objet goes out of scope, when the destructor is called, then `ROLLBACK` is issued.
Transaction `ROLLBACK` is never issued automatically, one exception stands: if
the transaction objet goes out of scope, when the destructor is called, then
`ROLLBACK` is issued.

All transactions must be `COMMIT` explicitely, or will be `ROLLBACK` later.

:::warning
The bridge is a single SQL session, which means that if you start a transaction, it will
remain in memory until it is being commited or rollbacked. Code later in stack will issue
SQL queries in the same transaction until it's finished.
Transaction is shared for the `DatabaseSession` instance which means that if
you start a transaction, it will remain in memory until it is being commited
or rollbacked. Code later in stack will issue SQL queries in the same
transaction until it's finished.
:::

## Savepoints
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/AbstractBridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use MakinaCorpus\QueryBuilder\Converter\Converter;
use MakinaCorpus\QueryBuilder\Converter\ConverterPluginRegistry;
use MakinaCorpus\QueryBuilder\DefaultQueryBuilder;
use MakinaCorpus\QueryBuilder\Error\Bridge\TransactionError;
use MakinaCorpus\QueryBuilder\Error\QueryBuilderError;
use MakinaCorpus\QueryBuilder\Error\Server\TransactionError;
use MakinaCorpus\QueryBuilder\Error\UnsupportedFeatureError;
use MakinaCorpus\QueryBuilder\Escaper\Escaper;
use MakinaCorpus\QueryBuilder\Expression;
Expand Down
8 changes: 0 additions & 8 deletions src/Bridge/Bridge.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace MakinaCorpus\QueryBuilder\Bridge;

use MakinaCorpus\QueryBuilder\DatabaseSession;
use MakinaCorpus\QueryBuilder\Schema\SchemaManager;
use MakinaCorpus\QueryBuilder\Writer\Writer;

interface Bridge extends DatabaseSession
Expand Down Expand Up @@ -54,13 +53,6 @@ public function isVersionGreaterOrEqualThan(string $version): bool;
*/
public function getWriter(): Writer;

/**
* Get schema manager.
*
* @experimental
*/
public function getSchemaManager(): SchemaManager;

/**
* Free everything.
*/
Expand Down
Loading

0 comments on commit c8ada84

Please sign in to comment.