diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 28995ec..af237be 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,17 +1,47 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method ReflectionType\\:\\:getName\\(\\)\\.$#" + message: "#^Parameter \\#1 \\$query of method Squirrel\\\\Entities\\\\RepositoryReadOnlyInterface\\:\\:fetchAll\\(\\) expects array\\(\\)\\|array\\('fields' \\=\\> array\\, 'field' \\=\\> string, 'where' \\=\\> array\\, 'order' \\=\\> array\\, 'limit' \\=\\> int, 'offset' \\=\\> int, 'lock' \\=\\> bool\\), array\\('where' \\=\\> array\\, 'order' \\=\\> array\\, 'fields' \\=\\> array\\, 'limit' \\=\\> int, 'offset' \\=\\> int, 'lock' \\=\\> bool\\) given\\.$#" count: 1 - path: src/Annotation/EntityProcessor.php + path: src/Action/SelectEntries.php - - message: "#^Parameter \\#1 \\$query of method Squirrel\\\\Queries\\\\DBInterface\\:\\:fetchOne\\(\\) expects array\\\\|bool\\|int\\|string\\>\\|string, array\\\\|string\\|true\\> given\\.$#" + message: "#^Parameter \\#1 \\$query of method Squirrel\\\\Entities\\\\RepositoryReadOnlyInterface\\:\\:fetchOne\\(\\) expects array\\(\\)\\|array\\('fields' \\=\\> array\\, 'field' \\=\\> string, 'where' \\=\\> array\\, 'order' \\=\\> array\\, 'offset' \\=\\> int, 'lock' \\=\\> bool\\), array\\('where' \\=\\> array\\, 'order' \\=\\> array\\, 'fields' \\=\\> array\\, 'offset' \\=\\> int, 'lock' \\=\\> bool\\) given\\.$#" count: 1 - path: src/RepositoryReadOnly.php + path: src/Action/SelectEntries.php + + - + message: "#^Parameter \\#1 \\$query of method Squirrel\\\\Entities\\\\RepositoryReadOnlyInterface\\:\\:fetchAllAndFlatten\\(\\) expects array\\(\\)\\|array\\('fields' \\=\\> array\\, 'field' \\=\\> string, 'where' \\=\\> array\\, 'order' \\=\\> array\\, 'limit' \\=\\> int, 'offset' \\=\\> int, 'lock' \\=\\> bool\\), array\\('where' \\=\\> array\\, 'order' \\=\\> array\\, 'fields' \\=\\> array\\, 'limit' \\=\\> int, 'offset' \\=\\> int, 'lock' \\=\\> bool\\) given\\.$#" + count: 1 + path: src/Action/SelectEntries.php + + - + message: "#^Method Squirrel\\\\Entities\\\\Action\\\\SelectEntries\\:\\:getFlattenedIntegerFields\\(\\) should return array\\ but returns array\\\\.$#" + count: 1 + path: src/Action/SelectEntries.php + + - + message: "#^Method Squirrel\\\\Entities\\\\Action\\\\SelectEntries\\:\\:getFlattenedFloatFields\\(\\) should return array\\ but returns array\\\\.$#" + count: 1 + path: src/Action/SelectEntries.php - - message: "#^Parameter \\#1 \\$query of method Squirrel\\\\Queries\\\\DBInterface\\:\\:select\\(\\) expects array\\\\|bool\\|int\\|string\\>\\|string, array\\ given\\.$#" + message: "#^Method Squirrel\\\\Entities\\\\Action\\\\SelectEntries\\:\\:getFlattenedStringFields\\(\\) should return array\\ but returns array\\\\.$#" + count: 1 + path: src/Action/SelectEntries.php + + - + message: "#^Method Squirrel\\\\Entities\\\\Action\\\\SelectEntries\\:\\:getFlattenedBooleanFields\\(\\) should return array\\ but returns array\\\\.$#" + count: 1 + path: src/Action/SelectEntries.php + + - + message: "#^Call to an undefined method ReflectionType\\:\\:getName\\(\\)\\.$#" + count: 1 + path: src/Annotation/EntityProcessor.php + + - + message: "#^Parameter \\#1 \\$query of method Squirrel\\\\Queries\\\\DBInterface\\:\\:fetchOne\\(\\) expects array\\(\\)\\|array\\('fields' \\=\\> array\\, 'field' \\=\\> string, 'tables' \\=\\> array\\, 'table' \\=\\> string, 'where' \\=\\> array\\, 'group' \\=\\> array\\, 'order' \\=\\> array\\, 'limit' \\=\\> int, \\.\\.\\.\\)\\|string, array\\('table' \\=\\> string, 'fields' \\=\\> array\\('num' \\=\\> 'COUNT\\(\\*\\)'\\), \\?'where' \\=\\> array\\, \\?'lock' \\=\\> true\\) given\\.$#" count: 1 path: src/RepositoryReadOnly.php @@ -21,7 +51,7 @@ parameters: path: src/RepositoryReadOnly.php - - message: "#^Parameter \\#1 \\$query of method Squirrel\\\\Queries\\\\DBInterface\\:\\:fetchAll\\(\\) expects array\\\\|bool\\|int\\|string\\>\\|string, array\\ given\\.$#" - count: 2 + message: "#^Parameter \\#1 \\$query of method Squirrel\\\\Entities\\\\RepositoryReadOnly\\:\\:select\\(\\) expects array\\(\\)\\|array\\('fields' \\=\\> array, 'field' \\=\\> string, 'where' \\=\\> array, 'order' \\=\\> array, 'limit' \\=\\> int, 'offset' \\=\\> int, 'lock' \\=\\> bool\\), array\\(\\?'fields' \\=\\> array\\, \\?'field' \\=\\> string, \\?'where' \\=\\> array\\, \\?'order' \\=\\> array\\, \\?'offset' \\=\\> int, \\?'lock' \\=\\> bool, 'limit' \\=\\> 1\\) given\\.$#" + count: 1 path: src/RepositoryReadOnly.php diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 8ff0963..f3fdf40 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,19 @@ + + + $values + $values + $values + $values + + + int[] + float[] + string[] + bool[] + + $db diff --git a/src/Action/MultiCountEntries.php b/src/Action/MultiCountEntries.php index 9a22575..d72644d 100644 --- a/src/Action/MultiCountEntries.php +++ b/src/Action/MultiCountEntries.php @@ -3,6 +3,7 @@ namespace Squirrel\Entities\Action; use Squirrel\Entities\MultiRepositoryReadOnlyInterface; +use Squirrel\Entities\RepositoryBuilderReadOnlyInterface; use Squirrel\Entities\RepositoryReadOnlyInterface; /** @@ -13,7 +14,7 @@ class MultiCountEntries implements ActionInterface private MultiRepositoryReadOnlyInterface $queryHandler; /** - * @var RepositoryReadOnlyInterface[] Repositories used in the multi query + * @var array Repositories used in the multi query */ private array $repositories = []; @@ -38,7 +39,7 @@ public function __construct(MultiRepositoryReadOnlyInterface $queryHandler) } /** - * @param RepositoryReadOnlyInterface[] $repositories + * @param array $repositories */ public function inRepositories(array $repositories): self { diff --git a/src/Action/SelectEntries.php b/src/Action/SelectEntries.php index 2102b5d..8ff8eac 100644 --- a/src/Action/SelectEntries.php +++ b/src/Action/SelectEntries.php @@ -2,7 +2,9 @@ namespace Squirrel\Entities\Action; +use Squirrel\Debug\Debug; use Squirrel\Entities\RepositoryReadOnlyInterface; +use Squirrel\Queries\Exception\DBInvalidOptionException; /** * Select query builder as a fluent object - build query and return object(s) or flattened fields @@ -155,6 +157,91 @@ public function getFlattenedFields(): array ]); } + /** + * @return int[] + */ + public function getFlattenedIntegerFields(): array + { + $values = $this->getFlattenedFields(); + + foreach ($values as $value) { + if (!\is_int($value)) { + throw Debug::createException( + DBInvalidOptionException::class, + [ActionInterface::class], + 'Flattened integers requested, but not all values were integers' + ); + } + } + + return $values; + } + + /** + * @return float[] + */ + public function getFlattenedFloatFields(): array + { + $values = $this->getFlattenedFields(); + + foreach ($values as $key => $value) { + if (\is_int($value)) { + $values[$key] = \floatval($value); + continue; + } + + if (!\is_float($value)) { + throw Debug::createException( + DBInvalidOptionException::class, + [ActionInterface::class], + 'Flattened floats requested, but not all values were floats' + ); + } + } + + return $values; + } + + /** + * @return string[] + */ + public function getFlattenedStringFields(): array + { + $values = $this->getFlattenedFields(); + + foreach ($values as $value) { + if (!\is_string($value)) { + throw Debug::createException( + DBInvalidOptionException::class, + [ActionInterface::class], + 'Flattened strings requested, but not all values were strings' + ); + } + } + + return $values; + } + + /** + * @return bool[] + */ + public function getFlattenedBooleanFields(): array + { + $values = $this->getFlattenedFields(); + + foreach ($values as $value) { + if (!\is_bool($value)) { + throw Debug::createException( + DBInvalidOptionException::class, + [ActionInterface::class], + 'Flattened booleans requested, but not all values were booleans' + ); + } + } + + return $values; + } + public function getIterator(): SelectIterator { return new SelectIterator($this->repository, [ diff --git a/tests/RepositoryActions/SelectEntriesTest.php b/tests/RepositoryActions/SelectEntriesTest.php index 1b99f0d..673d144 100644 --- a/tests/RepositoryActions/SelectEntriesTest.php +++ b/tests/RepositoryActions/SelectEntriesTest.php @@ -5,6 +5,7 @@ use Squirrel\Entities\Action\SelectEntries; use Squirrel\Entities\Action\SelectIterator; use Squirrel\Entities\RepositoryReadOnlyInterface; +use Squirrel\Queries\Exception\DBInvalidOptionException; class SelectEntriesTest extends \PHPUnit\Framework\TestCase { @@ -305,4 +306,356 @@ public function testGetFlattenedFields() $this->assertEquals([], $results); } + + public function testGetFlattenedIntegerFields() + { + $this->selectBuilder + ->where([ + 'responseId' => 5, + 'otherField' => '333', + ]) + ->orderBy([ + 'responseId' => 'DESC', + ]) + ->startAt(13) + ->limitTo(45) + ->blocking() + ->fields([ + 'responseId', + 'otherField', + ]); + + $this->repository + ->shouldReceive('fetchAllAndFlatten') + ->once() + ->with([ + 'where' => [ + 'responseId' => 5, + 'otherField' => '333', + ], + 'order' => [ + 'responseId' => 'DESC', + ], + 'fields' => [ + 'responseId', + 'otherField', + ], + 'limit' => 45, + 'offset' => 13, + 'lock' => true, + ]) + ->andReturn([5, 6, 8]); + + $results = $this->selectBuilder->getFlattenedIntegerFields(); + + $this->assertEquals([5, 6, 8], $results); + } + + public function testGetFlattenedFloatFields() + { + $this->selectBuilder + ->where([ + 'responseId' => 5, + 'otherField' => '333', + ]) + ->orderBy([ + 'responseId' => 'DESC', + ]) + ->startAt(13) + ->limitTo(45) + ->blocking() + ->fields([ + 'responseId', + 'otherField', + ]); + + $this->repository + ->shouldReceive('fetchAllAndFlatten') + ->once() + ->with([ + 'where' => [ + 'responseId' => 5, + 'otherField' => '333', + ], + 'order' => [ + 'responseId' => 'DESC', + ], + 'fields' => [ + 'responseId', + 'otherField', + ], + 'limit' => 45, + 'offset' => 13, + 'lock' => true, + ]) + ->andReturn([5, 6, 8, 3.7]); + + $results = $this->selectBuilder->getFlattenedFloatFields(); + + $this->assertEquals([5.0, 6.0, 8.0, 3.7], $results); + } + + public function testGetFlattenedBooleanFields() + { + $this->selectBuilder + ->where([ + 'responseId' => 5, + 'otherField' => '333', + ]) + ->orderBy([ + 'responseId' => 'DESC', + ]) + ->startAt(13) + ->limitTo(45) + ->blocking() + ->fields([ + 'responseId', + 'otherField', + ]); + + $this->repository + ->shouldReceive('fetchAllAndFlatten') + ->once() + ->with([ + 'where' => [ + 'responseId' => 5, + 'otherField' => '333', + ], + 'order' => [ + 'responseId' => 'DESC', + ], + 'fields' => [ + 'responseId', + 'otherField', + ], + 'limit' => 45, + 'offset' => 13, + 'lock' => true, + ]) + ->andReturn([true, false, true, true, false]); + + $results = $this->selectBuilder->getFlattenedBooleanFields(); + + $this->assertEquals([true, false, true, true, false], $results); + } + + public function testGetFlattenedStringFields() + { + $this->selectBuilder + ->where([ + 'responseId' => 5, + 'otherField' => '333', + ]) + ->orderBy([ + 'responseId' => 'DESC', + ]) + ->startAt(13) + ->limitTo(45) + ->blocking() + ->fields([ + 'responseId', + 'otherField', + ]); + + $this->repository + ->shouldReceive('fetchAllAndFlatten') + ->once() + ->with([ + 'where' => [ + 'responseId' => 5, + 'otherField' => '333', + ], + 'order' => [ + 'responseId' => 'DESC', + ], + 'fields' => [ + 'responseId', + 'otherField', + ], + 'limit' => 45, + 'offset' => 13, + 'lock' => true, + ]) + ->andReturn(['dada', '5', 'rtew', '', '7777.3']); + + $results = $this->selectBuilder->getFlattenedStringFields(); + + $this->assertEquals(['dada', '5', 'rtew', '', '7777.3'], $results); + } + + public function testGetFlattenedIntegerFieldsWrongType() + { + $this->expectException(DBInvalidOptionException::class); + + $this->selectBuilder + ->where([ + 'responseId' => 5, + 'otherField' => '333', + ]) + ->orderBy([ + 'responseId' => 'DESC', + ]) + ->startAt(13) + ->limitTo(45) + ->blocking() + ->fields([ + 'responseId', + 'otherField', + ]); + + $this->repository + ->shouldReceive('fetchAllAndFlatten') + ->once() + ->with([ + 'where' => [ + 'responseId' => 5, + 'otherField' => '333', + ], + 'order' => [ + 'responseId' => 'DESC', + ], + 'fields' => [ + 'responseId', + 'otherField', + ], + 'limit' => 45, + 'offset' => 13, + 'lock' => true, + ]) + ->andReturn([5, '7', 6, 8]); + + $this->selectBuilder->getFlattenedIntegerFields(); + } + + public function testGetFlattenedFloatFieldsWrongType() + { + $this->expectException(DBInvalidOptionException::class); + + $this->selectBuilder + ->where([ + 'responseId' => 5, + 'otherField' => '333', + ]) + ->orderBy([ + 'responseId' => 'DESC', + ]) + ->startAt(13) + ->limitTo(45) + ->blocking() + ->fields([ + 'responseId', + 'otherField', + ]); + + $this->repository + ->shouldReceive('fetchAllAndFlatten') + ->once() + ->with([ + 'where' => [ + 'responseId' => 5, + 'otherField' => '333', + ], + 'order' => [ + 'responseId' => 'DESC', + ], + 'fields' => [ + 'responseId', + 'otherField', + ], + 'limit' => 45, + 'offset' => 13, + 'lock' => true, + ]) + ->andReturn([5, 6, 8, '3.7']); + + $this->selectBuilder->getFlattenedFloatFields(); + } + + public function testGetFlattenedBooleanFieldsWrongType() + { + $this->expectException(DBInvalidOptionException::class); + + $this->selectBuilder + ->where([ + 'responseId' => 5, + 'otherField' => '333', + ]) + ->orderBy([ + 'responseId' => 'DESC', + ]) + ->startAt(13) + ->limitTo(45) + ->blocking() + ->fields([ + 'responseId', + 'otherField', + ]); + + $this->repository + ->shouldReceive('fetchAllAndFlatten') + ->once() + ->with([ + 'where' => [ + 'responseId' => 5, + 'otherField' => '333', + ], + 'order' => [ + 'responseId' => 'DESC', + ], + 'fields' => [ + 'responseId', + 'otherField', + ], + 'limit' => 45, + 'offset' => 13, + 'lock' => true, + ]) + ->andReturn([true, false, true, 'dada', false]); + + $this->selectBuilder->getFlattenedBooleanFields(); + } + + public function testGetFlattenedStringFieldsWrongType() + { + $this->expectException(DBInvalidOptionException::class); + + $this->selectBuilder + ->where([ + 'responseId' => 5, + 'otherField' => '333', + ]) + ->orderBy([ + 'responseId' => 'DESC', + ]) + ->startAt(13) + ->limitTo(45) + ->blocking() + ->fields([ + 'responseId', + 'otherField', + ]); + + $this->repository + ->shouldReceive('fetchAllAndFlatten') + ->once() + ->with([ + 'where' => [ + 'responseId' => 5, + 'otherField' => '333', + ], + 'order' => [ + 'responseId' => 'DESC', + ], + 'fields' => [ + 'responseId', + 'otherField', + ], + 'limit' => 45, + 'offset' => 13, + 'lock' => true, + ]) + ->andReturn(['dada', '5', 'rtew', 5, '7777.3']); + + $this->selectBuilder->getFlattenedStringFields(); + } }