Feature: #102587 - Introduce driver middleware interface UsableForConnectionInterface
See forge#102587
Description
Since v3, Doctrine DBAL supports adding custom driver middlewares. 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.
Global driver middlewares and connection driver middlewares are available and configuration has been enhanced with the DependencyOrderingService.
That means, that 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 specific configuration or drivers, if a middleware needs only be executed for a subset, for example only specific drivers.
TYPO3 now provides a custom \TYPO3\
driver middleware interface which requires the implementation of the method
public function canBeUsedForConnection(
string $identifier,
array $connectionParams
): bool {}
This allows to decide if a middleware should be used for specific connection,
either based on the $connection
or the $connection
,
for example the concrete $connection
.
Note
Real use cases to use this interface should be rare edge cases, usually a driver middleware should only be configured on a connection where is needed - or does not harm if used for all connection types as global driver middleware.
Custom driver middleware example using the interface
namespace MyVendor\MyExt\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 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);
// @todo Do something custom on connect, for example wrapping the driver
// connection class or executing some queries on connect.
return $connection;
}
}
namespace MyVendor\MyExt\DoctrineDBAL;
use Doctrine\DBAL\Driver as DoctrineDriverInterface;
use MyVendor\MyExt\DoctrineDBAL\CustomDriver as MyCustomDriver;
use TYPO3\CMS\Core\Database\Middleware\UsableForConnectionInterface;
final class CustomMiddleware implements 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';
}
}
use MyVendor\MyExt\DoctrineDBAL\CustomMiddleware;
$middlewareConfiguration = [
'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',
],
];
// Register middleware globally, to include it for all connection which
// uses the 'pdo_sqlite' driver.
$GLOBALS['TYPO3_CONF_VARS']['DB']['globalDriverMiddlewares']['myvendor/myext/custom-pdosqlite-driver-middleware']
= $middlewareConfiguration;
Impact
Extension author can provide conditional-based Doctrine driver middlewares by
implementing the \TYPO3\
along with the can
method.