Added Db::beforeExecuting() to register a hook which to be run just before a database query is executed. (#4908)

Co-authored-by: 李铭昕 <715557344@qq.com>
This commit is contained in:
Deeka Wong 2022-07-08 11:16:41 +08:00 committed by GitHub
parent 5ed82f8c0f
commit 13ab808087
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 8 deletions

View File

@ -51,6 +51,7 @@ composer analyse
- [#4700](https://github.com/hyperf/hyperf/pull/4700) Support coroutine style server for `socketio-server`.
- [#4852](https://github.com/hyperf/hyperf/pull/4852) Added `NullDisableEventDispatcher` to disable event dispatcher by default.
- [#4866](https://github.com/hyperf/hyperf/pull/4866) [#4869](https://github.com/hyperf/hyperf/pull/4869) Added Annotation `Scene` which use scene in FormRequest easily.
- [#4908](https://github.com/hyperf/hyperf/pull/4908) Added `Db::beforeExecuting()` to register a hook which to be run just before a database query is executed.
## Optimized

View File

@ -38,14 +38,14 @@ class Connection implements ConnectionInterface
/**
* The active PDO connection.
*
* @var \Closure|\PDO
* @var Closure|PDO
*/
protected $pdo;
/**
* The active PDO connection used for reads.
*
* @var \Closure|\PDO
* @var Closure|PDO
*/
protected $readPdo;
@ -149,10 +149,17 @@ class Connection implements ConnectionInterface
*/
protected static array $resolvers = [];
/**
* All the callbacks that should be invoked before a query is executed.
*
* @var Closure[]
*/
protected static array $beforeExecutingCallbacks = [];
/**
* Create a new database connection instance.
*
* @param \Closure|\PDO $pdo
* @param Closure|PDO $pdo
* @param string $database
* @param string $tablePrefix
*/
@ -489,6 +496,22 @@ class Connection implements ConnectionInterface
$this->setPdo(null)->setReadPdo(null);
}
/**
* Register a hook to be run just before a database query is executed.
*/
public static function beforeExecuting(Closure $callback): void
{
static::$beforeExecutingCallbacks[] = $callback;
}
/**
* Clear all hooks which will be run before a database query.
*/
public static function clearBeforeExecutingCallbacks(): void
{
static::$beforeExecutingCallbacks = [];
}
/**
* Register a database query listener with the connection.
*/
@ -623,7 +646,7 @@ class Connection implements ConnectionInterface
/**
* Set the PDO connection.
*
* @param null|\Closure|\PDO $pdo
* @param null|Closure|PDO $pdo
* @return $this
*/
public function setPdo($pdo)
@ -638,7 +661,7 @@ class Connection implements ConnectionInterface
/**
* Set the PDO connection used for reading.
*
* @param null|\Closure|\PDO $pdo
* @param null|Closure|PDO $pdo
* @return $this
*/
public function setReadPdo($pdo)
@ -983,7 +1006,7 @@ class Connection implements ConnectionInterface
/**
* Execute the given callback in "dry run" mode.
*
* @param \Closure $callback
* @param Closure $callback
* @return array
*/
protected function withFreshQueryLog($callback)
@ -1014,6 +1037,10 @@ class Connection implements ConnectionInterface
*/
protected function run(string $query, array $bindings, Closure $callback)
{
foreach (static::$beforeExecutingCallbacks as $beforeExecutingCallback) {
$beforeExecutingCallback($query, $bindings, $this);
}
$this->reconnectIfMissingConnection();
$start = microtime(true);

View File

@ -452,6 +452,35 @@ class ModelRealBuilderTest extends TestCase
$this->assertSame('ref', $res[0]->type);
}
public function testBeforeExecuting()
{
$container = $this->getContainer();
$container->shouldReceive('get')->with(Db::class)->andReturn(new Db($container));
$res = Db::selectOne('SELECT * FROM `user` WHERE id = ?;', [1]);
$this->assertSame('Hyperf', $res->name);
try {
$chan = new Channel(2);
Db::beforeExecuting(function (string $sql, array $bindings, Connection $connection) use ($chan) {
$this->assertSame(null, $connection->getConfig('name'));
$chan->push(1);
});
Db::beforeExecuting(function (string $sql, array $bindings, Connection $connection) use ($chan) {
$this->assertSame('SELECT * FROM `user` WHERE id = ?;', $sql);
$this->assertSame([1], $bindings);
$chan->push(2);
});
$res = Db::selectOne('SELECT * FROM `user` WHERE id = ?;', [1]);
$this->assertSame('Hyperf', $res->name);
$this->assertSame(1, $chan->pop(1));
$this->assertSame(2, $chan->pop(1));
} finally {
Connection::clearBeforeExecutingCallbacks();
}
}
protected function getContainer()
{
$dispatcher = Mockery::mock(EventDispatcherInterface::class);

View File

@ -11,7 +11,9 @@ declare(strict_types=1);
*/
namespace Hyperf\DbConnection;
use Closure;
use Generator;
use Hyperf\Database\Connection as Conn;
use Hyperf\Database\ConnectionInterface;
use Hyperf\Database\ConnectionResolverInterface;
use Hyperf\Database\Query\Builder;
@ -33,12 +35,12 @@ use Psr\Container\ContainerInterface;
* @method static int affectingStatement(string $query, array $bindings = [])
* @method static bool unprepared(string $query)
* @method static array prepareBindings(array $bindings)
* @method static mixed transaction(\Closure $callback, int $attempts = 1)
* @method static mixed transaction(Closure $callback, int $attempts = 1)
* @method static void beginTransaction()
* @method static void rollBack()
* @method static void commit()
* @method static int transactionLevel()
* @method static array pretend(\Closure $callback)
* @method static array pretend(Closure $callback)
* @method static ConnectionInterface connection(string $pool)
*/
class Db
@ -69,4 +71,9 @@ class Db
$resolver = $this->container->get(ConnectionResolverInterface::class);
return $resolver->connection($name);
}
public static function beforeExecuting(Closure $closure): void
{
Conn::beforeExecuting($closure);
}
}