Skip to content

Commit e09eea4

Browse files
authored
Merge pull request #20 from WyriHaximus/middleware
Middleware
2 parents 3464d04 + bdd9b71 commit e09eea4

14 files changed

+359
-39
lines changed

infection.json.dist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
},
2828
"IncrementInteger": {
2929
"ignore": [
30+
"WyriHaximus\\React\\SimpleORM\\Repository::create",
3031
"WyriHaximus\\React\\SimpleORM\\Repository::count",
3132
"WyriHaximus\\React\\SimpleORM\\Repository::buildTree",
3233
"WyriHaximus\\React\\SimpleORM\\Repository::translateFieldName"

phpstan.neon

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ parameters:
22
ignoreErrors:
33
- '#Class WyriHaximus\\React\\Tests\\SimpleORM\\Stub\\[a-zA-Z0-9_]+ is neither abstract nor final.#'
44
- '#Cannot call method getDocBlockTypes() on Roave\\BetterReflection\\Reflection\\ReflectionProperty|null.#'
5-
- '#Constructor in WyriHaximus\\React\\SimpleORM\\Client has parameter \$annotationReader with default value.#'
65
- '#Argument of an invalid type WyriHaximus\\React\\SimpleORM\\Annotation\\[a-zA-Z0-9_]+ supplied for foreach, only iterables are supported.#'
76
- '#Variable property access on \$this\(WyriHaximus\\React\\SimpleORM\\Annotation\\[a-zA-Z0-9_]+\).#'
87
classesAllowedToBeExtended:

src/Client.php

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
use Plasma\SQL\Grammar\PostgreSQL;
99
use Plasma\SQL\QueryBuilder;
1010
use Rx\Observable;
11+
use WyriHaximus\React\SimpleORM\Middleware\ExecuteQueryMiddleware;
12+
use WyriHaximus\React\SimpleORM\Middleware\GrammarMiddleware;
13+
use function ApiClients\Tools\Rx\unwrapObservableFromPromise;
1114

1215
final class Client implements ClientInterface
1316
{
@@ -20,10 +23,37 @@ final class Client implements ClientInterface
2023
/** @var Repository[] */
2124
private $repositories = [];
2225

23-
public function __construct(PgClient $client, ?Reader $annotationReader = null)
26+
/** @var MiddlewareRunner */
27+
private $middlewareRunner;
28+
29+
/**
30+
* @param array<int, MiddlewareInterface> $middleware
31+
*/
32+
public static function create(PgClient $client, MiddlewareInterface ...$middleware): self
33+
{
34+
return new self($client, new AnnotationReader(), ...$middleware);
35+
}
36+
37+
/**
38+
* @param array<int, MiddlewareInterface> $middleware
39+
*/
40+
public static function createWithAnnotationReader(PgClient $client, Reader $annotationReader, MiddlewareInterface ...$middleware): self
41+
{
42+
return new self($client, $annotationReader, ...$middleware);
43+
}
44+
45+
/**
46+
* @param array<int, MiddlewareInterface> $middleware
47+
*/
48+
private function __construct(PgClient $client, Reader $annotationReader, MiddlewareInterface ...$middleware)
2449
{
2550
$this->client = $client;
26-
$this->entityInspector = new EntityInspector($annotationReader ?? new AnnotationReader());
51+
$this->entityInspector = new EntityInspector($annotationReader);
52+
53+
$middleware[] = new GrammarMiddleware(new PostgreSQL());
54+
$middleware[] = new ExecuteQueryMiddleware($this->client);
55+
56+
$this->middlewareRunner = new MiddlewareRunner(...$middleware);
2757
}
2858

2959
public function getRepository(string $entity): RepositoryInterface
@@ -37,8 +67,6 @@ public function getRepository(string $entity): RepositoryInterface
3767

3868
public function query(QueryBuilder $query): Observable
3969
{
40-
$query = $query->withGrammar(new PostgreSQL());
41-
42-
return $this->client->executeStatement($query->getQuery(), $query->getParameters());
70+
return unwrapObservableFromPromise($this->middlewareRunner->query($query));
4371
}
4472
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace WyriHaximus\React\SimpleORM\Middleware;
4+
5+
use PgAsync\Client as PgClient;
6+
use Plasma\SQL\QueryBuilder;
7+
use React\Promise\PromiseInterface;
8+
use WyriHaximus\React\SimpleORM\MiddlewareInterface;
9+
use function React\Promise\resolve;
10+
11+
final class ExecuteQueryMiddleware implements MiddlewareInterface
12+
{
13+
/** @var PgClient */
14+
private $client;
15+
16+
public function __construct(PgClient $client)
17+
{
18+
$this->client = $client;
19+
}
20+
21+
public function query(QueryBuilder $query): PromiseInterface
22+
{
23+
return resolve($this->client->executeStatement($query->getQuery(), $query->getParameters()));
24+
}
25+
}

src/Middleware/GrammarMiddleware.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace WyriHaximus\React\SimpleORM\Middleware;
4+
5+
use Plasma\SQL\GrammarInterface;
6+
use Plasma\SQL\QueryBuilder;
7+
use React\Promise\PromiseInterface;
8+
use WyriHaximus\React\SimpleORM\MiddlewareInterface;
9+
use function React\Promise\resolve;
10+
11+
final class GrammarMiddleware implements MiddlewareInterface
12+
{
13+
/** @var GrammarInterface */
14+
private $grammer;
15+
16+
public function __construct(GrammarInterface $grammer)
17+
{
18+
$this->grammer = $grammer;
19+
}
20+
21+
public function query(QueryBuilder $query): PromiseInterface
22+
{
23+
return resolve($query->withGrammar($this->grammer));
24+
}
25+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace WyriHaximus\React\SimpleORM\Middleware;
4+
5+
use PgAsync\Client as PgClient;
6+
use Plasma\SQL\QueryBuilder;
7+
use React\Promise\PromiseInterface;
8+
use WyriHaximus\React\SimpleORM\MiddlewareInterface;
9+
use function React\Promise\resolve;
10+
11+
final class QueryCountMiddleware implements MiddlewareInterface
12+
{
13+
private const ZERO = 0;
14+
15+
/** @var int */
16+
private $count = self::ZERO;
17+
18+
public function query(QueryBuilder $query): PromiseInterface
19+
{
20+
$this->count++;
21+
22+
return resolve($query);
23+
}
24+
25+
public function getCount(): int
26+
{
27+
return $this->count;
28+
}
29+
30+
public function resetCount(): void
31+
{
32+
$this->count = 0;
33+
}
34+
}

src/MiddlewareInterface.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace WyriHaximus\React\SimpleORM;
4+
5+
use Plasma\SQL\QueryBuilder;
6+
use React\Promise\PromiseInterface;
7+
8+
interface MiddlewareInterface
9+
{
10+
/**
11+
* Returns the (modified) query through a promise.
12+
*
13+
* @param QueryBuilder $query
14+
*
15+
* @return PromiseInterface
16+
*/
17+
public function query(QueryBuilder $query): PromiseInterface;
18+
}

src/MiddlewareRunner.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace WyriHaximus\React\SimpleORM;
4+
5+
use Plasma\SQL\QueryBuilder;
6+
use Psr\Http\Message\ServerRequestInterface;
7+
use React\Promise\PromiseInterface;
8+
9+
final class MiddlewareRunner
10+
{
11+
/** @var MiddlewareInterface[] */
12+
private $middleware;
13+
14+
/**
15+
* @param array<int, MiddlewareInterface> $middleware
16+
*/
17+
public function __construct(MiddlewareInterface ...$middleware)
18+
{
19+
$this->middleware = $middleware;
20+
}
21+
22+
public function query(QueryBuilder $query): PromiseInterface
23+
{
24+
return $this->call($query, 0);
25+
}
26+
27+
private function call(QueryBuilder $query, int $position): PromiseInterface
28+
{
29+
// final request handler will be invoked without hooking into the promise
30+
if (!array_key_exists($position + 1, $this->middleware)) {
31+
return $this->middleware[$position]->query($query);
32+
}
33+
34+
return $this->middleware[$position]->query($query)->then(function (QueryBuilder $query) use ($position) {
35+
return $this->call($query, $position + 1);
36+
});
37+
}
38+
}

src/Repository.php

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Plasma\SQL\QueryBuilder;
66
use Plasma\SQL\QueryExpressions\Fragment;
77
use Ramsey\Uuid\Uuid;
8+
use React\Promise\LazyPromise;
89
use React\Promise\Promise;
910
use React\Promise\PromiseInterface;
1011
use Rx\Observable;
@@ -278,41 +279,43 @@ private function buildTree(array $row, InspectedEntityInterface $entity, string
278279
}
279280

280281
if ($join->getType() === 'inner' && ($join->getLazy() === JoinInterface::IS_LAZY || $entity->getClass() === $join->getEntity()->getClass())) {
281-
$tree[$join->getProperty()] = new Promise(function (callable $resolve, callable $reject) use ($row, $join, $tableKey): void {
282-
foreach ($join->getClause() as $clause) {
283-
if ($row[$this->tableAliases[$tableKey]][$clause->getLocalKey()] === null) {
284-
$resolve(null);
285-
286-
return;
282+
$tree[$join->getProperty()] = new LazyPromise(function () use ($row, $join, $tableKey): PromiseInterface {
283+
return new Promise(function (callable $resolve, callable $reject) use ($row, $join, $tableKey): void {
284+
foreach ($join->getClause() as $clause) {
285+
if ($row[$this->tableAliases[$tableKey]][$clause->getLocalKey()] === null) {
286+
$resolve(null);
287+
288+
return;
289+
}
287290
}
288-
}
289291

290-
$where = [];
291-
292-
foreach ($join->getClause() as $clause) {
293-
$onLeftSide = $clause->getForeignKey();
294-
if ($clause->getForeignFunction() !== null) {
295-
/** @psalm-suppress PossiblyNullOperand */
296-
$onLeftSide = new Fragment($clause->getForeignFunction() . '(' . $onLeftSide . ')');
297-
}
298-
if ($clause->getForeignCast() !== null) {
299-
/** @psalm-suppress PossiblyNullOperand */
300-
$onLeftSide = new Fragment('CAST(' . (string)$onLeftSide . ' AS ' . $clause->getForeignCast() . ')');
292+
$where = [];
293+
294+
foreach ($join->getClause() as $clause) {
295+
$onLeftSide = $clause->getForeignKey();
296+
if ($clause->getForeignFunction() !== null) {
297+
/** @psalm-suppress PossiblyNullOperand */
298+
$onLeftSide = new Fragment($clause->getForeignFunction() . '(' . $onLeftSide . ')');
299+
}
300+
if ($clause->getForeignCast() !== null) {
301+
/** @psalm-suppress PossiblyNullOperand */
302+
$onLeftSide = new Fragment('CAST(' . (string)$onLeftSide . ' AS ' . $clause->getForeignCast() . ')');
303+
}
304+
305+
$where[] = [
306+
$onLeftSide,
307+
'=',
308+
$row[$this->tableAliases[$tableKey]][$clause->getLocalKey()],
309+
];
301310
}
302311

303-
$where[] = [
304-
$onLeftSide,
305-
'=',
306-
$row[$this->tableAliases[$tableKey]][$clause->getLocalKey()],
307-
];
308-
}
309-
310-
$this->client
311-
->getRepository($join->getEntity()
312+
$this->client
313+
->getRepository($join->getEntity()
312314
->getClass())
313-
->fetch($where, [], self::SINGLE)
314-
->toPromise()
315-
->then($resolve, $reject);
315+
->fetch($where, [], self::SINGLE)
316+
->toPromise()
317+
->then($resolve, $reject);
318+
});
316319
});
317320

318321
continue;

tests/ClientTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ protected function setUp(): void
4040

4141
$this->pgClient = $this->prophesize(PgClient::class);
4242
$this->annotationReader = $this->prophesize(Reader::class);
43-
$this->client = new Client($this->pgClient->reveal(), $this->annotationReader->reveal());
43+
$this->client = Client::createWithAnnotationReader($this->pgClient->reveal(), $this->annotationReader->reveal());
4444
}
4545

4646
public function testGetRepository(): void

0 commit comments

Comments
 (0)