Doctrine DBAL driver middlewares
New in version 12.3
Introduction
Doctrine DBAL supports custom driver middlewares since version 3. These
middlewares act as a decorator around the actual Driver
component.
Subsequently, the Connection
, Statement
and Result
components can be
decorated as well. These middlewares must implement the
\Doctrine\
interface.
A common use case would be a middleware to implement SQL logging capabilities.
For more information on driver middlewares, see the Architecture chapter of the Doctrine DBAL documentation. Furthermore, look up the implementation of the EXT:adminpanel/Classes/Log/DoctrineSqlLoggingMiddleware.php (GitHub) in the Admin Panel system extension as an example.
Global driver middlewares and driver middlewares for a specific connection are combined for a connection. They are sortable.
Register a global driver middleware
New in version 13.0
Global driver middlewares are applied to all configured connections.
Example:
<?php
declare(strict_types=1);
use MyVendor\MyExtension\Doctrine\Driver\CustomGlobalDriverMiddleware;
defined('TYPO3') or die();
$GLOBALS['TYPO3_CONF_VARS']['DB']['globalDriverMiddlewares']['my-ext/custom-global-driver-middleware'] = [
'target' => CustomGlobalDriverMiddleware::class,
'after' => [
// NOTE: Custom driver middleware should be registered after essential
// TYPO3 Core driver middlewares. Use the following identifiers
// to ensure that.
'typo3/core/custom-platform-driver-middleware',
'typo3/core/custom-pdo-driver-result-middleware',
],
];
Disable a global middleware for a specific connection
Example:
<?php
declare(strict_types=1);
use MyVendor\MyExtension\Doctrine\Driver\CustomGlobalDriverMiddleware;
defined('TYPO3') or die();
// Register a global middleware
$GLOBALS['TYPO3_CONF_VARS']['DB']['globalDriverMiddlewares']['my-ext/custom-global-driver-middleware'] = [
'target' => CustomGlobalDriverMiddleware::class,
'after' => [
// NOTE: Custom driver middleware should be registered after essential
// TYPO3 Core driver middlewares. Use the following identifiers
// to ensure that.
'typo3/core/custom-platform-driver-middleware',
'typo3/core/custom-pdo-driver-result-middleware',
],
];
// Disable a global driver middleware for a specific connection
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['SecondDatabase']['driverMiddlewares']['my-ext/custom-global-driver-middleware']['disabled'] = true;
Register a driver middleware for a specific connection
New in version 12.3
Deprecated since version 13.0
Using the simple 'identifier' => My
configuration schema
to register Doctrine DBAL middlewares for a connection is now deprecated in
favour of using a
sortable registration configuration
similar to the PSR-15 middleware registration.
See Migration
and Registration for driver middlewares for TYPO3 v12 and v13.
In this example, the custom driver middleware My
is added
to the Default
connection:
<?php
declare(strict_types=1);
use MyVendor\MyExtension\Doctrine\Driver\MyDriverMiddleware;
defined('TYPO3') or die();
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['driverMiddlewares']['driver-middleware-identifier'] = [
'target' => MyDriverMiddleware::class,
'after' => [
// NOTE: Custom driver middleware should be registered after essential
// TYPO3 Core driver middlewares. Use the following identifiers
// to ensure that.
'typo3/core/custom-platform-driver-middleware',
'typo3/core/custom-pdo-driver-result-middleware',
],
];
Migration
For example:
<?php
declare(strict_types=1);
defined('TYPO3') or die();
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['driverMiddlewares']['driver-middleware-identifier']
= MyDriverMiddlewareClass::class;
needs to be converted to:
<?php
declare(strict_types=1);
use MyVendor\MyExtension\Doctrine\Driver\MyDriverMiddleware;
defined('TYPO3') or die();
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['driverMiddlewares']['driver-middleware-identifier'] = [
'target' => MyDriverMiddleware::class,
'after' => [
// NOTE: Custom driver middleware should be registered after essential
// TYPO3 Core driver middlewares. Use the following identifiers
// to ensure that.
'typo3/core/custom-platform-driver-middleware',
'typo3/core/custom-pdo-driver-result-middleware',
],
];
Registration for driver middlewares for TYPO3 v12 and v13
Extension authors providing dual Core support in one extension version can use
the Typo3Version
class to provide the configuration suitable for the Core
version and avoiding the deprecation notice:
<?php
declare(strict_types=1);
use MyVendor\MyExtension\Doctrine\Driver\MyDriverMiddleware;
use TYPO3\CMS\Core\Information\Typo3Version;
defined('TYPO3') or die();
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['Default']['driverMiddlewares']['driver-middleware-identifier']
= ((new Typo3Version())->getMajorVersion() < 13)
? MyDriverMiddleware::class
: [
'target' => MyDriverMiddleware::class,
'after' => [
// NOTE: Custom driver middleware should be registered after essential
// TYPO3 Core driver middlewares. Use the following identifiers
// to ensure that.
'typo3/core/custom-platform-driver-middleware',
'typo3/core/custom-pdo-driver-result-middleware',
],
];
Sorting of driver middlewares
New in version 13.0
Global driver middlewares and connection driver middlewares are combined for a connection.
TYPO3 makes the global and connection driver middlewares sortable similar to the PSR-15 middleware stack. The available structure for a middleware configuration is:
target
-
- Data type
-
string
- Required
-
yes
The fully-qualified class name of the driver middleware.
before
-
- Data type
-
list of strings
- Required
-
no
- Default
-
[]
A list of middleware identifiers the current middleware should be registered before.
after
-
- Data type
-
list of strings
- Required
-
no
- Default
-
[]
A list of middleware identifiers the current middleware should be registered after.
Note
All custom driver middlewares, global or connection-based, should be placed after the
'typo3/
andcore/ custom- platform- driver- middleware' 'typo3/
driver middlewares to ensure essential Core driver middlewares have been processed first.core/ custom- pdo- driver- result- middleware'
disabled
-
- Data type
-
boolean
- Required
-
no
- Default
-
false
It can be used to disable a global middleware for a specific connection.
Warning
Do not disable global driver middlewares provided by TYPO3 - they are essential.
Example:
<?php
declare(strict_types=1);
use MyVendor\MyExtension\Doctrine\Driver\CustomGlobalDriverMiddleware;
defined('TYPO3') or die();
// Register global driver middleware
$GLOBALS['TYPO3_CONF_VARS']['DB']['globalDriverMiddlewares']['global-driver-middleware-identifier'] = [
'target' => CustomGlobalDriverMiddleware::class,
'disabled' => false,
'after' => [
// NOTE: Custom driver middleware should be registered after essential
// TYPO3 Core driver middlewares. Use the following identifiers
// to ensure that.
'typo3/core/custom-platform-driver-middleware',
'typo3/core/custom-pdo-driver-result-middleware',
],
'before' => [
'some-driver-middleware-identifier',
],
];
// Disable a global driver middleware for a connection
$GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']['SecondDatabase']['driverMiddlewares']['global-driver-middleware-identifier'] = [
// To disable a global driver middleware, setting disabled to true for a
// connection is enough. Repeating target, after and/or before configuration
// is not required.
'disabled' => true,
];
Tip
If the lowlevel system extension is installed and active, a Doctrine DBAL Driver Middlewares section is provided in the System > Configuration module to view the raw middleware configuration and the ordered middlewares for each connection:
The interface UsableForConnectionInterface
New in version 13.0
Note
Real use cases for this interface should be rare edge cases. Typically, a driver middleware should only be configured on a connection where it is needed - or does not harm, if used for all connection types as a global driver middleware.
Doctrine DBAL driver middlewares can be registered globally for all connections or for specific connections. Due to the nature of the decorator pattern, it may become hard to determine for a specific configuration or drivers, if a middleware needs to be executed only for a subset, for example, only specific drivers.
TYPO3 provides a custom \TYPO3\
driver middleware interface which requires the implementation of the method
can
:
- interface UsableForConnectionInterface
-
- Fully qualified name
-
\TYPO3\
CMS\ Core\ Database\ Middleware\ Usable For Connection Interface
Custom driver middleware can implement this interface to decide per connection and connection configuration if it should be used or not. For example, registering a global driver middleware which only takes affect on connections using a specific driver like
pdo_
.sqlite Usually this should be a rare case and mostly a driver middleware can be simply configured as a connection middleware directly, which leaves this more or less a special implementation detail for the TYPO3 core.
This allows to decide, if a middleware should be used for a specific connection,
either based on the $connection
or the $connection
,
for example the concrete $connection
.
Example
The custom driver:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\DoctrineDBAL;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
// Using the abstract class minimize the methods to implement and therefore
// reduces a lot of boilerplate code. Override only methods that needed to be
// customized.
use Doctrine\DBAL\Driver\Middleware\AbstractDriverMiddleware;
final class CustomDriver extends AbstractDriverMiddleware
{
public function connect(#[\SensitiveParameter] array $params): DriverConnection
{
$connection = parent::connect($params);
// Do something custom on connect, for example wrapping the driver
// connection class or executing some queries on connect.
return $connection;
}
}
The custom driver middleware which implements the
\TYPO3\
:
<?php
declare(strict_types=1);
namespace MyVendor\MyExtension\DoctrineDBAL;
use Doctrine\DBAL\Driver as DoctrineDriverInterface;
use Doctrine\DBAL\Driver\Middleware as DoctrineDriverMiddlewareInterface;
use MyVendor\MyExtension\DoctrineDBAL\CustomDriver as MyCustomDriver;
use TYPO3\CMS\Core\Database\Middleware\UsableForConnectionInterface;
final class CustomMiddleware implements DoctrineDriverMiddlewareInterface, UsableForConnectionInterface
{
public function wrap(DoctrineDriverInterface $driver): DoctrineDriverInterface
{
// Wrap the original or already wrapped driver with our custom driver
// decoration class to provide additional features.
return new MyCustomDriver($driver);
}
public function canBeUsedForConnection(
string $identifier,
array $connectionParams,
): bool {
// Only use this driver middleware, if the configured connection driver
// is 'pdo_sqlite' (sqlite using php-ext PDO).
return ($connectionParams['driver'] ?? '') === 'pdo_sqlite';
}
}
Register the custom driver middleware:
<?php
declare(strict_types=1);
use MyVendor\MyExtension\DoctrineDBAL\CustomMiddleware;
defined('TYPO3') or die();
// Register middleware globally, to include it for all connections which
// uses the 'pdo_sqlite' driver.
$GLOBALS['TYPO3_CONF_VARS']['DB']['globalDriverMiddlewares']['my-ext/custom-pdosqlite-driver-middleware'] = [
'target' => CustomMiddleware::class,
'after' => [
// NOTE: Custom driver middleware should be registered after essential
// TYPO3 Core driver middlewares. Use the following identifiers
// to ensure that.
'typo3/core/custom-platform-driver-middleware',
'typo3/core/custom-pdo-driver-result-middleware',
],
];