diff --git a/src/Db.php b/src/Db.php index aa1ae6e..2918d69 100644 --- a/src/Db.php +++ b/src/Db.php @@ -38,7 +38,7 @@ public function exec(): bool */ public function fetch(int|string|array|callable|object $object = stdClass::class, array|null $extra = null): mixed { - $result = $this->performFetch(__FUNCTION__, $object, $extra); + $result = $this->executeStatement($object, $extra)->fetch(); return is_callable($object) ? $object($result) : $result; } @@ -51,16 +51,11 @@ public function fetchAll( int|string|array|callable|object $object = stdClass::class, array|null $extra = null, ): mixed { - $result = $this->performFetch(__FUNCTION__, $object, $extra); + $result = $this->executeStatement($object, $extra)->fetchAll(); return is_callable($object) ? array_map($object, $result) : $result; } - public function getSql(): Sql - { - return $this->currentSql; - } - /** * @param int|string|array|callable|object $object * @param array|null $extra @@ -101,18 +96,6 @@ private function executeStatement( return $statement; } - /** - * @param int|string|array|callable|object $object - * @param array|null $extra - */ - private function performFetch( - string $method, - int|string|array|callable|object $object = stdClass::class, - array|null $extra = null, - ): mixed { - return $this->executeStatement($object, $extra)->{$method}(); - } - /** @param array $arguments */ public function __call(string $methodName, array $arguments): static { diff --git a/src/Mapper.php b/src/Mapper.php index b67ea09..69fe6d9 100644 --- a/src/Mapper.php +++ b/src/Mapper.php @@ -8,10 +8,10 @@ use PDOException; use PDOStatement; use Respect\Data\AbstractMapper; -use Respect\Data\CollectionIterator; -use Respect\Data\Collections\Collection; use Respect\Data\Hydrator; use Respect\Data\Hydrators\PrestyledAssoc; +use Respect\Data\Scope; +use Respect\Data\ScopeIterator; use SplObjectStorage; use Throwable; @@ -39,47 +39,43 @@ public function __construct(PDO|Db $db, Hydrator $hydrator = new PrestyledAssoc( $this->persisting = new SplObjectStorage(); } - public function fetch(Collection $collection, mixed $extra = null): mixed + public function fetch(Scope $scope, mixed $extra = null): mixed { if ($extra === null) { - $cached = $this->findInIdentityMap($collection); + $cached = $this->findInIdentityMap($scope); if ($cached !== null) { return $cached; } } - $hydrated = $this->fetchHydrated($collection, $this->createStatement($collection, $extra)); + $hydrated = $this->fetchHydrated($scope, $this->createStatement($scope, $extra)); return $hydrated ? $this->parseHydrated($hydrated) : false; } /** @return array */ - public function fetchAll(Collection $collection, mixed $extra = null): array + public function fetchAll(Scope $scope, mixed $extra = null): array { - $statement = $this->createStatement($collection, $extra); + $statement = $this->createStatement($scope, $extra); $entities = []; - while ($hydrated = $this->fetchHydrated($collection, $statement)) { + while ($hydrated = $this->fetchHydrated($scope, $statement)) { $entities[] = $this->parseHydrated($hydrated); } return $entities; } - public function persist(object $object, Collection $onCollection): object + public function persist(object $object, Scope $onScope): object { if ($this->persisting->offsetExists($object)) { - return parent::persist($object, $onCollection); + return parent::persist($object, $onScope); } $this->persisting[$object] = true; try { - foreach ($onCollection->with as $child) { - if ($child->name === null) { - continue; - } - + foreach ($onScope->with as $child) { $remote = $this->style->remoteIdentifier($child->name); $related = $this->getRelatedEntity($object, $remote); if ($related === null) { @@ -92,7 +88,7 @@ public function persist(object $object, Collection $onCollection): object $this->persisting->offsetUnset($object); } - return parent::persist($object, $onCollection); + return parent::persist($object, $onScope); } public function flush(): void @@ -138,7 +134,7 @@ private function flushSingle(object $entity): void $op = $this->pending[$entity]; match ($op) { - 'delete' => $this->rawDelete($cols, $coll, $entity), + 'delete' => $this->rawDelete($cols, $coll), 'insert' => $this->rawInsert($cols, $coll, $entity), default => $this->rawUpdate($cols, $coll), }; @@ -155,37 +151,35 @@ private function flushSingle(object $entity): void * * @return array> */ - private function guessCondition(array &$columns, Collection $collection): array + private function guessCondition(array &$columns, Scope $scope): array { - $primaryName = $this->style->identifier($collection->name); + $primaryName = $this->style->identifier($scope->name); $condition = [[$primaryName, '=', $columns[$primaryName]]]; unset($columns[$primaryName]); return $condition; } - /** @param array $condition */ + /** @param array $columns */ private function rawDelete( - array $condition, - Collection $collection, - object $entity, + array $columns, + Scope $scope, ): bool { - $columns = $this->extractColumns($entity, $collection); - $condition = $this->guessCondition($columns, $collection); + $condition = $this->guessCondition($columns, $scope); return $this->db - ->deleteFrom($collection->name) + ->deleteFrom($scope->name) ->where($condition) ->exec(); } /** @param array $columns */ - private function rawUpdate(array $columns, Collection $collection): bool + private function rawUpdate(array $columns, Scope $scope): bool { - $condition = $this->guessCondition($columns, $collection); + $condition = $this->guessCondition($columns, $scope); return $this->db - ->update($collection->name) + ->update($scope->name) ->set($columns) ->where($condition) ->exec(); @@ -194,22 +188,22 @@ private function rawUpdate(array $columns, Collection $collection): bool /** @param array $columns */ private function rawInsert( array $columns, - Collection $collection, + Scope $scope, object|null $entity = null, ): bool { $result = $this->db - ->insertInto($collection->name, array_keys($columns)) + ->insertInto($scope->name, array_keys($columns)) ->values(array_values($columns)) ->exec(); if ($entity !== null) { - $this->checkNewIdentity($entity, $collection); + $this->checkNewIdentity($entity, $scope); } return $result; } - private function checkNewIdentity(object $entity, Collection $collection): bool + private function checkNewIdentity(object $entity, Scope $scope): bool { try { $identity = $this->db->connection->lastInsertId(); @@ -221,27 +215,27 @@ private function checkNewIdentity(object $entity, Collection $collection): bool return false; } - $this->entityFactory->set($entity, $this->style->identifier($collection->name), (int) $identity); + $this->entityFactory->set($entity, $this->style->identifier($scope->name), (int) $identity); return true; } - private function generateQuery(Collection $collection): Sql + private function generateQuery(Scope $scope): Sql { - $collections = iterator_to_array( - CollectionIterator::recursive($collection), + $scopes = iterator_to_array( + ScopeIterator::recursive($scope), true, ); $sql = new Sql(); - $this->buildSelectStatement($sql, $collections); - $this->buildTables($sql, $collections); + $this->buildSelectStatement($sql, $scopes); + $this->buildTables($sql, $scopes); return $sql; } /** @return array */ - private function extractColumns(object $entity, Collection $collection): array + private function extractColumns(object $entity, Scope $scope): array { $dbCols = []; foreach ($this->entityFactory->extractColumns($entity) as $key => $value) { @@ -251,11 +245,11 @@ private function extractColumns(object $entity, Collection $collection): array return $dbCols; } - /** @param array $collections */ - private function buildSelectStatement(Sql $sql, array $collections): Sql + /** @param array $scopes */ + private function buildSelectStatement(Sql $sql, array $scopes): Sql { $selectTable = []; - foreach ($collections as $tableSpecifier => $c) { + foreach ($scopes as $tableSpecifier => $c) { foreach ($this->entityFactory->enumerateFields($c->name) as $dbCol => $styledProp) { $selectTable[] = self::aliasedColumn($tableSpecifier, $dbCol, $styledProp); } @@ -270,15 +264,15 @@ private static function aliasedColumn(string $specifier, string $dbCol, string $ return [$specifier . '__' . $prop => $specifier . '.' . $dbCol]; } - /** @param array $collections */ - private function buildTables(Sql $sql, array $collections): Sql + /** @param array $scopes */ + private function buildTables(Sql $sql, array $scopes): Sql { $conditions = $aliases = []; - foreach ($collections as $alias => $collection) { - $this->parseCollection( + foreach ($scopes as $alias => $scope) { + $this->parseScope( $sql, - $collection, + $scope, $alias, $aliases, $conditions, @@ -293,15 +287,15 @@ private function buildTables(Sql $sql, array $collections): Sql * * @return array */ - private function parseConditions(array &$conditions, Collection $collection, string $alias): array + private function parseConditions(array &$conditions, Scope $scope, string $alias): array { $parsedConditions = []; - $aliasedPk = $alias . '.' . $this->style->identifier($collection->name); + $aliasedPk = $alias . '.' . $this->style->identifier($scope->name); - if (is_scalar($collection->filter)) { - $parsedConditions[] = [$aliasedPk, '=', $collection->filter]; - } elseif (is_array($collection->filter)) { - foreach ($collection->filter as $column => $value) { + if (is_scalar($scope->filter)) { + $parsedConditions[] = [$aliasedPk, '=', $scope->filter]; + } elseif (is_array($scope->filter)) { + foreach ($scope->filter as $column => $value) { if (!empty($parsedConditions)) { $parsedConditions[] = 'AND'; } @@ -317,22 +311,22 @@ private function parseConditions(array &$conditions, Collection $collection, str * @param array $aliases * @param array $conditions */ - private function parseCollection( + private function parseScope( Sql $sql, - Collection $collection, + Scope $scope, string $alias, array &$aliases, array &$conditions, ): void { $s = $this->style; - $entity = $collection->name; - $parent = $collection->parent?->name; + $entity = $scope->name; + $parent = $scope->parent?->name; $parentAlias = $parent ? $aliases[$parent] : null; $aliases[$entity] = $alias; $parsed = $this->parseConditions( $conditions, - $collection, + $scope, $alias, ); if (!empty($parsed)) { @@ -343,14 +337,14 @@ private function parseCollection( array_push($conditions, ...$parsed); } - //No parent collection means it's the first table in the query + //No parent scope means it's the first table in the query if ($parentAlias === null) { $sql->from($entity); return; } - if ($collection->required) { + if ($scope->required) { $sql->innerJoin($entity); } else { $sql->leftJoin($entity); @@ -363,7 +357,7 @@ private function parseCollection( $aliasedPk = $alias . '.' . $s->identifier($entity); $aliasedParentPk = $parentAlias . '.' . $s->identifier($parent); - if ($this->isCompositionJoin($collection, $entity, $parent)) { + if ($this->isCompositionJoin($scope, $entity, $parent)) { $onName = $alias . '.' . $s->remoteIdentifier($parent); $onAlias = $aliasedParentPk; } else { @@ -374,13 +368,10 @@ private function parseCollection( $sql->on([$onName => $onAlias]); } - private function isCompositionJoin(Collection $collection, string $entity, string $parent): bool + private function isCompositionJoin(Scope $scope, string $entity, string $parent): bool { - foreach ($collection->with as $child) { + foreach ($scope->with as $child) { $connected = $child->name; - if ($connected === null) { - continue; - } if ( $entity === $this->style->composed($parent, $connected) @@ -393,7 +384,7 @@ private function isCompositionJoin(Collection $collection, string $entity, strin return false; } - /** @param SplObjectStorage $hydrated */ + /** @param SplObjectStorage $hydrated */ private function parseHydrated(SplObjectStorage $hydrated): object { $this->tracked->addAll($hydrated); @@ -408,19 +399,19 @@ private function parseHydrated(SplObjectStorage $hydrated): object return $hydrated->current(); } - /** @return SplObjectStorage|false */ - private function fetchHydrated(Collection $collection, PDOStatement $statement): SplObjectStorage|false + /** @return SplObjectStorage|false */ + private function fetchHydrated(Scope $scope, PDOStatement $statement): SplObjectStorage|false { $row = $statement->fetch(PDO::FETCH_ASSOC); - return $this->hydrator->hydrateAll($row, $collection); + return $this->hydrator->hydrateAll($row, $scope); } private function createStatement( - Collection $collection, + Scope $scope, mixed $withExtra = null, ): PDOStatement { - $query = $this->generateQuery($collection); + $query = $this->generateQuery($scope); if ($withExtra instanceof Sql) { $query->concat($withExtra); diff --git a/tests/DbTest.php b/tests/DbTest.php index b8ad777..36f24e5 100644 --- a/tests/DbTest.php +++ b/tests/DbTest.php @@ -108,14 +108,6 @@ public function testFetchingArray2(): void $this->assertTrue(is_array($line)); } - public function testGetSql(): void - { - $sql = $this->object->select('*')->from('unit') - ->where([['testb', '=', 'abc']])->getSql(); - $this->assertEquals('SELECT * FROM unit WHERE testb = ?', (string) $sql); - $this->assertEquals(['abc'], $sql->params); - } - public function testFluentSelectWithParams(): void { $line = $this->object->select('*')->from('unit') diff --git a/tests/MapperTest.php b/tests/MapperTest.php index 3d8eeb6..5789cb1 100644 --- a/tests/MapperTest.php +++ b/tests/MapperTest.php @@ -365,8 +365,7 @@ public function testNestedPersistCollectionShortcut(): void $postWithAuthor->title = 'hi'; $postWithAuthor->text = 'hi text'; $postWithAuthor->author = $author; - $this->mapper->registerCollection('postAuthor', $this->mapper->post([$this->mapper->author()])); - $this->mapper->persist($postWithAuthor, $this->mapper->postAuthor()); + $this->mapper->persist($postWithAuthor, $this->mapper->post([$this->mapper->author()])); $this->mapper->flush(); $author = $this->query( 'select * from author order by id desc limit 1', @@ -378,7 +377,7 @@ public function testNestedPersistCollectionShortcut(): void $this->assertEquals('hi', $post->title); } - public function testNestedPersistCollectionWithChildrenShortcut(): void + public function testNestedPersistWithChildrenShortcut(): void { $author = new Author(); $author->name = 'New'; @@ -386,8 +385,7 @@ public function testNestedPersistCollectionWithChildrenShortcut(): void $postWithAuthor->title = 'hi'; $postWithAuthor->text = 'hi text'; $postWithAuthor->author = $author; - $this->mapper->registerCollection('postAuthor', $this->mapper->post([$this->mapper->author()])); - $this->mapper->persist($postWithAuthor, $this->mapper->postAuthor()); + $this->mapper->persist($postWithAuthor, $this->mapper->post([$this->mapper->author()])); $this->mapper->flush(); $author = $this->query( 'select * from author order by id desc limit 1', @@ -748,15 +746,6 @@ public function testFetchWithExtraBypassesIdentityMap(): void $this->assertEquals($first->id, $second->id); } - public function testIdentityMapCountIncreasesOnFetch(): void - { - $this->assertSame(0, $this->mapper->identityMapCount()); - - $this->mapper->fetch($this->mapper->author(filter: 1)); - - $this->assertGreaterThan(0, $this->mapper->identityMapCount()); - } - public function testClearIdentityMapForcesFreshFetch(): void { $first = $this->mapper->fetch($this->mapper->post(filter: 5)); diff --git a/tests/Stubs/Postcomment.php b/tests/Stubs/Postcomment.php deleted file mode 100644 index 601eeff..0000000 --- a/tests/Stubs/Postcomment.php +++ /dev/null @@ -1,16 +0,0 @@ -