From 24500dc2fdd51096bc3439b2856474ac5cdea5af Mon Sep 17 00:00:00 2001 From: Cees-Jan Kiewiet Date: Tue, 19 Mar 2024 22:41:16 +0100 Subject: [PATCH] Transform to Attributes --- .github/workflows/ci.yml | 2 +- composer.json | 8 +- composer.lock | 24 ++--- src/Annotation/Clause.php | 85 --------------- src/Annotation/InnerJoin.php | 65 ----------- src/Annotation/JoinInterface.php | 22 ---- src/Annotation/LeftJoin.php | 65 ----------- src/Annotation/Table.php | 29 ----- src/Attribute/Clause.php | 18 ++++ src/Attribute/InnerJoin.php | 21 ++++ src/Attribute/JoinInterface.php | 18 ++++ src/Attribute/LeftJoin.php | 21 ++++ src/Attribute/Table.php | 16 +++ src/Client.php | 19 ++-- src/ClientInterface.php | 9 +- src/Connection.php | 14 +-- src/Entity/Join.php | 2 +- src/EntityInspector.php | 54 +++++----- src/EntityInterface.php | 4 +- src/Middleware/QueryCountMiddleware.php | 2 +- src/Repository.php | 75 ++++++------- src/RepositoryInterface.php | 28 ++--- tests/ClientTest.php | 2 +- tests/RepositoryTest.php | 4 +- tests/Stub/BlogPostStub.php | 136 +++++++++++------------- tests/Stub/CommentStub.php | 52 +++++---- tests/Stub/LogStub.php | 22 ++-- tests/Stub/NoSQLStub.php | 8 +- tests/Stub/UserStub.php | 45 ++++---- tests/types/basic.php | 9 +- 30 files changed, 322 insertions(+), 557 deletions(-) delete mode 100644 src/Annotation/Clause.php delete mode 100644 src/Annotation/InnerJoin.php delete mode 100644 src/Annotation/JoinInterface.php delete mode 100644 src/Annotation/LeftJoin.php delete mode 100644 src/Annotation/Table.php create mode 100644 src/Attribute/Clause.php create mode 100644 src/Attribute/InnerJoin.php create mode 100644 src/Attribute/JoinInterface.php create mode 100644 src/Attribute/LeftJoin.php create mode 100644 src/Attribute/Table.php diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 144bc23..3941a96 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,4 +16,4 @@ jobs: name: Continuous Integration uses: WyriHaximus/github-workflows/.github/workflows/package.yaml@main with: - services: "{\"postgres\":{\"image\":\"postgres:\$\{\{ matrix.postgres \}\}\",\"env\":{\"POSTGRES_PASSWORD\":\"postgres\"},\"options\":\"--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5\"}}" + services: "{\"postgres\":{\"image\":\"postgres:${{ matrix.postgres }}\",\"env\":{\"POSTGRES_PASSWORD\":\"postgres\"},\"options\":\"--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5\"}}" diff --git a/composer.json b/composer.json index 7ceddaf..a4815cc 100644 --- a/composer.json +++ b/composer.json @@ -11,17 +11,15 @@ ], "require": { "php": "^8.2", - "doctrine/annotations": "^1.13", "eventsauce/object-hydrator": "^1.4", "latitude/latitude": "^4.1", "ramsey/uuid": "^4.2.3", - "react/dns": "^1.9", "react/event-loop": "^1.3", "react/promise": "^3.1", "react/stream": "^1.1", - "reactivex/rxphp": "^2.0", - "roave/better-reflection": "^4.0 || ^5 || ^6", - "thecodingmachine/safe": "^1.3 || ^2", + "reactivex/rxphp": "^2.0.12", + "roave/better-reflection": "^6", + "thecodingmachine/safe": "^2", "voryx/pgasync": "^2.0", "wyrihaximus/constants": "^1.5", "wyrihaximus/doctrine-annotation-autoloader": "^1.0", diff --git a/composer.lock b/composer.lock index 06f2f91..5103505 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7d2df79e2899d0c518ca81858a4a6606", + "content-hash": "b0e4380c260781edb515dd2a75f903d0", "packages": [ { "name": "brick/math", @@ -2875,16 +2875,16 @@ }, { "name": "composer/pcre", - "version": "3.1.2", + "version": "3.1.3", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace" + "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace", - "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace", + "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", + "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", "shasum": "" }, "require": { @@ -2926,7 +2926,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.2" + "source": "https://github.com/composer/pcre/tree/3.1.3" }, "funding": [ { @@ -2942,7 +2942,7 @@ "type": "tidelift" } ], - "time": "2024-03-07T15:38:35+00:00" + "time": "2024-03-19T10:26:25+00:00" }, { "name": "composer/semver", @@ -6667,16 +6667,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.62", + "version": "1.10.63", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9" + "reference": "ad12836d9ca227301f5fb9960979574ed8628339" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd5c8a1660ed3540b211407c77abf4af193a6af9", - "reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ad12836d9ca227301f5fb9960979574ed8628339", + "reference": "ad12836d9ca227301f5fb9960979574ed8628339", "shasum": "" }, "require": { @@ -6725,7 +6725,7 @@ "type": "tidelift" } ], - "time": "2024-03-13T12:27:20+00:00" + "time": "2024-03-18T16:53:53+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", diff --git a/src/Annotation/Clause.php b/src/Annotation/Clause.php deleted file mode 100644 index 5da6af0..0000000 --- a/src/Annotation/Clause.php +++ /dev/null @@ -1,85 +0,0 @@ - $value) { - if (! property_exists($this, $name)) { - continue; - } - - $this->$name = $clause[$name]; /** @phpstan-ignore-line */ - } - } - - public function localKey(): string - { - //phpcs:disable - return $this->local_key; - //phpcs:enable - } - - public function foreignKey(): string - { - //phpcs:disable - return $this->foreign_key; - //phpcs:enable - } - - /** @phpstan-ignore-next-line */ - public function localCast(): string|null - { - //phpcs:disable - return $this->local_cast; - //phpcs:enable - } - - /** @phpstan-ignore-next-line */ - public function foreignCast(): string|null - { - //phpcs:disable - return $this->foreign_cast; - //phpcs:enable - } - - /** @phpstan-ignore-next-line */ - public function localFunction(): string|null - { - //phpcs:disable - return $this->local_function; - //phpcs:enable - } - - /** @phpstan-ignore-next-line */ - public function foreignFunction(): string|null - { - //phpcs:disable - return $this->foreign_function; - //phpcs:enable - } -} diff --git a/src/Annotation/InnerJoin.php b/src/Annotation/InnerJoin.php deleted file mode 100644 index 4594d53..0000000 --- a/src/Annotation/InnerJoin.php +++ /dev/null @@ -1,65 +0,0 @@ - $value) { - if (! property_exists($this, $name)) { - continue; - } - - $this->$name = $innerJoin[$name]; /** @phpstan-ignore-line */ - } - } - - public function entity(): string - { - return $this->entity; - } - - public function type(): string - { - return 'inner'; - } - - public function lazy(): bool - { - return $this->lazy; - } - - /** @return Clause[] */ - public function clause(): array - { - return $this->clause; - } - - public function property(): string - { - return $this->property; - } -} diff --git a/src/Annotation/JoinInterface.php b/src/Annotation/JoinInterface.php deleted file mode 100644 index de06bf6..0000000 --- a/src/Annotation/JoinInterface.php +++ /dev/null @@ -1,22 +0,0 @@ - $value) { - if (! property_exists($this, $name)) { - continue; - } - - $this->$name = $leftJoin[$name]; /** @phpstan-ignore-line */ - } - } - - public function entity(): string - { - return $this->entity; - } - - public function type(): string - { - return 'left'; - } - - public function lazy(): bool - { - return $this->lazy; - } - - /** @return Clause[] */ - public function clause(): array - { - return $this->clause; - } - - public function property(): string - { - return $this->property; - } -} diff --git a/src/Annotation/Table.php b/src/Annotation/Table.php deleted file mode 100644 index 0710b9b..0000000 --- a/src/Annotation/Table.php +++ /dev/null @@ -1,29 +0,0 @@ -table = current($table); /** @phpstan-ignore-line */ - } - - public function table(): string - { - return $this->table; - } -} diff --git a/src/Attribute/Clause.php b/src/Attribute/Clause.php new file mode 100644 index 0000000..ae7e86d --- /dev/null +++ b/src/Attribute/Clause.php @@ -0,0 +1,18 @@ + $clause */ + public function __construct( + public string $entity, + public array $clause, + public string $property, + public bool $lazy = self::IS_NOT_LAZY, + ) { + $this->type = 'inner'; + } +} diff --git a/src/Attribute/JoinInterface.php b/src/Attribute/JoinInterface.php new file mode 100644 index 0000000..984663f --- /dev/null +++ b/src/Attribute/JoinInterface.php @@ -0,0 +1,18 @@ + $clause + */ +interface JoinInterface +{ + public const IS_LAZY = true; + public const IS_NOT_LAZY = false; +} diff --git a/src/Attribute/LeftJoin.php b/src/Attribute/LeftJoin.php new file mode 100644 index 0000000..06b3d04 --- /dev/null +++ b/src/Attribute/LeftJoin.php @@ -0,0 +1,21 @@ + $clause */ + public function __construct( + public string $entity, + public array $clause, + public string $property, + public bool $lazy = self::IS_NOT_LAZY, + ) { + $this->type = 'left'; + } +} diff --git a/src/Attribute/Table.php b/src/Attribute/Table.php new file mode 100644 index 0000000..9794bcf --- /dev/null +++ b/src/Attribute/Table.php @@ -0,0 +1,16 @@ +entityInspector = new EntityInspector($configuration, $annotationReader); + $this->entityInspector = new EntityInspector($configuration); $this->queryFactory = new QueryFactory($adapter->engine()); $this->connection = new Connection($this->adapter, new MiddlewareRunner(...$middleware)); } /** - * @template T * @param class-string $entity + * * @return RepositoryInterface + * + * @template T */ public function repository(string $entity): RepositoryInterface { diff --git a/src/ClientInterface.php b/src/ClientInterface.php index 935d84a..7ea0a75 100644 --- a/src/ClientInterface.php +++ b/src/ClientInterface.php @@ -9,16 +9,15 @@ interface ClientInterface { - /** - * @template T * @param class-string $entity + * * @return RepositoryInterface + * + * @template T */ public function repository(string $entity): RepositoryInterface; - /** - * @deprecated This function will disappear at initial release - */ + /** @deprecated This function will disappear at initial release */ public function query(ExpressionInterface $query): Observable; } diff --git a/src/Connection.php b/src/Connection.php index f941217..a019397 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -4,27 +4,19 @@ namespace WyriHaximus\React\SimpleORM; -use Doctrine\Common\Annotations\AnnotationReader; -use Doctrine\Common\Annotations\Reader; use Latitude\QueryBuilder\ExpressionInterface; -use Latitude\QueryBuilder\QueryFactory; use React\Promise\PromiseInterface; use Rx\Observable; -use function array_key_exists; use function React\Promise\resolve; -/** - * @internal - */ +/** @internal */ final readonly class Connection { public function __construct( private AdapterInterface $adapter, - private MiddlewareRunner $middlewareRunner - ) - { - + private MiddlewareRunner $middlewareRunner, + ) { } public function query(ExpressionInterface $query): Observable diff --git a/src/Entity/Join.php b/src/Entity/Join.php index 7c1f987..40d3a67 100644 --- a/src/Entity/Join.php +++ b/src/Entity/Join.php @@ -4,7 +4,7 @@ namespace WyriHaximus\React\SimpleORM\Entity; -use WyriHaximus\React\SimpleORM\Annotation\Clause; +use WyriHaximus\React\SimpleORM\Attribute\Clause; use WyriHaximus\React\SimpleORM\InspectedEntityInterface; final class Join diff --git a/src/EntityInspector.php b/src/EntityInspector.php index f3afddf..cdb3e5a 100644 --- a/src/EntityInspector.php +++ b/src/EntityInspector.php @@ -4,17 +4,17 @@ namespace WyriHaximus\React\SimpleORM; -use Doctrine\Common\Annotations\Reader; use ReflectionClass; use Roave\BetterReflection\BetterReflection; use Roave\BetterReflection\Reflection\ReflectionProperty; use RuntimeException; -use WyriHaximus\React\SimpleORM\Annotation\JoinInterface; -use WyriHaximus\React\SimpleORM\Annotation\Table; +use WyriHaximus\React\SimpleORM\Attribute\JoinInterface; +use WyriHaximus\React\SimpleORM\Attribute\Table; use WyriHaximus\React\SimpleORM\Entity\Field; use WyriHaximus\React\SimpleORM\Entity\Join; use function array_key_exists; +use function count; use function current; use function method_exists; @@ -23,34 +23,27 @@ final class EntityInspector /** @var InspectedEntityInterface[] */ private array $entities = []; - public function __construct(private Configuration $configuration, private Reader $annotationReader) + public function __construct(private Configuration $configuration) { } public function entity(string $entity): InspectedEntityInterface { if (! array_key_exists($entity, $this->entities)) { - /** - * @phpstan-ignore-next-line - * @psalm-suppress ArgumentTypeCoercion - */ $class = new ReflectionClass($entity); - $tableAnnotation = $this->annotationReader->getClassAnnotation($class, Table::class); + $tableAttributes = $class->getAttributes(Table::class); - if ($tableAnnotation instanceof Table === false) { + if (count($tableAttributes) === 0) { throw new RuntimeException('Missing Table annotation on entity: ' . $entity); } - /** - * @phpstan-ignore-next-line - * @psalm-suppress ArgumentTypeCoercion - */ - $joins = [...$this->joins($class)]; - /** @psalm-suppress ArgumentTypeCoercion */ + $tableAttribute = current($tableAttributes)->newInstance(); + + $joins = [...$this->joins($class)]; $this->entities[$entity] = new InspectedEntity( $entity, - $this->configuration->tablePrefix() . $tableAnnotation->table(), - [...$this->fields($class, $joins)], /** @phpstan-ignore-line */ + $this->configuration->tablePrefix() . $tableAttribute->table, + [...$this->fields($class, $joins)], $joins, ); } @@ -105,20 +98,25 @@ private function fields(ReflectionClass $class, array $joins): iterable */ private function joins(ReflectionClass $class): iterable { - $annotations = $this->annotationReader->getClassAnnotations($class); - - foreach ($annotations as $annotation) { + foreach ($class->getAttributes() as $attribute) { + $annotation = $attribute->newInstance(); if ($annotation instanceof JoinInterface === false) { continue; } - yield $annotation->property() => new Join( - new LazyInspectedEntity($this, $annotation->entity()), - $annotation->type(), - $annotation->property(), - $annotation->lazy(), - ...$annotation->clause(), - ); + yield from $this->join($annotation); } } + + /** @return iterable */ + private function join(JoinInterface $join): iterable + { + yield $join->property => new Join( + new LazyInspectedEntity($this, $join->entity), + $join->type, + $join->property, + $join->lazy, + ...$join->clause, + ); + } } diff --git a/src/EntityInterface.php b/src/EntityInterface.php index 84cad30..f596d9c 100644 --- a/src/EntityInterface.php +++ b/src/EntityInterface.php @@ -4,9 +4,7 @@ namespace WyriHaximus\React\SimpleORM; -/** - * @property string $id - */ +/** @property string $id */ interface EntityInterface { } diff --git a/src/Middleware/QueryCountMiddleware.php b/src/Middleware/QueryCountMiddleware.php index 79c930c..435b81e 100644 --- a/src/Middleware/QueryCountMiddleware.php +++ b/src/Middleware/QueryCountMiddleware.php @@ -11,8 +11,8 @@ use Throwable; use WyriHaximus\React\SimpleORM\MiddlewareInterface; -use function Safe\hrtime; use function React\Promise\resolve; +use function Safe\hrtime; final class QueryCountMiddleware implements MiddlewareInterface { diff --git a/src/Repository.php b/src/Repository.php index 894c864..673c324 100644 --- a/src/Repository.php +++ b/src/Repository.php @@ -17,7 +17,7 @@ use Rx\Scheduler\ImmediateScheduler; use Rx\Subject\Subject; use Safe\DateTimeImmutable; -use WyriHaximus\React\SimpleORM\Annotation\JoinInterface; +use WyriHaximus\React\SimpleORM\Attribute\JoinInterface; use WyriHaximus\React\SimpleORM\Query\Limit; use WyriHaximus\React\SimpleORM\Query\Order; use WyriHaximus\React\SimpleORM\Query\SectionInterface; @@ -35,9 +35,9 @@ use function Latitude\QueryBuilder\func; use function Latitude\QueryBuilder\on; use function Safe\date; -use function substr; use function spl_object_hash; use function strpos; +use function substr; use const WyriHaximus\Constants\Boolean\TRUE_; use const WyriHaximus\Constants\Numeric\ONE; @@ -66,14 +66,11 @@ public function __construct( private ClientInterface $client, private QueryFactory $queryFactory, private Connection $connection, - ) - { + ) { $this->hydrator = new Hydrator(); } - /** - * @return 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')); @@ -88,9 +85,7 @@ public function count(Where|null $where = null): PromiseInterface }); } - /** - * @return 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()); @@ -99,9 +94,7 @@ public function page(int $page, Where|null $where = null, Order|null $order = nu return $this->fetchAndHydrate($query); } - /** - * @return Observable - */ + /** @return Observable */ public function fetch(SectionInterface ...$sections): Observable { $query = $this->buildSelectQuery(...$sections); @@ -116,9 +109,7 @@ public function fetch(SectionInterface ...$sections): Observable return $this->fetchAndHydrate($query); } - /** - * @return Observable - */ + /** @return Observable */ public function stream(SectionInterface ...$sections): Observable { $stream = new Subject(); @@ -183,9 +174,7 @@ public function create(array $fields): PromiseInterface }); } - /** - * @return PromiseInterface - */ + /** @return PromiseInterface */ public function update(EntityInterface $entity): PromiseInterface { $fields = $this->hydrator->extract($this->entity, $entity); @@ -202,9 +191,7 @@ public function update(EntityInterface $entity): PromiseInterface }); } - /** - * @return PromiseInterface - */ + /** @return PromiseInterface */ public function delete(EntityInterface $entity): PromiseInterface { return $this->connection->query( @@ -305,27 +292,27 @@ private function buildJoins(SelectQuery $query, InspectedEntityInterface $entity $clauses = null; foreach ($join->clause() as $clause) { - $onLeftSide = $this->tableAliases[$tableKey] . '.' . $clause->foreignKey(); - if ($clause->foreignFunction() !== null) { + $onLeftSide = $this->tableAliases[$tableKey] . '.' . $clause->foreignKey; + if ($clause->foreignFunction !== null) { /** @psalm-suppress PossiblyNullOperand */ - $onLeftSide = $clause->foreignFunction() . '(' . $onLeftSide . ')'; + $onLeftSide = $clause->foreignFunction . '(' . $onLeftSide . ')'; } - if ($clause->foreignCast() !== null) { + if ($clause->foreignCast !== null) { /** @psalm-suppress PossiblyNullOperand */ - $onLeftSide = 'CAST(' . $onLeftSide . ' AS ' . $clause->foreignCast() . ')'; + $onLeftSide = 'CAST(' . $onLeftSide . ' AS ' . $clause->foreignCast . ')'; } $onRightSide = - $this->tableAliases[spl_object_hash($entity) . '___' . $rootProperty] . '.' . $clause->localKey(); - if ($clause->localFunction() !== null) { + $this->tableAliases[spl_object_hash($entity) . '___' . $rootProperty] . '.' . $clause->localKey; + if ($clause->localFunction !== null) { /** @psalm-suppress PossiblyNullOperand */ - $onRightSide = $clause->localFunction() . '(' . $onRightSide . ')'; + $onRightSide = $clause->localFunction . '(' . $onRightSide . ')'; } - if ($clause->localCast() !== null) { + if ($clause->localCast !== null) { /** @psalm-suppress PossiblyNullOperand */ - $onRightSide = 'CAST(' . $onRightSide . ' AS ' . $clause->localCast() . ')'; + $onRightSide = 'CAST(' . $onRightSide . ' AS ' . $clause->localCast . ')'; } if ($clauses === null) { @@ -360,9 +347,7 @@ private function buildJoins(SelectQuery $query, InspectedEntityInterface $entity return $query; } - /** - * @return Observable - */ + /** @return Observable */ private function fetchAndHydrate(QueryInterface $query): Observable { return $this->connection->query( @@ -414,7 +399,7 @@ private function buildTree(array $row, InspectedEntityInterface $entity, string $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) { - if ($row[$this->tableAliases[$tableKey]][$clause->localKey()] === null) { + if ($row[$this->tableAliases[$tableKey]][$clause->localKey] === null) { $resolve(null); return; @@ -424,15 +409,15 @@ private function buildTree(array $row, InspectedEntityInterface $entity, string $where = []; foreach ($join->clause() as $clause) { - $onLeftSide = $clause->foreignKey(); - if ($clause->foreignFunction() !== null) { + $onLeftSide = $clause->foreignKey; + if ($clause->foreignFunction !== null) { /** @psalm-suppress PossiblyNullArgument */ - $onLeftSide = func($clause->foreignFunction(), $onLeftSide); + $onLeftSide = func($clause->foreignFunction, $onLeftSide); } - if ($clause->foreignCast() !== null) { + if ($clause->foreignCast !== null) { /** @psalm-suppress PossiblyNullArgument */ - $onLeftSide = alias(func('CAST', $onLeftSide), $clause->foreignCast()); + $onLeftSide = alias(func('CAST', $onLeftSide), $clause->foreignCast); } if (is_string($onLeftSide)) { @@ -440,7 +425,7 @@ private function buildTree(array $row, InspectedEntityInterface $entity, string $onLeftSide, 'eq', [ - $row[$this->tableAliases[$tableKey]][$clause->localKey()], + $row[$this->tableAliases[$tableKey]][$clause->localKey], ], ); } else { @@ -448,7 +433,7 @@ private function buildTree(array $row, InspectedEntityInterface $entity, string $onLeftSide, 'eq', [ - $row[$this->tableAliases[$tableKey]][$clause->localKey()], + $row[$this->tableAliases[$tableKey]][$clause->localKey], ], ); } @@ -472,10 +457,10 @@ function () use ($row, $join, $tableKey): Observable { foreach ($join->clause() as $clause) { $where[] = new Where\Field( - $clause->foreignKey(), + $clause->foreignKey, 'eq', [ - $row[$this->tableAliases[$tableKey]][$clause->localKey()], + $row[$this->tableAliases[$tableKey]][$clause->localKey], ], ); } diff --git a/src/RepositoryInterface.php b/src/RepositoryInterface.php index d39d72a..55ed75d 100644 --- a/src/RepositoryInterface.php +++ b/src/RepositoryInterface.php @@ -10,49 +10,41 @@ use WyriHaximus\React\SimpleORM\Query\SectionInterface; use WyriHaximus\React\SimpleORM\Query\Where; -/** - * @template T - */ +/** @template T */ interface RepositoryInterface { public const DEFAULT_PER_PAGE = 50; /** * @return PromiseInterface - * @phpstan-ignore-next-line * + * @phpstan-ignore-next-line */ - public function count(Where $where = null): PromiseInterface; + public function count(Where|null $where = null): PromiseInterface; /** * @return Observable + * * @phpstan-ignore-next-line */ - public function page(int $page, Where $where = null, Order $order = null, int $perPage = self::DEFAULT_PER_PAGE): Observable; + public function page(int $page, Where|null $where = null, Order|null $order = null, int $perPage = self::DEFAULT_PER_PAGE): Observable; - /** - * @return Observable - */ + /** @return Observable */ public function fetch(SectionInterface ...$sections): Observable; - /** - * @return Observable - */ + /** @return Observable */ public function stream(SectionInterface ...$sections): Observable; /** * @param array $fields + * * @return PromiseInterface */ public function create(array $fields): PromiseInterface; - /** - * @return PromiseInterface - */ + /** @return PromiseInterface */ public function update(EntityInterface $entity): PromiseInterface; - /** - * @return PromiseInterface - */ + /** @return PromiseInterface */ public function delete(EntityInterface $entity): PromiseInterface; } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 7086a75..a9ba725 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -13,7 +13,7 @@ use Rx\Observable; use WyriHaximus\AsyncTestUtilities\AsyncTestCase; use WyriHaximus\React\SimpleORM\Adapter\Postgres; -use WyriHaximus\React\SimpleORM\Annotation\Table; +use WyriHaximus\React\SimpleORM\Attribute\Table; use WyriHaximus\React\SimpleORM\Client; use WyriHaximus\React\Tests\SimpleORM\Stub\UserStub; diff --git a/tests/RepositoryTest.php b/tests/RepositoryTest.php index acbef3a..3974624 100644 --- a/tests/RepositoryTest.php +++ b/tests/RepositoryTest.php @@ -29,9 +29,7 @@ final class RepositoryTest extends AsyncTestCase { - /** - * @var ObjectProphecy - */ + /** @var ObjectProphecy */ private ObjectProphecy $client; protected function setUp(): void diff --git a/tests/Stub/BlogPostStub.php b/tests/Stub/BlogPostStub.php index 1a313c3..ac138bc 100644 --- a/tests/Stub/BlogPostStub.php +++ b/tests/Stub/BlogPostStub.php @@ -7,74 +7,72 @@ use React\Promise\PromiseInterface; use Rx\Observable; use Safe\DateTimeImmutable; -use WyriHaximus\React\SimpleORM\Annotation\Clause; -use WyriHaximus\React\SimpleORM\Annotation\InnerJoin; -use WyriHaximus\React\SimpleORM\Annotation\LeftJoin; -use WyriHaximus\React\SimpleORM\Annotation\Table; +use WyriHaximus\React\SimpleORM\Attribute\Clause; +use WyriHaximus\React\SimpleORM\Attribute\InnerJoin; +use WyriHaximus\React\SimpleORM\Attribute\JoinInterface; +use WyriHaximus\React\SimpleORM\Attribute\LeftJoin; +use WyriHaximus\React\SimpleORM\Attribute\Table; use WyriHaximus\React\SimpleORM\EntityInterface; use WyriHaximus\React\SimpleORM\Tools\WithFieldsTrait; -/** - * @Table("blog_posts") - * @LeftJoin( - nonExistingProperty="fake", - entity=CommentStub::class, - clause={ - @Clause( - local_key="id", - local_cast="BIGINT", - foreign_key="blog_post_id", - ) - }, - property="comments", - lazy=LeftJoin::IS_LAZY - * ) - * @InnerJoin( - entity=UserStub::class, - clause={ - @Clause( - local_key="author_id", - foreign_key="id", - ) - }, - property="author", - lazy=InnerJoin::IS_NOT_LAZY - * ) - * @InnerJoin( - entity=UserStub::class, - clause={ - @Clause( - local_key="publisher_id", - foreign_key="id", - ) - }, - property="publisher", - lazy=InnerJoin::IS_NOT_LAZY - * ) - * @InnerJoin( - entity=BlogPostStub::class, - clause={ - @Clause( - local_key="previous_blog_post_id", - foreign_key="id", - ) - }, - property="previous_blog_post", - lazy=InnerJoin::IS_LAZY - * ) - * @InnerJoin( - entity=BlogPostStub::class, - clause={ - @Clause( - local_key="next_blog_post_id", - foreign_key="id", - ) - }, - property="next_blog_post", - lazy=InnerJoin::IS_NOT_LAZY - * ) - */ -final class BlogPostStub implements EntityInterface +#[Table('blog_posts')] +#[LeftJoin( + entity: CommentStub::class, + clause: [ + new Clause( + localKey: 'id', + localCast: 'BIGINT', + foreignKey: 'blog_post_id', + ), + ], + property: 'comments', + lazy: JoinInterface::IS_LAZY, +)] +#[InnerJoin( + entity: UserStub::class, + clause: [ + new Clause( + localKey: 'author_id', + foreignKey: 'id', + ), + ], + property: 'author', + lazy: JoinInterface::IS_LAZY, +)] +#[InnerJoin( + entity: UserStub::class, + clause: [ + new Clause( + localKey: 'publisher_id', + foreignKey: 'id', + ), + ], + property: 'publisher', + lazy: JoinInterface::IS_LAZY, +)] +#[InnerJoin( + entity: BlogPostStub::class, + clause: [ + new Clause( + localKey: 'previous_blog_post_id', + foreignKey: 'id', + ), + ], + property: 'previous_blog_post', + lazy: JoinInterface::IS_LAZY, +)] +#[InnerJoin( + entity: BlogPostStub::class, + clause: [ + new Clause( + localKey: 'next_blog_post_id', + foreignKey: 'id', + ), + ], + property: 'next_blog_post', + lazy: JoinInterface::IS_LAZY, +)] +final readonly class BlogPostStub implements EntityInterface { use WithFieldsTrait; @@ -121,9 +119,7 @@ public function id(): string return $this->id; } - /** - * @return PromiseInterface - */ + /** @return PromiseInterface */ public function getPreviousBlogPost(): PromiseInterface { //phpcs:disable @@ -131,9 +127,7 @@ public function getPreviousBlogPost(): PromiseInterface //phpcs:enable } - /** - * @return PromiseInterface - */ + /** @return PromiseInterface */ public function getNextBlogPost(): PromiseInterface { //phpcs:disable diff --git a/tests/Stub/CommentStub.php b/tests/Stub/CommentStub.php index 47dc086..2152ba0 100644 --- a/tests/Stub/CommentStub.php +++ b/tests/Stub/CommentStub.php @@ -4,36 +4,34 @@ namespace WyriHaximus\React\Tests\SimpleORM\Stub; -use WyriHaximus\React\SimpleORM\Annotation\Clause; -use WyriHaximus\React\SimpleORM\Annotation\InnerJoin; -use WyriHaximus\React\SimpleORM\Annotation\Table; +use WyriHaximus\React\SimpleORM\Attribute\Clause; +use WyriHaximus\React\SimpleORM\Attribute\InnerJoin; +use WyriHaximus\React\SimpleORM\Attribute\Table; use WyriHaximus\React\SimpleORM\EntityInterface; use WyriHaximus\React\SimpleORM\Tools\WithFieldsTrait; -/** - * @Table("comments") - * @InnerJoin( - entity=UserStub::class, - clause={ - @Clause( - local_key="author_id", - foreign_key="id", - ) - }, - property="author" - * ) - * @InnerJoin( - entity=BlogPostStub::class, - clause={ - @Clause( - local_key="blog_post_id", - foreign_key="id", - ) - }, - property="blog_post" - * ) - */ -final class CommentStub implements EntityInterface +#[Table('comments')] +#[InnerJoin( + entity: UserStub::class, + clause: [ + new Clause( + localKey: 'author_id', + foreignKey: 'id', + ), + ], + property: 'author', +)] +#[InnerJoin( + entity: BlogPostStub::class, + clause: [ + new Clause( + localKey: 'blog_post_id', + foreignKey: 'id', + ), + ], + property: 'blog_post', +)] +final readonly class CommentStub implements EntityInterface { use WithFieldsTrait; diff --git a/tests/Stub/LogStub.php b/tests/Stub/LogStub.php index 6b5922b..8ff9a76 100644 --- a/tests/Stub/LogStub.php +++ b/tests/Stub/LogStub.php @@ -4,26 +4,18 @@ namespace WyriHaximus\React\Tests\SimpleORM\Stub; -use WyriHaximus\React\SimpleORM\Annotation\Table; +use WyriHaximus\React\SimpleORM\Attribute\Table; use WyriHaximus\React\SimpleORM\EntityInterface; use WyriHaximus\React\SimpleORM\Tools\WithFieldsTrait; -/** @Table("logs") */ -final class LogStub implements EntityInterface +#[Table('logs')] +final readonly class LogStub implements EntityInterface { use WithFieldsTrait; - protected string $id; - - protected string $message; - - public function id(): string - { - return $this->id; - } - - public function message(): string - { - return $this->message; + public function __construct( + public string $id, + public string $message, + ) { } } diff --git a/tests/Stub/NoSQLStub.php b/tests/Stub/NoSQLStub.php index 12676b0..8a06880 100644 --- a/tests/Stub/NoSQLStub.php +++ b/tests/Stub/NoSQLStub.php @@ -6,10 +6,12 @@ use WyriHaximus\React\SimpleORM\EntityInterface; -final class NoSQLStub implements EntityInterface +final readonly class NoSQLStub implements EntityInterface { - public function id(): string + public string $id; + + public function __construct() { - return ''; + $this->id = ''; } } diff --git a/tests/Stub/UserStub.php b/tests/Stub/UserStub.php index aed458b..eefea71 100644 --- a/tests/Stub/UserStub.php +++ b/tests/Stub/UserStub.php @@ -5,28 +5,27 @@ namespace WyriHaximus\React\Tests\SimpleORM\Stub; use React\Promise\PromiseInterface; -use WyriHaximus\React\SimpleORM\Annotation\Clause; -use WyriHaximus\React\SimpleORM\Annotation\InnerJoin; -use WyriHaximus\React\SimpleORM\Annotation\Table; +use WyriHaximus\React\SimpleORM\Attribute\Clause; +use WyriHaximus\React\SimpleORM\Attribute\InnerJoin; +use WyriHaximus\React\SimpleORM\Attribute\JoinInterface; +use WyriHaximus\React\SimpleORM\Attribute\Table; use WyriHaximus\React\SimpleORM\EntityInterface; use WyriHaximus\React\SimpleORM\Tools\WithFieldsTrait; -/** - * @Table("users") - * @InnerJoin( - entity=UserStub::class, - clause={ - @Clause( - local_key="name", - foreign_key="name", - foreign_function="INITCAP", - ) - }, - property="zelf", - lazy=true - * ) - */ -final class UserStub implements EntityInterface +#[Table('users')] +#[InnerJoin( + entity: UserStub::class, + clause: [ + new Clause( + localKey: 'name', + foreignKey: 'name', + foreignFunction: 'INITCAP', + ), + ], + property: 'zelf', + lazy: JoinInterface::IS_LAZY, +)] +final readonly class UserStub implements EntityInterface { use WithFieldsTrait; @@ -34,9 +33,7 @@ final class UserStub implements EntityInterface protected string $name; - /** - * @var PromiseInterface - */ + /** @var PromiseInterface */ protected PromiseInterface $zelf; public function id(): string @@ -49,9 +46,7 @@ public function getName(): string return $this->name; } - /** - * @return PromiseInterface - */ + /** @return PromiseInterface */ public function getZelf(): PromiseInterface { return $this->zelf; diff --git a/tests/types/basic.php b/tests/types/basic.php index 2152771..21c7d9b 100644 --- a/tests/types/basic.php +++ b/tests/types/basic.php @@ -1,12 +1,15 @@ repository(\WyriHaximus\React\Tests\SimpleORM\Stub\NoSQLStub::class); +$client = Client::create(); +$repository = $client->repository(NoSQLStub::class); assertType('WyriHaximus\React\SimpleORM\RepositoryInterface', $repository); assertType('React\Promise\PromiseInterface', $repository->count());