From 5915180985e31d222f11680c7e2d78390338b7dd Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Mon, 18 Mar 2024 20:39:19 +0100 Subject: [PATCH] Template types --- src/Adapter/Postgres.php | 5 +- src/Annotation/Clause.php | 12 +- src/Annotation/InnerJoin.php | 8 +- src/Annotation/JoinInterface.php | 4 +- src/Annotation/LeftJoin.php | 8 +- src/Annotation/Table.php | 4 +- src/Client.php | 25 +- src/ClientInterface.php | 9 + src/Configuration.php | 5 +- src/Connection.php | 39 +++ src/Entity/Field.php | 8 +- src/Entity/Join.php | 20 +- src/EntityInspector.php | 18 +- src/EntityInterface.php | 4 +- src/Hydrator.php | 13 +- src/InspectedEntity.php | 24 +- src/InspectedEntityInterface.php | 8 +- src/LazyInspectedEntity.php | 21 +- src/Middleware/QueryCountMiddleware.php | 11 +- src/MiddlewareRunner.php | 4 +- src/Query/Limit.php | 5 +- src/Query/Order.php | 4 +- src/Query/Order/Asc.php | 5 +- src/Query/Order/Desc.php | 5 +- src/Query/Where.php | 4 +- src/Query/Where/Expression.php | 14 +- src/Query/Where/Field.php | 14 +- src/Repository.php | 113 +++++---- src/RepositoryInterface.php | 31 ++- src/Tools/WithFieldsTrait.php | 4 +- tests/ClientTest.php | 13 +- tests/EntityInspectorTest.php | 12 +- tests/FunctionalTest.php | 233 +++++++----------- tests/HydratorTest.php | 18 +- tests/Middleware/QueryCountMiddlewareTest.php | 49 ++-- tests/RepositoryTest.php | 47 ++-- tests/Stub/BlogPostStub.php | 12 + tests/Stub/LogStub.php | 4 +- tests/Stub/UserStub.php | 6 + tests/types/basic.php | 15 ++ 40 files changed, 381 insertions(+), 477 deletions(-) create mode 100644 src/Connection.php create mode 100644 tests/types/basic.php diff --git a/src/Adapter/Postgres.php b/src/Adapter/Postgres.php index 10323c0..602568e 100644 --- a/src/Adapter/Postgres.php +++ b/src/Adapter/Postgres.php @@ -20,13 +20,10 @@ final class Postgres implements AdapterInterface { - private PgClient $client; - private EngineInterface $engine; - public function __construct(PgClient $client) + public function __construct(private PgClient $client) { - $this->client = $client; $this->engine = new PostgresEngine(); } diff --git a/src/Annotation/Clause.php b/src/Annotation/Clause.php index 841c3bc..5da6af0 100644 --- a/src/Annotation/Clause.php +++ b/src/Annotation/Clause.php @@ -24,9 +24,7 @@ final class Clause private ?string $foreign_function = null; //phpcs:enable - /** - * @param string[]|null[] $clause - */ + /** @param string[]|null[] $clause */ public function __construct(array $clause) { /** @psalm-suppress RawObjectIteration */ @@ -54,7 +52,7 @@ public function foreignKey(): string } /** @phpstan-ignore-next-line */ - public function localCast(): ?string + public function localCast(): string|null { //phpcs:disable return $this->local_cast; @@ -62,7 +60,7 @@ public function localCast(): ?string } /** @phpstan-ignore-next-line */ - public function foreignCast(): ?string + public function foreignCast(): string|null { //phpcs:disable return $this->foreign_cast; @@ -70,7 +68,7 @@ public function foreignCast(): ?string } /** @phpstan-ignore-next-line */ - public function localFunction(): ?string + public function localFunction(): string|null { //phpcs:disable return $this->local_function; @@ -78,7 +76,7 @@ public function localFunction(): ?string } /** @phpstan-ignore-next-line */ - public function foreignFunction(): ?string + public function foreignFunction(): string|null { //phpcs:disable return $this->foreign_function; diff --git a/src/Annotation/InnerJoin.php b/src/Annotation/InnerJoin.php index b02fd8f..4594d53 100644 --- a/src/Annotation/InnerJoin.php +++ b/src/Annotation/InnerJoin.php @@ -24,9 +24,7 @@ final class InnerJoin implements JoinInterface private bool $lazy = self::IS_NOT_LAZY; - /** - * @param string[]|array[]|bool[] $innerJoin - */ + /** @param string[]|array[]|bool[] $innerJoin */ public function __construct(array $innerJoin) { /** @psalm-suppress RawObjectIteration */ @@ -54,9 +52,7 @@ public function lazy(): bool return $this->lazy; } - /** - * @return Clause[] - */ + /** @return Clause[] */ public function clause(): array { return $this->clause; diff --git a/src/Annotation/JoinInterface.php b/src/Annotation/JoinInterface.php index 95b04de..de06bf6 100644 --- a/src/Annotation/JoinInterface.php +++ b/src/Annotation/JoinInterface.php @@ -17,8 +17,6 @@ public function property(): string; public function lazy(): bool; - /** - * @return Clause[] - */ + /** @return Clause[] */ public function clause(): array; } diff --git a/src/Annotation/LeftJoin.php b/src/Annotation/LeftJoin.php index a7f81b1..78588bd 100644 --- a/src/Annotation/LeftJoin.php +++ b/src/Annotation/LeftJoin.php @@ -24,9 +24,7 @@ final class LeftJoin implements JoinInterface private bool $lazy = self::IS_NOT_LAZY; - /** - * @param string[]|array[]|bool[] $leftJoin - */ + /** @param string[]|array[]|bool[] $leftJoin */ public function __construct(array $leftJoin) { /** @psalm-suppress RawObjectIteration */ @@ -54,9 +52,7 @@ public function lazy(): bool return $this->lazy; } - /** - * @return Clause[] - */ + /** @return Clause[] */ public function clause(): array { return $this->clause; diff --git a/src/Annotation/Table.php b/src/Annotation/Table.php index 72571d2..0710b9b 100644 --- a/src/Annotation/Table.php +++ b/src/Annotation/Table.php @@ -16,9 +16,7 @@ final class Table { private string $table; - /** - * @param string[] $table - */ + /** @param string[] $table */ public function __construct(array $table) { $this->table = current($table); /** @phpstan-ignore-line */ diff --git a/src/Client.php b/src/Client.php index aa58ab7..f0c6d94 100644 --- a/src/Client.php +++ b/src/Client.php @@ -11,20 +11,16 @@ use React\Promise\PromiseInterface; use Rx\Observable; -use function ApiClients\Tools\Rx\unwrapObservableFromPromise; use function array_key_exists; use function React\Promise\resolve; final class Client implements ClientInterface { - private AdapterInterface $adapter; - private EntityInspector $entityInspector; - /** @var array */ private array $repositories = []; - private MiddlewareRunner $middlewareRunner; + private Connection $connection; private QueryFactory $queryFactory; @@ -38,19 +34,23 @@ public static function createWithAnnotationReader(AdapterInterface $adapter, Con return new self($adapter, $configuration, $annotationReader, ...$middleware); } - private function __construct(AdapterInterface $adapter, Configuration $configuration, Reader $annotationReader, MiddlewareInterface ...$middleware) + private function __construct(private AdapterInterface $adapter, Configuration $configuration, Reader $annotationReader, MiddlewareInterface ...$middleware) { - $this->adapter = $adapter; $this->entityInspector = new EntityInspector($configuration, $annotationReader); $this->queryFactory = new QueryFactory($adapter->engine()); - $this->middlewareRunner = new MiddlewareRunner(...$middleware); + $this->connection = new Connection($this->adapter, new MiddlewareRunner(...$middleware)); } + /** + * @template T + * @param class-string $entity + * @return RepositoryInterface + */ public function repository(string $entity): RepositoryInterface { if (! array_key_exists($entity, $this->repositories)) { - $this->repositories[$entity] = new Repository($this->entityInspector->entity($entity), $this, $this->queryFactory); + $this->repositories[$entity] = new Repository($this->entityInspector->entity($entity), $this, $this->queryFactory, $this->connection); } return $this->repositories[$entity]; @@ -58,11 +58,6 @@ public function repository(string $entity): RepositoryInterface public function query(ExpressionInterface $query): Observable { - return unwrapObservableFromPromise($this->middlewareRunner->query( - $query, - function (ExpressionInterface $query): PromiseInterface { - return resolve($this->adapter->query($query)); - } - )); + return $this->connection->query($query); } } diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 44b5eaf..935d84a 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -9,7 +9,16 @@ interface ClientInterface { + + /** + * @template T + * @param class-string $entity + * @return RepositoryInterface + */ public function repository(string $entity): RepositoryInterface; + /** + * @deprecated This function will disappear at initial release + */ public function query(ExpressionInterface $query): Observable; } diff --git a/src/Configuration.php b/src/Configuration.php index 1df6538..c33e054 100644 --- a/src/Configuration.php +++ b/src/Configuration.php @@ -6,11 +6,8 @@ final class Configuration { - private string $tablePrefix = ''; - - public function __construct(string $tablePrefix) + public function __construct(private string $tablePrefix = '') { - $this->tablePrefix = $tablePrefix; } public function tablePrefix(): string diff --git a/src/Connection.php b/src/Connection.php new file mode 100644 index 0000000..f941217 --- /dev/null +++ b/src/Connection.php @@ -0,0 +1,39 @@ +middlewareRunner->query( + $query, + function (ExpressionInterface $query): PromiseInterface { + return resolve($this->adapter->query($query)); + }, + ))->mergeAll(); + } +} diff --git a/src/Entity/Field.php b/src/Entity/Field.php index 41489f7..04f8834 100644 --- a/src/Entity/Field.php +++ b/src/Entity/Field.php @@ -6,14 +6,8 @@ final class Field { - private string $name; - - private string $type; - - public function __construct(string $name, string $type) + public function __construct(private string $name, private string $type) { - $this->name = $name; - $this->type = $type; } public function name(): string diff --git a/src/Entity/Join.php b/src/Entity/Join.php index dc2f2d1..7c1f987 100644 --- a/src/Entity/Join.php +++ b/src/Entity/Join.php @@ -9,24 +9,12 @@ final class Join { - private InspectedEntityInterface $entity; - - private string $type; - - private string $property; - - private bool $lazy; - /** @var array */ private array $clause; - public function __construct(InspectedEntityInterface $entity, string $type, string $property, bool $lazy, Clause ...$clause) + public function __construct(private InspectedEntityInterface $entity, private string $type, private string $property, private bool $lazy, Clause ...$clause) { - $this->entity = $entity; - $this->type = $type; - $this->property = $property; - $this->lazy = $lazy; - $this->clause = $clause; + $this->clause = $clause; } public function entity(): InspectedEntityInterface @@ -49,9 +37,7 @@ public function lazy(): bool return $this->lazy; } - /** - * @return Clause[] - */ + /** @return Clause[] */ public function clause(): array { return $this->clause; diff --git a/src/EntityInspector.php b/src/EntityInspector.php index 085d19e..f3afddf 100644 --- a/src/EntityInspector.php +++ b/src/EntityInspector.php @@ -17,20 +17,14 @@ use function array_key_exists; use function current; use function method_exists; -use function WyriHaximus\iteratorOrArrayToArray; final class EntityInspector { - private Configuration $configuration; - private Reader $annotationReader; - /** @var InspectedEntityInterface[] */ private array $entities = []; - public function __construct(Configuration $configuration, Reader $annotationReader) + public function __construct(private Configuration $configuration, private Reader $annotationReader) { - $this->configuration = $configuration; - $this->annotationReader = $annotationReader; } public function entity(string $entity): InspectedEntityInterface @@ -51,13 +45,13 @@ public function entity(string $entity): InspectedEntityInterface * @phpstan-ignore-next-line * @psalm-suppress ArgumentTypeCoercion */ - $joins = iteratorOrArrayToArray($this->joins($class)); + $joins = [...$this->joins($class)]; /** @psalm-suppress ArgumentTypeCoercion */ $this->entities[$entity] = new InspectedEntity( $entity, $this->configuration->tablePrefix() . $tableAnnotation->table(), - iteratorOrArrayToArray($this->fields($class, $joins)), /** @phpstan-ignore-line */ - $joins + [...$this->fields($class, $joins)], /** @phpstan-ignore-line */ + $joins, ); } @@ -99,7 +93,7 @@ private function fields(ReflectionClass $class, array $joins): iterable } return (string) current($property->getDocBlockTypes()); - })($roaveProperty) + })($roaveProperty), ); } } @@ -123,7 +117,7 @@ private function joins(ReflectionClass $class): iterable $annotation->type(), $annotation->property(), $annotation->lazy(), - ...$annotation->clause() + ...$annotation->clause(), ); } } diff --git a/src/EntityInterface.php b/src/EntityInterface.php index c864845..84cad30 100644 --- a/src/EntityInterface.php +++ b/src/EntityInterface.php @@ -4,7 +4,9 @@ namespace WyriHaximus\React\SimpleORM; +/** + * @property string $id + */ interface EntityInterface { - public function id(): string; } diff --git a/src/Hydrator.php b/src/Hydrator.php index 451ed96..a2dcb21 100644 --- a/src/Hydrator.php +++ b/src/Hydrator.php @@ -58,7 +58,7 @@ public function hydrate(InspectedEntityInterface $inspectedEntity, array $data): $data[$join->property()] = $this->hydrate( $join->entity(), - $data[$join->property()] + $data[$join->property()], ); } @@ -72,9 +72,7 @@ public function hydrate(InspectedEntityInterface $inspectedEntity, array $data): return $this->hydrators[$class]->hydrate($data, new $class()); } - /** - * @return mixed[] - */ + /** @return mixed[] */ public function extract(InspectedEntityInterface $inspectedEntity, EntityInterface $entity): array { $class = $inspectedEntity->class(); @@ -83,12 +81,7 @@ public function extract(InspectedEntityInterface $inspectedEntity, EntityInterfa return $hydrator->extract($entity); } - /** - * @param mixed $value - * - * @return mixed - */ - private function castValueToCorrectType(Field $field, $value) + private function castValueToCorrectType(Field $field, mixed $value): mixed { if ($field->type() === 'int') { return (int) $value; diff --git a/src/InspectedEntity.php b/src/InspectedEntity.php index f3cce0a..d18c1e8 100644 --- a/src/InspectedEntity.php +++ b/src/InspectedEntity.php @@ -9,26 +9,12 @@ final class InspectedEntity implements InspectedEntityInterface { - private string $class; - - private string $table; - - /** @var Field[] */ - private array $fields = []; - - /** @var Join[] */ - private array $joins = []; - /** * @param Field[] $fields * @param Join[] $joins */ - public function __construct(string $class, string $table, array $fields, array $joins) + public function __construct(private string $class, private string $table, private array $fields = [], private array $joins = []) { - $this->class = $class; - $this->table = $table; - $this->fields = $fields; - $this->joins = $joins; } public function class(): string @@ -41,17 +27,13 @@ public function table(): string return $this->table; } - /** - * @return Field[] - */ + /** @return Field[] */ public function fields(): array { return $this->fields; } - /** - * @return Join[] - */ + /** @return Join[] */ public function joins(): array { return $this->joins; diff --git a/src/InspectedEntityInterface.php b/src/InspectedEntityInterface.php index b0418a2..23f6d72 100644 --- a/src/InspectedEntityInterface.php +++ b/src/InspectedEntityInterface.php @@ -13,13 +13,9 @@ public function class(): string; public function table(): string; - /** - * @return Field[] - */ + /** @return Field[] */ public function fields(): array; - /** - * @return Join[] - */ + /** @return Join[] */ public function joins(): array; } diff --git a/src/LazyInspectedEntity.php b/src/LazyInspectedEntity.php index 0a55bc8..0f15268 100644 --- a/src/LazyInspectedEntity.php +++ b/src/LazyInspectedEntity.php @@ -9,9 +9,7 @@ final class LazyInspectedEntity implements InspectedEntityInterface { - private string $class; - - private ?string $table = null; + private string|null $table = null; /** @var Field[] */ private array $fields = []; @@ -19,11 +17,10 @@ final class LazyInspectedEntity implements InspectedEntityInterface /** @var Join[] */ private array $joins = []; - private ?EntityInspector $entityInspector = null; + private EntityInspector|null $entityInspector = null; - public function __construct(EntityInspector $entityInspector, string $class) + public function __construct(EntityInspector $entityInspector, private string $class) { - $this->class = $class; $this->entityInspector = $entityInspector; } @@ -32,9 +29,7 @@ public function class(): string return $this->class; } - /** - * @psalm-suppress InvalidNullableReturnType - */ + /** @psalm-suppress InvalidNullableReturnType */ public function table(): string { if ($this->table === null) { @@ -48,9 +43,7 @@ public function table(): string return $this->table; } - /** - * @return Field[] - */ + /** @return Field[] */ public function fields(): array { if ($this->table === null) { @@ -60,9 +53,7 @@ public function fields(): array return $this->fields; } - /** - * @return Join[] - */ + /** @return Join[] */ public function joins(): array { if ($this->table === null) { diff --git a/src/Middleware/QueryCountMiddleware.php b/src/Middleware/QueryCountMiddleware.php index 17d25f6..79c930c 100644 --- a/src/Middleware/QueryCountMiddleware.php +++ b/src/Middleware/QueryCountMiddleware.php @@ -11,7 +11,7 @@ use Throwable; use WyriHaximus\React\SimpleORM\MiddlewareInterface; -use function hrtime; +use function Safe\hrtime; use function React\Promise\resolve; final class QueryCountMiddleware implements MiddlewareInterface @@ -28,11 +28,8 @@ final class QueryCountMiddleware implements MiddlewareInterface private int $completedCount = self::ZERO; - private int $slowQueryTime; - - public function __construct(int $slowQueryTime) + public function __construct(private int $slowQueryTime) { - $this->slowQueryTime = $slowQueryTime; } public function query(ExpressionInterface $query, callable $next): PromiseInterface @@ -87,9 +84,7 @@ function () use ($subject, &$handledInitialRow): void { }); } - /** - * @return iterable - */ + /** @return iterable */ public function counters(): iterable { yield 'initiated' => $this->initiatedCount; diff --git a/src/MiddlewareRunner.php b/src/MiddlewareRunner.php index 8ab6e6b..272f497 100644 --- a/src/MiddlewareRunner.php +++ b/src/MiddlewareRunner.php @@ -11,9 +11,7 @@ use const WyriHaximus\Constants\Numeric\ZERO; -/** - * @internal - */ +/** @internal */ final class MiddlewareRunner { diff --git a/src/Query/Limit.php b/src/Query/Limit.php index bae74da..1190ce9 100644 --- a/src/Query/Limit.php +++ b/src/Query/Limit.php @@ -6,11 +6,8 @@ final class Limit implements SectionInterface { - private int $limit; - - public function __construct(int $limit) + public function __construct(private int $limit) { - $this->limit = $limit; } public function limit(): int diff --git a/src/Query/Order.php b/src/Query/Order.php index 726bb4a..c5e3727 100644 --- a/src/Query/Order.php +++ b/src/Query/Order.php @@ -14,9 +14,7 @@ public function __construct(OrderInterface ...$orders) $this->orders = $orders; } - /** - * @return iterable - */ + /** @return iterable */ public function orders(): iterable { yield from $this->orders; diff --git a/src/Query/Order/Asc.php b/src/Query/Order/Asc.php index d3ee08b..962c02a 100644 --- a/src/Query/Order/Asc.php +++ b/src/Query/Order/Asc.php @@ -8,11 +8,8 @@ final class Asc implements OrderInterface { - private string $field; - - public function __construct(string $field) + public function __construct(private string $field) { - $this->field = $field; } public function field(): string diff --git a/src/Query/Order/Desc.php b/src/Query/Order/Desc.php index dfc7b56..b6c3d0f 100644 --- a/src/Query/Order/Desc.php +++ b/src/Query/Order/Desc.php @@ -8,11 +8,8 @@ final class Desc implements OrderInterface { - private string $field; - - public function __construct(string $field) + public function __construct(private string $field) { - $this->field = $field; } public function field(): string diff --git a/src/Query/Where.php b/src/Query/Where.php index 1ff65d4..8e8fcb7 100644 --- a/src/Query/Where.php +++ b/src/Query/Where.php @@ -14,9 +14,7 @@ public function __construct(WhereInterface ...$wheres) $this->wheres = $wheres; } - /** - * @return iterable - */ + /** @return iterable */ public function wheres(): iterable { yield from $this->wheres; diff --git a/src/Query/Where/Expression.php b/src/Query/Where/Expression.php index c64d6a7..ec9235f 100644 --- a/src/Query/Where/Expression.php +++ b/src/Query/Where/Expression.php @@ -11,19 +11,9 @@ final class Expression implements WhereInterface { - private ExpressionInterface $expression; - private string $criteria; - /** @var mixed[] */ - private array $criteriaArguments = []; - - /** - * @param mixed[] $criteriaArguments - */ - public function __construct(ExpressionInterface $expression, string $criteria, array $criteriaArguments) + /** @param mixed[] $criteriaArguments */ + public function __construct(private ExpressionInterface $expression, private string $criteria, private array $criteriaArguments = []) { - $this->expression = $expression; - $this->criteria = $criteria; - $this->criteriaArguments = $criteriaArguments; } public function expression(): ExpressionInterface diff --git a/src/Query/Where/Field.php b/src/Query/Where/Field.php index 099170b..a0c62ea 100644 --- a/src/Query/Where/Field.php +++ b/src/Query/Where/Field.php @@ -10,19 +10,9 @@ final class Field implements WhereInterface { - private string $field; - private string $criteria; - /** @var mixed[] */ - private array $criteriaArguments = []; - - /** - * @param mixed[] $criteriaArguments - */ - public function __construct(string $field, string $criteria, array $criteriaArguments) + /** @param mixed[] $criteriaArguments */ + public function __construct(private string $field, private string $criteria, private array $criteriaArguments = []) { - $this->field = $field; - $this->criteria = $criteria; - $this->criteriaArguments = $criteriaArguments; } public function field(): string diff --git a/src/Repository.php b/src/Repository.php index 5d4d6c6..894c864 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -35,7 +35,7 @@ use function Latitude\QueryBuilder\func; use function Latitude\QueryBuilder\on; use function Safe\date; -use function Safe\substr; +use function substr; use function spl_object_hash; use function strpos; @@ -43,18 +43,16 @@ use const WyriHaximus\Constants\Numeric\ONE; use const WyriHaximus\Constants\Numeric\ZERO; +/** + * @template T + * @template-implements RepositoryInterface + */ final class Repository implements RepositoryInterface { private const DATE_TIME_TIMEZONE_FORMAT = 'Y-m-d H:i:s e'; - private const SINGLE = ONE; + private const SINGLE = 1; private const STREAM_PER_PAGE = 100; - private InspectedEntityInterface $entity; - - private ClientInterface $client; - - private QueryFactory $queryFactory; - private Hydrator $hydrator; /** @var ExpressionInterface[] */ @@ -63,31 +61,37 @@ final class Repository implements RepositoryInterface /** @var string[] */ private array $tableAliases = []; - public function __construct(InspectedEntityInterface $entity, ClientInterface $client, QueryFactory $queryFactory) + public function __construct( + private InspectedEntityInterface $entity, + private ClientInterface $client, + private QueryFactory $queryFactory, + private Connection $connection, + ) { - $this->entity = $entity; - $this->client = $client; - $this->queryFactory = $queryFactory; - $this->hydrator = new Hydrator(); + $this->hydrator = new Hydrator(); } - /** @phpstan-ignore-next-line */ - public function count(?Where $where = null): PromiseInterface + /** + * @return PromiseInterface + */ + public function count(Where|null $where = null): PromiseInterface { $query = $this->queryFactory->select(alias(func('COUNT', '*'), 'count'))->from(alias($this->entity->table(), 't0')); if ($where instanceof Where) { $query = $this->applyWhereToQuery($where, $query); } - return $this->client->query( - $query->asExpression() + return $this->connection->query( + $query->asExpression(), )->take(self::SINGLE)->toPromise()->then(static function (array $row): int { return (int) $row['count']; }); } - /** @phpstan-ignore-next-line */ - public function page(int $page, ?Where $where = null, ?Order $order = null, int $perPage = RepositoryInterface::DEFAULT_PER_PAGE): Observable + /** + * @return Observable + */ + public function page(int $page, Where|null $where = null, Order|null $order = null, int $perPage = RepositoryInterface::DEFAULT_PER_PAGE): Observable { $query = $this->buildSelectQuery($where ?? new Where(), $order ?? new Order()); $query = $query->limit($perPage)->offset(--$page * $perPage); @@ -96,9 +100,7 @@ public function page(int $page, ?Where $where = null, ?Order $order = null, int } /** - * @param array $sections - * - * @phpstan-ignore-next-line + * @return Observable */ public function fetch(SectionInterface ...$sections): Observable { @@ -115,9 +117,7 @@ public function fetch(SectionInterface ...$sections): Observable } /** - * @param array $sections - * - * @phpstan-ignore-next-line + * @return Observable */ public function stream(SectionInterface ...$sections): Observable { @@ -129,9 +129,7 @@ public function stream(SectionInterface ...$sections): Observable $hasRows = false; $this->fetchAndHydrate($q->limit(self::STREAM_PER_PAGE)->offset($offset))->subscribe( - /** - * @psalm-suppress MissingClosureParamType - */ + /** @psalm-suppress MissingClosureParamType */ static function ($value) use (&$hasRows, $stream): void { if ($stream->isDisposed()) { return; @@ -160,6 +158,8 @@ static function () use (&$hasRows, &$page, $stream, $offset): void { /** * @param array $fields + * + * @return PromiseInterface */ public function create(array $fields): PromiseInterface { @@ -170,8 +170,8 @@ public function create(array $fields): PromiseInterface $fields = $this->prepareFields($fields); - return $this->client->query( - $this->queryFactory->insert($this->entity->table(), $fields)->asExpression() + return $this->connection->query( + $this->queryFactory->insert($this->entity->table(), $fields)->asExpression(), )->toPromise()->then(function () use ($id): PromiseInterface { return $this->fetch(new Where( new Where\Field( @@ -183,27 +183,33 @@ public function create(array $fields): PromiseInterface }); } + /** + * @return PromiseInterface + */ public function update(EntityInterface $entity): PromiseInterface { $fields = $this->hydrator->extract($this->entity, $entity); $fields['modified'] = new DateTimeImmutable(); $fields = $this->prepareFields($fields); - return $this->client->query( + return $this->connection->query( $this->queryFactory->update($this->entity->table(), $fields)-> - where(field('id')->eq($entity->id()))->asExpression() + where(field('id')->eq($entity->id))->asExpression(), )->toPromise()->then(function () use ($entity): PromiseInterface { return $this->fetch(new Where( - new Where\Field('id', 'eq', [$entity->id()]), + new Where\Field('id', 'eq', [$entity->id]), ), new Limit(ONE))->toPromise(); }); } + /** + * @return PromiseInterface + */ public function delete(EntityInterface $entity): PromiseInterface { - return $this->client->query( + return $this->connection->query( $this->queryFactory->delete($this->entity->table())-> - where(field('id')->eq($entity->id()))->asExpression() + where(field('id')->eq($entity->id))->asExpression(), )->toPromise(); } @@ -336,9 +342,9 @@ private function buildJoins(SelectQuery $query, InspectedEntityInterface $entity $query = $query->innerJoin( alias( $join->entity()->table(), - $this->tableAliases[$tableKey] + $this->tableAliases[$tableKey], ), - $clauses + $clauses, ); } @@ -354,10 +360,13 @@ private function buildJoins(SelectQuery $query, InspectedEntityInterface $entity return $query; } + /** + * @return Observable + */ private function fetchAndHydrate(QueryInterface $query): Observable { - return $this->client->query( - $query->asExpression() + return $this->connection->query( + $query->asExpression(), )->map(function (array $row): array { return $this->inflate($row); })->map(function (array $row): array { @@ -368,9 +377,9 @@ private function fetchAndHydrate(QueryInterface $query): Observable } /** - * @param mixed[] $row + * @param array $row * - * @return mixed[] + * @return array> */ private function inflate(array $row): array { @@ -385,9 +394,9 @@ private function inflate(array $row): array } /** - * @param mixed[] $row + * @param array> $row * - * @return mixed[] + * @return array */ private function buildTree(array $row, InspectedEntityInterface $entity, string $tableKeySuffix = 'root'): array { @@ -402,10 +411,6 @@ private function buildTree(array $row, InspectedEntityInterface $entity, string } if ($join->type() === 'inner' && ($join->lazy() === JoinInterface::IS_LAZY || $entity->class() === $join->entity()->class())) { - /** - * @phpstan-ignore-next-line - * @psalm-suppress DeprecatedClass - */ $tree[$join->property()] = new LazyPromise(function () use ($row, $join, $tableKey): PromiseInterface { return new Promise(function (callable $resolve, callable $reject) use ($row, $join, $tableKey): void { foreach ($join->clause() as $clause) { @@ -436,7 +441,7 @@ private function buildTree(array $row, InspectedEntityInterface $entity, string 'eq', [ $row[$this->tableAliases[$tableKey]][$clause->localKey()], - ] + ], ); } else { $where[] = new Where\Expression( @@ -444,7 +449,7 @@ private function buildTree(array $row, InspectedEntityInterface $entity, string 'eq', [ $row[$this->tableAliases[$tableKey]][$clause->localKey()], - ] + ], ); } } @@ -471,13 +476,13 @@ function () use ($row, $join, $tableKey): Observable { 'eq', [ $row[$this->tableAliases[$tableKey]][$clause->localKey()], - ] + ], ); } return $this->client->repository($join->entity()->class())->fetch(new Where(...$where)); }, - new ImmediateScheduler() + new ImmediateScheduler(), ); } @@ -495,9 +500,9 @@ private function translateFieldName(string $name): string } /** - * @param mixed[] $fields + * @param array $fields * - * @return mixed[] + * @return array */ private function prepareFields(array $fields): array { @@ -505,7 +510,7 @@ private function prepareFields(array $fields): array if ($value instanceof DateTimeInterface) { $fields[$key] = $value = date( self::DATE_TIME_TIMEZONE_FORMAT, - (int) $value->format('U') + (int) $value->format('U'), ); } diff --git a/src/RepositoryInterface.php b/src/RepositoryInterface.php index 388852e..d39d72a 100644 --- a/src/RepositoryInterface.php +++ b/src/RepositoryInterface.php @@ -10,26 +10,49 @@ use WyriHaximus\React\SimpleORM\Query\SectionInterface; use WyriHaximus\React\SimpleORM\Query\Where; +/** + * @template T + */ interface RepositoryInterface { public const DEFAULT_PER_PAGE = 50; - /** @phpstan-ignore-next-line */ - public function count(?Where $where = null): PromiseInterface; + /** + * @return PromiseInterface + * @phpstan-ignore-next-line + * + */ + public function count(Where $where = null): PromiseInterface; - /** @phpstan-ignore-next-line */ - public function page(int $page, ?Where $where = null, ?Order $order = null, int $perPage = self::DEFAULT_PER_PAGE): Observable; + /** + * @return Observable + * @phpstan-ignore-next-line + */ + public function page(int $page, Where $where = null, Order $order = null, int $perPage = self::DEFAULT_PER_PAGE): Observable; + /** + * @return Observable + */ public function fetch(SectionInterface ...$sections): Observable; + /** + * @return Observable + */ public function stream(SectionInterface ...$sections): Observable; /** * @param array $fields + * @return PromiseInterface */ public function create(array $fields): PromiseInterface; + /** + * @return PromiseInterface + */ public function update(EntityInterface $entity): PromiseInterface; + /** + * @return PromiseInterface + */ public function delete(EntityInterface $entity): PromiseInterface; } diff --git a/src/Tools/WithFieldsTrait.php b/src/Tools/WithFieldsTrait.php index a9e6c61..4468aa9 100644 --- a/src/Tools/WithFieldsTrait.php +++ b/src/Tools/WithFieldsTrait.php @@ -8,9 +8,7 @@ trait WithFieldsTrait { - /** - * @param array $fields - */ + /** @param array $fields */ public function withFields(array $fields): self { $clone = clone $this; diff --git a/tests/ClientTest.php b/tests/ClientTest.php index e03cfb6..7086a75 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -10,14 +10,15 @@ use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; use ReflectionClass; +use Rx\Observable; use WyriHaximus\AsyncTestUtilities\AsyncTestCase; use WyriHaximus\React\SimpleORM\Adapter\Postgres; use WyriHaximus\React\SimpleORM\Annotation\Table; use WyriHaximus\React\SimpleORM\Client; use WyriHaximus\React\Tests\SimpleORM\Stub\UserStub; -use function ApiClients\Tools\Rx\observableFromArray; use function Latitude\QueryBuilder\field; +use function React\Async\await; final class ClientTest extends AsyncTestCase { @@ -40,11 +41,11 @@ public function testGetRepository(): void { $this->annotationReader->getClassAnnotation( Argument::type(ReflectionClass::class), - Table::class + Table::class, )->shouldBeCalled()->willReturn(new Table(['users'])); $this->annotationReader->getClassAnnotations( - Argument::type(ReflectionClass::class) + Argument::type(ReflectionClass::class), )->shouldBeCalled()->willReturn([ new Table(['users']), ]); @@ -57,15 +58,15 @@ public function testFetch(): void $query = (new QueryFactory())->select()->from('table')->where(field('id')->eq(1))->asExpression(); $this->pgClient->executeStatement('SELECT * FROM "table" WHERE "id" = $1', [1])->shouldBeCalled()->willReturn( - observableFromArray([ + Observable::fromArray([ [ 'id' => 1, 'title' => 'Title', ], - ]) + ]), ); - $rows = $this->await($this->client->query($query)->toArray()->toPromise()); + $rows = await($this->client->query($query)->toArray()->toPromise()); self::assertCount(1, $rows); } diff --git a/tests/EntityInspectorTest.php b/tests/EntityInspectorTest.php index 3511a78..854d68e 100644 --- a/tests/EntityInspectorTest.php +++ b/tests/EntityInspectorTest.php @@ -26,9 +26,7 @@ protected function setUp(): void $this->entityInspector = new EntityInspector(new Configuration(''), new AnnotationReader()); } - /** - * @test - */ + /** @test */ public function inspect(): void { $inspectedEntity = $this->entityInspector->entity(UserStub::class); @@ -44,9 +42,7 @@ public function inspect(): void self::assertSame('string', $fields['name']->type()); } - /** - * @test - */ + /** @test */ public function inspectWithJoins(): void { $inspectedEntity = $this->entityInspector->entity(BlogPostStub::class); @@ -108,9 +104,7 @@ public function inspectWithJoins(): void self::assertSame('author', $joins['comments']->entity()->joins()['author']->property()); } - /** - * @test - */ + /** @test */ public function inspectWithoutTable(): void { self::expectException(RuntimeException::class); diff --git a/tests/FunctionalTest.php b/tests/FunctionalTest.php index 0b5c2ca..dffaeac 100644 --- a/tests/FunctionalTest.php +++ b/tests/FunctionalTest.php @@ -26,14 +26,12 @@ use function assert; use function bin2hex; use function random_bytes; -use function Safe\sleep; +use function React\Async\await; +use function sleep; use function time; -use function WyriHaximus\iteratorOrArrayToArray; final class FunctionalTest extends AsyncTestCase { - private const AWAIT_TIMEOUT = 66.6; - private ClientInterface $client; private QueryCountMiddleware $counter; @@ -58,25 +56,22 @@ protected function setUp(): void 'password' => 'postgres', 'database' => 'postgres', ], - Loop::get() - ) + Loop::get(), + ), ), new Configuration(''), - $this->counter + $this->counter, ); } - /** - * @test - */ + /** @test */ public function usersCount(): void { self::assertSame( 3, - $this->await( + await( $this->client->repository(UserStub::class)->count(), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -85,20 +80,17 @@ public function usersCount(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function usersCountResultSet(): void { self::assertCount( 3, - $this->await( + await( $this->client->repository(UserStub::class)->fetch()->toArray()->toPromise(), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -107,20 +99,17 @@ public function usersCountResultSet(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function blogPostsCount(): void { self::assertSame( 2, - $this->await( + await( $this->client->repository(BlogPostStub::class)->count(), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -129,20 +118,17 @@ public function blogPostsCount(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function blogPostsCountResultSet(): void { self::assertCount( 2, - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->toArray()->toPromise(), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -151,22 +137,19 @@ public function blogPostsCountResultSet(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function firstBlogPostCommentCount(): void { self::assertCount( 2, - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->take(1)->toPromise()->then(static function (BlogPostStub $blogPost): PromiseInterface { return $blogPost->getComments()->toArray()->toPromise(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -175,22 +158,19 @@ public function firstBlogPostCommentCount(): void 'errored' => 0, 'slow' => 0, 'completed' => 2, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function firstBlogPostAuthorId(): void { self::assertSame( 'fb175cbc-04cc-41c7-8e35-6b817ac016ca', - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->take(1)->toPromise()->then(static function (BlogPostStub $blogPost): string { return $blogPost->getAuthor()->id(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -199,22 +179,19 @@ public function firstBlogPostAuthorId(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function firstBlogPostAuthorIdUsingLimit(): void { self::assertSame( 'fb175cbc-04cc-41c7-8e35-6b817ac016ca', - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch(new Limit(1))->toPromise()->then(static function (BlogPostStub $blogPost): string { return $blogPost->getAuthor()->id(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -223,12 +200,10 @@ public function firstBlogPostAuthorIdUsingLimit(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function firstBlogPostCommentAuthorIds(): void { self::assertSame( @@ -241,14 +216,13 @@ public function firstBlogPostCommentAuthorIds(): void static function (CommentStub $comment): string { return $comment->getAuthor()->id(); }, - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->take(1)->toPromise()->then(static function (BlogPostStub $blogPost): PromiseInterface { return $blogPost->getComments()->toArray()->toPromise(); }), - self::AWAIT_TIMEOUT - ) - ) - ) + ), + ), + ), ); self::assertSame([ @@ -257,22 +231,19 @@ static function (CommentStub $comment): string { 'errored' => 0, 'slow' => 0, 'completed' => 2, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function firstBlogPostNextBlogPostResolvesToBlogPost(): void { self::assertInstanceOf( BlogPostStub::class, - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->take(1)->toPromise()->then(static function (BlogPostStub $blogPost): PromiseInterface { return $blogPost->getNextBlogPost(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -281,21 +252,18 @@ public function firstBlogPostNextBlogPostResolvesToBlogPost(): void 'errored' => 0, 'slow' => 0, 'completed' => 2, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function firstBlogPostPreviousBlogPostResolvesToNull(): void { self::assertNull( - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->take(1)->toPromise()->then(static function (BlogPostStub $blogPost): PromiseInterface { return $blogPost->getPreviousBlogPost(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -304,24 +272,21 @@ public function firstBlogPostPreviousBlogPostResolvesToNull(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function secondBlogPostCommentCount(): void { self::assertCount( 1, - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->filter(static function (BlogPostStub $blogPost): bool { return $blogPost->id() === '090fa83b-5c5a-4042-9f05-58d9ab649a1a'; })->toPromise()->then(static function (BlogPostStub $blogPost): PromiseInterface { return $blogPost->getComments()->toArray()->toPromise(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -330,24 +295,21 @@ public function secondBlogPostCommentCount(): void 'errored' => 0, 'slow' => 0, 'completed' => 2, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function secondBlogPostAuthorId(): void { self::assertSame( '15f25357-4b3d-4d4d-b6a5-2ceb93864b77', - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->filter(static function (BlogPostStub $blogPost): bool { return $blogPost->id() === '090fa83b-5c5a-4042-9f05-58d9ab649a1a'; })->toPromise()->then(static function (BlogPostStub $blogPost): string { return $blogPost->getAuthor()->id(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -356,12 +318,10 @@ public function secondBlogPostAuthorId(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function secondBlogPostCommentAuthorIds(): void { self::assertSame( @@ -371,16 +331,15 @@ public function secondBlogPostCommentAuthorIds(): void static function (CommentStub $comment): string { return $comment->getAuthor()->id(); }, - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->filter(static function (BlogPostStub $blogPost): bool { return $blogPost->id() === '090fa83b-5c5a-4042-9f05-58d9ab649a1a'; })->toPromise()->then(static function (BlogPostStub $blogPost): PromiseInterface { return $blogPost->getComments()->toArray()->toPromise(); }), - self::AWAIT_TIMEOUT - ) - ) - ) + ), + ), + ), ); self::assertSame([ @@ -389,17 +348,15 @@ static function (CommentStub $comment): string { 'errored' => 0, 'slow' => 0, 'completed' => 2, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function secondBlogPostPreviousBlogPostAuthorId(): void { self::assertSame( 'fb175cbc-04cc-41c7-8e35-6b817ac016ca', - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->filter(static function (BlogPostStub $blogPost): bool { return $blogPost->id() === '090fa83b-5c5a-4042-9f05-58d9ab649a1a'; })->toPromise()->then(static function (BlogPostStub $blogPost): PromiseInterface { @@ -407,8 +364,7 @@ public function secondBlogPostPreviousBlogPostAuthorId(): void })->then(static function (BlogPostStub $blogPost): string { return $blogPost->getAuthor()->id(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -417,23 +373,20 @@ public function secondBlogPostPreviousBlogPostAuthorId(): void 'errored' => 0, 'slow' => 0, 'completed' => 2, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function secondBlogPostNextBlogPostResolvesToNull(): void { self::assertNull( - $this->await( + await( $this->client->repository(BlogPostStub::class)->fetch()->filter(static function (BlogPostStub $blogPost): bool { return $blogPost->id() === '090fa83b-5c5a-4042-9f05-58d9ab649a1a'; })->toPromise()->then(static function (BlogPostStub $blogPost): PromiseInterface { return $blogPost->getNextBlogPost(); }), - self::AWAIT_TIMEOUT - ) + ), ); self::assertSame([ @@ -442,21 +395,18 @@ public function secondBlogPostNextBlogPostResolvesToNull(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function createUser(): void { $name = 'Commander Fuzzy paws'; $fields = ['name' => $name]; - $user = $this->await( + $user = await( $this->client->repository(UserStub::class)->create($fields), - self::AWAIT_TIMEOUT ); self::assertSame($name, $user->getName()); @@ -466,15 +416,13 @@ public function createUser(): void 'errored' => 0, 'slow' => 0, 'completed' => 2, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function increaseViews(): void { - sleep(3); + sleep(3); /** @phpstan-ignore-line We're using blocking sleep here on purpose */ self::waitUntilTheNextSecond(); $repository = $this->client->repository(BlogPostStub::class); @@ -483,7 +431,7 @@ public function increaseViews(): void $timestamp = null; $randomContents = bin2hex(random_bytes(13)); - $updatedBlogPost = $this->await( + $updatedBlogPost = await( $repository->fetch()->takeLast(1)->toPromise()->then(static function (BlogPostStub $blogPost) use (&$originalBlogPost, &$timestamp, $randomContents): BlogPostStub { self::waitUntilTheNextSecond(); @@ -494,7 +442,6 @@ public function increaseViews(): void })->then(static function (BlogPostStub $blogPost) use ($repository): PromiseInterface { return $repository->update($blogPost); }), - self::AWAIT_TIMEOUT ); assert($originalBlogPost instanceof BlogPostStub); @@ -511,25 +458,23 @@ public function increaseViews(): void 'errored' => 0, 'slow' => 0, 'completed' => 3, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); self::assertNotSame($originalBlogPost->getContents(), $updatedBlogPost->getContents()); self::assertSame($updatedBlogPost->getContents(), $randomContents); } - /** - * @test - */ + /** @test */ public function userSelf(): void { $repository = $this->client->repository(UserStub::class); $userId = null; - $self = $this->await($repository->fetch()->take(1)->toPromise()->then(static function (UserStub $user) use (&$userId): PromiseInterface { + $self = await($repository->fetch()->take(1)->toPromise()->then(static function (UserStub $user) use (&$userId): PromiseInterface { $userId = $user->id(); return $user->getZelf(); - }), self::AWAIT_TIMEOUT); + })); self::assertNotNull($userId); self::assertNotNull($self); @@ -540,39 +485,33 @@ public function userSelf(): void 'errored' => 0, 'slow' => 0, 'completed' => 2, - ], iteratorOrArrayToArray($this->counter->counters())); + ], [...$this->counter->counters()]); } - /** - * @test - */ + /** @test */ public function countWithoutConstraints(): void { $repository = $this->client->repository(BlogPostStub::class); - $count = $this->await($repository->count(), self::AWAIT_TIMEOUT); + $count = await($repository->count()); self::assertSame(2, $count); } - /** - * @test - */ + /** @test */ public function countWithConstraints(): void { $repository = $this->client->repository(BlogPostStub::class); - $count = $this->await($repository->count(new Where(new Where\Field('author_id', 'eq', ['fb175cbc-04cc-41c7-8e35-6b817ac016ca']))), self::AWAIT_TIMEOUT); + $count = await($repository->count(new Where(new Where\Field('author_id', 'eq', ['fb175cbc-04cc-41c7-8e35-6b817ac016ca'])))); self::assertSame(1, $count); } - /** - * @test - */ + /** @test */ public function streamLogs(): void { $repository = $this->client->repository(LogStub::class); - $rows = $this->await($repository->stream()->toArray()->toPromise(), self::AWAIT_TIMEOUT); + $rows = await($repository->stream()->toArray()->toPromise()); self::assertCount(256, $rows); } } diff --git a/tests/HydratorTest.php b/tests/HydratorTest.php index 7fd8480..3936b83 100644 --- a/tests/HydratorTest.php +++ b/tests/HydratorTest.php @@ -5,6 +5,7 @@ namespace WyriHaximus\React\Tests\SimpleORM; use Doctrine\Common\Annotations\AnnotationReader; +use Rx\Observable; use WyriHaximus\React\SimpleORM\Configuration; use WyriHaximus\React\SimpleORM\EntityInspector; use WyriHaximus\React\SimpleORM\Hydrator; @@ -12,16 +13,13 @@ use WyriHaximus\React\Tests\SimpleORM\Stub\UserStub; use WyriHaximus\TestUtilities\TestCase; -use function ApiClients\Tools\Rx\observableFromArray; use function assert; use function bin2hex; use function random_bytes; use function React\Promise\resolve; use function Safe\date; -/** - * @internal - */ +/** @internal */ final class HydratorTest extends TestCase { public function testHydrate(): void @@ -35,7 +33,7 @@ public function testHydrate(): void 'id' => $id, 'name' => $title, 'zelf' => resolve(true), - ] + ], ); assert($entity instanceof UserStub); @@ -56,7 +54,7 @@ public function testHydrateIgnoringNonExistingFields(): void 'name' => $title, 'zelf' => resolve(true), 'alsodoesnotexist' => resolve(true), - ] + ], ); assert($entity instanceof UserStub); @@ -97,9 +95,9 @@ public function testHydrateWithJoins(): void 'name' => $publisherName, 'zelf' => resolve(true), ], - 'comments' => observableFromArray([]), + 'comments' => Observable::fromArray([]), 'alsodoesnotexist' => resolve(true), - ] + ], ); assert($entity instanceof BlogPostStub); @@ -144,8 +142,8 @@ public function testHydrateWithJoinsIgnoringNonExistingFields(): void 'name' => $publisherName, 'zelf' => resolve(true), ], - 'comments' => observableFromArray([]), - ] + 'comments' => Observable::fromArray([]), + ], ); assert($entity instanceof BlogPostStub); diff --git a/tests/Middleware/QueryCountMiddlewareTest.php b/tests/Middleware/QueryCountMiddlewareTest.php index b74bdb9..72fc9f1 100644 --- a/tests/Middleware/QueryCountMiddlewareTest.php +++ b/tests/Middleware/QueryCountMiddlewareTest.php @@ -8,18 +8,13 @@ use Latitude\QueryBuilder\QueryFactory; use React\Promise\Deferred; use React\Promise\PromiseInterface; +use Rx\Observable; use Rx\Subject\Subject; use WyriHaximus\AsyncTestUtilities\AsyncTestCase; use WyriHaximus\React\SimpleORM\Middleware\QueryCountMiddleware; -use function ApiClients\Tools\Rx\observableFromArray; -use function ApiClients\Tools\Rx\unwrapObservableFromPromise; -use function Safe\sleep; -use function WyriHaximus\iteratorOrArrayToArray; +use function sleep; -/** - * @internal - */ final class QueryCountMiddlewareTest extends AsyncTestCase { public function testCountingSuccess(): void @@ -32,13 +27,13 @@ public function testCountingSuccess(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); $deferred = new Deferred(); - unwrapObservableFromPromise($middleware->query((new QueryFactory())->select()->asExpression(), static function () use ($deferred): PromiseInterface { + Observable::fromPromise($middleware->query((new QueryFactory())->select()->asExpression(), static function () use ($deferred): PromiseInterface { return $deferred->promise(); - }))->subscribe(static function (): void { + }))->mergeAll()->subscribe(static function (): void { }, static function (): void { }); @@ -48,9 +43,9 @@ public function testCountingSuccess(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); - $deferred->resolve(observableFromArray([[]])); + $deferred->resolve(Observable::fromArray([[]])); self::assertSame([ 'initiated' => 1, @@ -58,7 +53,7 @@ public function testCountingSuccess(): void 'errored' => 0, 'slow' => 0, 'completed' => 1, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); $middleware->resetCounters(); @@ -68,7 +63,7 @@ public function testCountingSuccess(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); } public function testCountingError(): void @@ -81,13 +76,13 @@ public function testCountingError(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); $deferred = new Deferred(); - unwrapObservableFromPromise($middleware->query((new QueryFactory())->select()->asExpression(), static function () use ($deferred): PromiseInterface { + Observable::fromPromise($middleware->query((new QueryFactory())->select()->asExpression(), static function () use ($deferred): PromiseInterface { return $deferred->promise(); - }))->subscribe(static function (): void { + }))->mergeAll()->subscribe(static function (): void { }, static function (): void { }); @@ -97,7 +92,7 @@ public function testCountingError(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); $subject = new Subject(); $deferred->resolve($subject); @@ -109,7 +104,7 @@ public function testCountingError(): void 'errored' => 1, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); $middleware->resetCounters(); @@ -119,7 +114,7 @@ public function testCountingError(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); } public function testCountingErrorSlow(): void @@ -132,13 +127,13 @@ public function testCountingErrorSlow(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); $deferred = new Deferred(); - unwrapObservableFromPromise($middleware->query((new QueryFactory())->select()->asExpression(), static function () use ($deferred): PromiseInterface { + Observable::fromPromise($middleware->query((new QueryFactory())->select()->asExpression(), static function () use ($deferred): PromiseInterface { return $deferred->promise(); - }))->subscribe(static function (): void { + }))->mergeAll()->subscribe(static function (): void { }, static function (): void { }); @@ -148,9 +143,9 @@ public function testCountingErrorSlow(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); - sleep(2); + sleep(2); /** @phpstan-ignore-line We're using blocking sleep here on purpose */ $subject = new Subject(); $deferred->resolve($subject); @@ -162,7 +157,7 @@ public function testCountingErrorSlow(): void 'errored' => 1, 'slow' => 1, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); $middleware->resetCounters(); @@ -172,6 +167,6 @@ public function testCountingErrorSlow(): void 'errored' => 0, 'slow' => 0, 'completed' => 0, - ], iteratorOrArrayToArray($middleware->counters())); + ], [...$middleware->counters()]); } } diff --git a/tests/RepositoryTest.php b/tests/RepositoryTest.php index 53a7a72..acbef3a 100644 --- a/tests/RepositoryTest.php +++ b/tests/RepositoryTest.php @@ -10,6 +10,7 @@ use Latitude\QueryBuilder\QueryFactory; use Prophecy\Argument; use Prophecy\Prophecy\ObjectProphecy; +use Rx\Observable; use WyriHaximus\AsyncTestUtilities\AsyncTestCase; use WyriHaximus\React\SimpleORM\ClientInterface; use WyriHaximus\React\SimpleORM\Configuration; @@ -21,13 +22,16 @@ use WyriHaximus\React\Tests\SimpleORM\Stub\CommentStub; use WyriHaximus\React\Tests\SimpleORM\Stub\UserStub; -use function ApiClients\Tools\Rx\observableFromArray; use function assert; +use function React\Async\await; use function Safe\date; use function strpos; final class RepositoryTest extends AsyncTestCase { + /** + * @var ObjectProphecy + */ private ObjectProphecy $client; protected function setUp(): void @@ -46,20 +50,19 @@ public function testCount(): void self::assertStringContainsString('COUNT(*) AS "count"', $query); return true; - }))->shouldBeCalled()->willReturn(observableFromArray([ + }))->shouldBeCalled()->willReturn(Observable::fromArray([ ['count' => '123'], ])); $client = $this->client->reveal(); - assert($client instanceof ClientInterface); $repository = new Repository( (new EntityInspector(new Configuration(''), new AnnotationReader()))->entity(UserStub::class), $client, - new QueryFactory() + new QueryFactory(), ); - self::assertSame(123, $this->await($repository->count())); + self::assertSame(123, await($repository->count())); } public function testCountWithContraints(): void @@ -72,20 +75,19 @@ public function testCountWithContraints(): void self::assertStringContainsString('WHERE "t0"."field" = ?', $query); return true; - }))->shouldBeCalled()->willReturn(observableFromArray([ + }))->shouldBeCalled()->willReturn(Observable::fromArray([ ['count' => '123'], ])); $client = $this->client->reveal(); - assert($client instanceof ClientInterface); $repository = new Repository( (new EntityInspector(new Configuration(''), new AnnotationReader()))->entity(UserStub::class), $client, - new QueryFactory() + new QueryFactory(), ); - self::assertSame(123, $this->await($repository->count( + self::assertSame(123, await($repository->count( new Where( new Where\Field('field', 'eq', ['values']), ), @@ -105,21 +107,20 @@ public function testCountWithJoins(): void self::assertStringNotContainsString('LEFT JOIN comments', $query); return true; - }))->shouldBeCalled()->willReturn(observableFromArray([ + }))->shouldBeCalled()->willReturn(Observable::fromArray([ ['count' => '123'], ['count' => '456'], ])); $client = $this->client->reveal(); - assert($client instanceof ClientInterface); $repository = new Repository( (new EntityInspector(new Configuration(''), new AnnotationReader()))->entity(BlogPostStub::class), $client, - new QueryFactory() + new QueryFactory(), ); - self::assertSame(123, $this->await($repository->count())); + self::assertSame(123, await($repository->count())); } public function testFetchWithJoins(): void @@ -147,7 +148,7 @@ public function testFetchWithJoins(): void self::assertStringNotContainsString('FROM "comments"', $query); return true; - }))->shouldBeCalled()->willReturn(observableFromArray([ + }))->shouldBeCalled()->willReturn(Observable::fromArray([ [ 't0___id' => '98ce9eaf-b38b-4a51-93ed-131ffac4051e', 't0___title' => 'blog_post_title', @@ -165,15 +166,14 @@ public function testFetchWithJoins(): void ])); $client = $this->client->reveal(); - assert($client instanceof ClientInterface); $repository = new Repository( (new EntityInspector(new Configuration(''), new AnnotationReader()))->entity(BlogPostStub::class), $client, - new QueryFactory() + new QueryFactory(), ); - $blogPost = $this->await($repository->fetch(new Where( + $blogPost = await($repository->fetch(new Where( new Where\Field('id', 'eq', ['98ce9eaf-b38b-4a51-93ed-131ffac4051e']), ), new Order( new Order\Desc('id'), @@ -192,10 +192,9 @@ public function testFetchWithJoins(): void public function testFetchWithJoinsLazyLoadComments(): void { $client = $this->client->reveal(); - assert($client instanceof ClientInterface); $this->client->repository(CommentStub::class)->shouldBeCalled()->willReturn( - new Repository((new EntityInspector(new Configuration(''), new AnnotationReader()))->entity(CommentStub::class), $client, new QueryFactory()) + new Repository((new EntityInspector(new Configuration(''), new AnnotationReader()))->entity(CommentStub::class), $client, new QueryFactory()), ); $this->client->query(Argument::that(static function (ExpressionInterface $expression): bool { @@ -224,7 +223,7 @@ public function testFetchWithJoinsLazyLoadComments(): void self::assertStringNotContainsString('FROM "comments"', $query); return true; - }))->shouldBeCalled()->willReturn(observableFromArray([ + }))->shouldBeCalled()->willReturn(Observable::fromArray([ [ 't0___id' => '99d00028-28d6-4194-b377-a0039b278c4d', 't0___author_id' => '3fbf8eec-8a3f-4b01-ba9a-355f6650644b', @@ -258,7 +257,7 @@ public function testFetchWithJoinsLazyLoadComments(): void self::assertStringContainsString('"t0"."blog_post_id" = ?', $query); return true; - }))->shouldBeCalled()->willReturn(observableFromArray([ + }))->shouldBeCalled()->willReturn(Observable::fromArray([ [ 't0___id' => '99d00028-28d6-4194-b377-a0039b278c4d', 't0___author_id' => 'd45e8a1b-b962-4c1b-a7e7-c867fa06ffa7', @@ -345,10 +344,10 @@ public function testFetchWithJoinsLazyLoadComments(): void $repository = new Repository( (new EntityInspector(new Configuration(''), new AnnotationReader()))->entity(BlogPostStub::class), $client, - new QueryFactory() + new QueryFactory(), ); - $blogPost = $this->await($repository->fetch(new Where( + $blogPost = await($repository->fetch(new Where( new Where\Field('id', 'eq', ['99d00028-28d6-4194-b377-a0039b278c4d']), ), new Order( new Order\Desc('id'), @@ -363,7 +362,7 @@ public function testFetchWithJoinsLazyLoadComments(): void self::assertSame('publisher_name', $blogPost->getPublisher()->getName()); /** @var CommentStub[] $comments */ - $comments = $this->await($blogPost->getComments()->toArray()->toPromise()); + $comments = await($blogPost->getComments()->toArray()->toPromise()); self::assertSame('99d00028-28d6-4194-b377-a0039b278c4d', $comments[0]->id()); self::assertSame('d45e8a1b-b962-4c1b-a7e7-c867fa06ffa7', $comments[0]->getAuthor()->id()); diff --git a/tests/Stub/BlogPostStub.php b/tests/Stub/BlogPostStub.php index 51febeb..1a313c3 100644 --- a/tests/Stub/BlogPostStub.php +++ b/tests/Stub/BlogPostStub.php @@ -83,10 +83,16 @@ final class BlogPostStub implements EntityInterface protected ?string $previous_blog_post_id = null; + /** + * @var PromiseInterface + */ protected PromiseInterface $previous_blog_post; protected ?string $next_blog_post_id = null; + /** + * @var PromiseInterface + */ protected PromiseInterface $next_blog_post; protected string $author_id; @@ -115,6 +121,9 @@ public function id(): string return $this->id; } + /** + * @return PromiseInterface + */ public function getPreviousBlogPost(): PromiseInterface { //phpcs:disable @@ -122,6 +131,9 @@ public function getPreviousBlogPost(): PromiseInterface //phpcs:enable } + /** + * @return PromiseInterface + */ public function getNextBlogPost(): PromiseInterface { //phpcs:disable diff --git a/tests/Stub/LogStub.php b/tests/Stub/LogStub.php index b411c34..6b5922b 100644 --- a/tests/Stub/LogStub.php +++ b/tests/Stub/LogStub.php @@ -8,9 +8,7 @@ use WyriHaximus\React\SimpleORM\EntityInterface; use WyriHaximus\React\SimpleORM\Tools\WithFieldsTrait; -/** - * @Table("logs") - */ +/** @Table("logs") */ final class LogStub implements EntityInterface { use WithFieldsTrait; diff --git a/tests/Stub/UserStub.php b/tests/Stub/UserStub.php index 70d63ef..aed458b 100644 --- a/tests/Stub/UserStub.php +++ b/tests/Stub/UserStub.php @@ -34,6 +34,9 @@ final class UserStub implements EntityInterface protected string $name; + /** + * @var PromiseInterface + */ protected PromiseInterface $zelf; public function id(): string @@ -46,6 +49,9 @@ public function getName(): string return $this->name; } + /** + * @return PromiseInterface + */ public function getZelf(): PromiseInterface { return $this->zelf; diff --git a/tests/types/basic.php b/tests/types/basic.php new file mode 100644 index 0000000..2152771 --- /dev/null +++ b/tests/types/basic.php @@ -0,0 +1,15 @@ +repository(\WyriHaximus\React\Tests\SimpleORM\Stub\NoSQLStub::class); + +assertType('WyriHaximus\React\SimpleORM\RepositoryInterface', $repository); +assertType('React\Promise\PromiseInterface', $repository->count()); +assertType('Rx\Observable', $repository->fetch()); +assertType('Rx\Observable', $repository->page(1)); +assertType('Rx\Observable', $repository->stream());