diff --git a/src/AbstractDbValidator.php b/src/AbstractDbValidator.php index b77f8d2..7cec8f2 100644 --- a/src/AbstractDbValidator.php +++ b/src/AbstractDbValidator.php @@ -33,15 +33,14 @@ abstract class AbstractDbValidator extends AbstractValidator implements AdapterA /** * Error constants */ - public const ERROR_NO_RECORD_FOUND = 'noRecordFound'; - public const ERROR_RECORD_FOUND = 'recordFound'; + public const string ERROR_NO_RECORD_FOUND = 'noRecordFound'; + public const string ERROR_RECORD_FOUND = 'recordFound'; /** * Message templates. * * @var array */ - // @mago-ignore analysis:incompatible-property-type public array $messageTemplates = [ self::ERROR_NO_RECORD_FOUND => 'No record matching the input was found', self::ERROR_RECORD_FOUND => 'A record matching the input was found', @@ -165,7 +164,7 @@ public function getTable(): string /** * Run query and returns matches, or null if no matches are found. * - * @return mixed when matches are found. + * @return mixed when matches are found; null when there are none (PDO reports no rows as false). */ protected function query(mixed $value): mixed { @@ -180,6 +179,8 @@ protected function query(mixed $value): mixed $parameters['where1'] = $value; } - return $statement->execute()?->current(); + $row = $statement->execute()?->current(); + + return false === $row ? null : $row; } } diff --git a/test/unit/NoRecordExistsTest.php b/test/unit/NoRecordExistsTest.php index 7656a03..770106a 100644 --- a/test/unit/NoRecordExistsTest.php +++ b/test/unit/NoRecordExistsTest.php @@ -137,6 +137,22 @@ public function excludeWithStringNoRecord() static::assertTrue($validator->isValid('nosuchvalue')); } + /** + * PDO reports "no rows" as false rather than null; that must still count as no record. + * + * @throws Exception + */ + #[Test] + public function noRowsReportedAsFalseFindsNoRecord(): void + { + $validator = new NoRecordExists([ + 'table' => 'users', + 'field' => 'field1', + 'adapter' => $this->getMockNoResult(false), + ]); + static::assertTrue($validator->isValid('nosuchvalue')); + } + /** * @throws Exception */ @@ -241,13 +257,13 @@ protected function getMockHasResult(): Adapter * * @throws Exception */ - protected function getMockNoResult(): Adapter + protected function getMockNoResult(mixed $current = null): Adapter { // mock the adapter, driver, and parts $mockConnection = $this->createMock(ConnectionInterface::class); $mockNoResult = $this->createMock(ResultInterface::class); - $mockNoResult->method('current')->willReturn(null); + $mockNoResult->method('current')->willReturn($current); $mockNoResultStatement = $this->createMock(StatementInterface::class); $mockNoResultStatement->method('execute')->willReturn($mockNoResult);