From dc944ec96a84f90a491dd37d98d45bdbe245c6a3 Mon Sep 17 00:00:00 2001 From: Theodore Brown Date: Tue, 1 Aug 2023 14:54:23 -0500 Subject: [PATCH] Make col optional when constructing a Prop with a getValue function This will simplify usage and improve efficiency for some computed properties. --- CHANGELOG.md | 7 ++++++- src/Helpers.php | 26 ++++++++++++++------------ src/Prop.php | 16 ++++++++++------ src/QueryOptions.php | 14 +++++++++----- test/EntitiesDbTest.php | 4 +++- test/HelpersTest.php | 4 ++++ test/src/ModernUsers.php | 6 ++++++ 7 files changed, 52 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 328f65d..19edcdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [2.6.0] - 2023-08-01 +### Changed +- `col` is now optional when constructing `Prop` with a `getValue` function. + ## [2.5.0] - 2023-03-05 ### Changed - Minor code cleanup and refactoring. @@ -132,7 +136,8 @@ return early if passed an empty IDs array. ### Changed - Initial stable release -[Unreleased]: https://github.com/theodorejb/phaster/compare/v2.5.0...HEAD +[Unreleased]: https://github.com/theodorejb/phaster/compare/v2.6.0...HEAD +[2.6.0]: https://github.com/theodorejb/phaster/compare/v2.5.0...v2.6.0 [2.5.0]: https://github.com/theodorejb/phaster/compare/v2.4.0...v2.5.0 [2.4.0]: https://github.com/theodorejb/phaster/compare/v2.3.0...v2.4.0 [2.3.0]: https://github.com/theodorejb/phaster/compare/v2.2.2...v2.3.0 diff --git a/src/Helpers.php b/src/Helpers.php index b1996d9..81d3bbc 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -83,9 +83,20 @@ public static function mapRows(\Generator $rows, array $fieldProps): array /** @var Prop[] $nullParents */ $nullParents = []; - /** @var mixed $value */ - foreach ($row as $colName => $value) { - $prop = $aliasMap[$colName]; + foreach ($aliasMap as $colName => $prop) { + if ($prop->getValue) { + /** @var mixed $value */ + $value = ($prop->getValue)($row); + } else { + /** @var mixed $value */ + $value = $row[$colName]; + + if ($prop->type) { + settype($value, $prop->type); + } elseif (is_string($value) && $prop->timeZone !== false) { + $value = (new \DateTimeImmutable($value, $prop->timeZone))->format(\DateTime::ATOM); + } + } if ($prop->nullGroup && $value === null) { // only add if there isn't a higher-level null parent @@ -103,15 +114,6 @@ public static function mapRows(\Generator $rows, array $fieldProps): array continue; } - if ($prop->getValue) { - /** @var mixed $value */ - $value = ($prop->getValue)($row); - } elseif ($prop->type) { - settype($value, $prop->type); - } elseif (is_string($value) && $prop->timeZone !== false) { - $value = (new \DateTimeImmutable($value, $prop->timeZone))->format(\DateTime::ATOM); - } - $key = $prop->map[0]; /** @psalm-suppress EmptyArrayAccess */ $_ref = &$entity[$key]; diff --git a/src/Prop.php b/src/Prop.php index 1b7bc4b..f0cac27 100644 --- a/src/Prop.php +++ b/src/Prop.php @@ -64,7 +64,7 @@ class Prop */ public function __construct( string $name, - string $col, + string $col = '', bool $nullGroup = false, bool $isDefault = true, string $alias = '', @@ -86,7 +86,13 @@ public function __construct( throw new \Exception("nullGroup cannot be set on top-level {$name} property"); } - if ($getValue !== null) { + if ($getValue === null) { + if ($col === '') { + throw new \Exception("col cannot be blank on {$name} property without getValue function"); + } elseif (count($dependsOn) !== 0) { + throw new \Exception("dependsOn cannot be used on {$name} property without getValue function"); + } + } else { if ($type !== null) { throw new \Exception("type cannot be set on {$name} property along with getValue"); } elseif ($timeZone !== false) { @@ -106,10 +112,6 @@ public function __construct( } } - if (count($dependsOn) !== 0 && $getValue === null) { - throw new \Exception("dependsOn cannot be used on {$name} property without getValue function"); - } - $this->name = $name; $this->col = $col; $this->alias = $alias; @@ -125,6 +127,8 @@ public function getOutputCol(): string { if ($this->alias) { return $this->alias; + } elseif ($this->col === '') { + return $this->name; } $col = explode('.', $this->col); diff --git a/src/QueryOptions.php b/src/QueryOptions.php index afb5e75..b00e41c 100644 --- a/src/QueryOptions.php +++ b/src/QueryOptions.php @@ -68,18 +68,22 @@ public function getFieldProps(): array public function getColumns(): string { - $columns = []; + $columns = ''; foreach ($this->fieldProps as $prop) { - $col = $prop->col; + if ($prop->col === '') { + continue; + } + + $columns .= $prop->col; if ($prop->alias) { - $col .= ' AS ' . $prop->alias; + $columns .= ' AS ' . $prop->alias; } - $columns[] = $col; + $columns .= ', '; } - return implode(', ', $columns); + return substr($columns, 0, -2); } } diff --git a/test/EntitiesDbTest.php b/test/EntitiesDbTest.php index 571c37f..57cb4cd 100644 --- a/test/EntitiesDbTest.php +++ b/test/EntitiesDbTest.php @@ -344,13 +344,14 @@ public function testModernUsers(Entities $entities, PeachySql $db): void $ids = $entities->addEntities($users); $db->insertRow('UserThings', ['user_id' => $ids[3]]); - $actual = $entities->getEntitiesByIds([$ids[2], $ids[3]], ['id', 'name', 'isDisabled', 'weight', 'thing.uid']); + $actual = $entities->getEntitiesByIds([$ids[2], $ids[3]], ['id', 'name', 'isDisabled', 'computed', 'weight', 'thing.uid']); $expected = [ [ 'id' => $ids[3], 'name' => 'Modern user 4', 'isDisabled' => false, + 'computed' => $ids[3] - 1, 'weight' => 40.0, 'thing' => [ 'uid' => $ids[3], @@ -360,6 +361,7 @@ public function testModernUsers(Entities $entities, PeachySql $db): void 'id' => $ids[2], 'name' => 'Modern user 3 modified', 'isDisabled' => false, + 'computed' => $ids[2] - 1, 'weight' => 30.0, 'thing' => null, ], diff --git a/test/HelpersTest.php b/test/HelpersTest.php index f4dcdbf..7c7a706 100644 --- a/test/HelpersTest.php +++ b/test/HelpersTest.php @@ -262,6 +262,8 @@ public function testPropMapToAliasMap(): void 'username' => ['col' => 'a.UserName'], 'client.isDisabled' => ['col' => 'c.isDisabled', 'alias' => 'isClientDisabled', 'type' => 'bool'], 'dateCreated' => ['col' => 'DateCreatedUTC', 'timeZone' => $utc], + 'computed' => ['getValue' => fn(array $_): bool => true], + 'computed2' => ['alias' => 'aliased', 'getValue' => fn(array $_): bool => false], ]; $props = Helpers::rawPropMapToProps($rawPropMap); @@ -272,6 +274,8 @@ public function testPropMapToAliasMap(): void 'UserName' => $propMap['username'], 'isClientDisabled' => $propMap['client.isDisabled'], 'DateCreatedUTC' => $propMap['dateCreated'], + 'computed' => $propMap['computed'], + 'aliased' => $propMap['computed2'], ]; $this->assertSame($expected, Helpers::propMapToAliasMap($propMap)); diff --git a/test/src/ModernUsers.php b/test/src/ModernUsers.php index 5da14ed..d42c6ac 100644 --- a/test/src/ModernUsers.php +++ b/test/src/ModernUsers.php @@ -30,10 +30,16 @@ protected function getMap(): array protected function getSelectProps(): array { + $getValue = function (array $row): int { + /** @var array{u_id: int} $row */ + return $row['u_id'] - 1; + }; + return [ new Prop('id', 'u.u_id'), new Prop('name', 'name', false, true, 'username'), new Prop('isDisabled', 'isDisabled', false, true, '', 'bool'), + new Prop('computed', '', false, true, '', null, false, $getValue), new Prop('thing.id', 'ut.thing_id', true), new Prop('thing.uid', 'ut.user_id', false, true, 'thing_user'), ];