Skip to content

Commit

Permalink
Merge pull request #17 from zfegg/develop
Browse files Browse the repository at this point in the history
Add DbalRecordExistsFilter
  • Loading branch information
Moln committed Jan 9, 2022
2 parents 3a84edd + 81eb4b7 commit f2dee6a
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 4 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/qa.yml
Expand Up @@ -12,7 +12,7 @@ jobs:
fail-fast: false
matrix:
operating-system: [ubuntu-latest]
php-versions: ['7.4', '8.0']
php-versions: ['7.4', '8.0', '8.1']
steps:
- name: Checkout
uses: actions/checkout@v2
Expand All @@ -30,8 +30,8 @@ jobs:
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-

- name: Install Composer dependencies
Expand All @@ -49,6 +49,6 @@ jobs:
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
composer require php-coveralls/php-coveralls --dev -q
composer require php-coveralls/php-coveralls "psr/log:^2" --dev -q
./vendor/bin/phpunit --coverage-clover build/logs/clover.xml
./vendor/bin/php-coveralls -v
36 changes: 36 additions & 0 deletions README.md
Expand Up @@ -24,6 +24,42 @@ composer require zfegg/content-validation
Usage / 使用
--------------

### `Opis\JsonSchema\Validator` factory config.

```php
// config.php
return [
Opis\JsonSchema\Validator::class => [
'resolvers' => [
'protocolDir' => [
// foo-schema://host/foo.create.json => schema/dir/foo.create.json
['foo-schema', 'host', 'schema/dir'],
],
'protocol' => [
],
'prefix' => [
['prefix1', 'path/to/dir'],
['prefix2', 'path/to/dir'],
],
'file' => [
['SchemaFoo', 'path/to/file'],
['SchemaBar', 'path/to/file2'],
],
'raw' => [
['{"type":"object", ...}', 'schema id 1'],
['{"type":"object", ...}', 'schema id 2'],
]
],
'filters' => [
'foo-filter' => ['filter' => 'FilterFilterName', 'types' => ['integer']],
],
'filtersNS' => [
'foo-ns' => 'FilterResolverName',
],
]
]
```

### Mezzio

Add `ConfigProvider` in 'config.php'. / 在 `config.php` 中添加 `ConfigProvider`.
Expand Down
6 changes: 6 additions & 0 deletions src/Factory/ValidatorFactory.php
Expand Up @@ -6,6 +6,7 @@

use Opis\JsonSchema\Validator;
use Psr\Container\ContainerInterface;
use Zfegg\ContentValidation\Opis\Filter\DbalRecordExistsFilter;
use Zfegg\ContentValidation\Opis\Filter\DoctrineRecordExistsFilter;
use Zfegg\ContentValidation\Opis\Filter\RecordExistsFilter;
use Zfegg\ContentValidation\Opis\RemoveAdditionalPropertiesParser;
Expand Down Expand Up @@ -65,6 +66,11 @@ public function __invoke(ContainerInterface $container): Validator
new DoctrineRecordExistsFilter($container),
$types
);
$parser->getFilterResolver()->registerMultipleTypes(
'dbal-exists',
new DbalRecordExistsFilter($container),
$types
);

return $validator;
}
Expand Down
43 changes: 43 additions & 0 deletions src/Opis/Filter/DbalRecordExistsFilter.php
@@ -0,0 +1,43 @@
<?php

declare(strict_types = 1);

namespace Zfegg\ContentValidation\Opis\Filter;

use Opis\JsonSchema\Filter;
use Opis\JsonSchema\Schema;
use Opis\JsonSchema\ValidationContext;
use Psr\Container\ContainerInterface;

class DbalRecordExistsFilter implements Filter
{

private ContainerInterface $container;
private string $defaultId;

public function __construct(ContainerInterface $container, string $defaultId = 'db')
{
$this->container = $container;
$this->defaultId = $defaultId;
}

public function validate(ValidationContext $context, Schema $schema, array $args = []): bool
{
/** @var \Doctrine\DBAL\Connection $db */
$db = $this->container->get($args['db'] ?? $this->defaultId);

if (isset($args['sql'])) {
$sql = $args['sql'];
} elseif (isset($args['table']) && isset($args['field'])) {
$sql = sprintf('SELECT COUNT(*) FROM %s WHERE %s=?', $args['table'], $args['field']);
} else {
throw new \InvalidArgumentException('Invalid args.');
}

$exists = $args['exists'] ?? false;
$sth = $db->prepare($sql);
$row = $sth->executeQuery([$context->currentData()])->fetchNumeric();

return $row[0] == $exists;
}
}
1 change: 1 addition & 0 deletions src/Opis/Filter/RecordExistsFilter.php
Expand Up @@ -24,6 +24,7 @@ public function __construct(ContainerInterface $container, string $defaultId = '

public function validate(ValidationContext $context, Schema $schema, array $args = []): bool
{
/** @var \PDO $db */
$db = $this->container->get($args['db'] ?? $this->defaultId);

if (isset($args['sql'])) {
Expand Down
71 changes: 71 additions & 0 deletions test/Opis/Filter/DbalRecordExistsFilterTest.php
@@ -0,0 +1,71 @@
<?php

declare(strict_types = 1);

namespace ZfeggTest\ContentValidation\Opis\Filter;

use Doctrine\DBAL\DriverManager;
use Opis\JsonSchema\Schema;
use Opis\JsonSchema\SchemaLoader;
use Opis\JsonSchema\ValidationContext;
use Opis\JsonSchema\Validator;
use Zfegg\ContentValidation\Opis\Filter\DbalRecordExistsFilter;
use PHPUnit\Framework\TestCase;
use ZfeggTest\ContentValidation\SetupTrait;

class DbalRecordExistsFilterTest extends TestCase
{

use SetupTrait {
setUp as setUpContainer;
}

const SQL = <<<SQL
create table foo
(
id INTEGER not null primary key autoincrement,
key VARCHAR(32) not null,
value VARCHAR(32) not null
);
SQL;
private $db;

protected function setUp(): void
{
$this->setUpContainer();
$db = DriverManager::getConnection(['url' => 'sqlite:///:memory:']);
$db->executeStatement(self::SQL);
$db->executeStatement('INSERT INTO foo VALUES(NULL, "exists","123")');
$this->container->setService('db', $db->getWrappedConnection());
$this->container->setService('dbal', $db);
}

public function testValidate(): void
{
$filter = new DbalRecordExistsFilter($this->container, 'dbal');

$context = new ValidationContext('test', new SchemaLoader());
$schema = $this->createMock(Schema::class);
$rs = $filter->validate($context, $schema, ['sql' => 'SELECT count(*) FROM foo where key=?']);
$this->assertTrue($rs);

$rs = $filter->validate($context, $schema, ['sql' => 'SELECT count(*) FROM foo where key=?', 'exists' => true]);
$this->assertFalse($rs);

$rs = $filter->validate($context, $schema, ['table' => 'foo', 'field' => 'key']);
$this->assertTrue($rs);
}


public function testInValidator(): void
{
$validator = $this->container->get(Validator::class);
$data = <<<'JSON'
{"key": "exists"}
JSON;
$data = json_decode($data);
$result = $validator->validate($data, 'test:test/test-dbal-filter.json');

$this->assertTrue($result->isValid());
}
}
23 changes: 23 additions & 0 deletions test/test-dbal-filter.json
@@ -0,0 +1,23 @@
{
"type": "object",
"properties": {
"key": {
"type": "string",
"minLength": 1,
"maxLength": 64,
"$filters": [
{
"$func": "dbal-exists",
"$vars": {
"db": "dbal",
"table": "foo",
"field": "key",
"exists": true
}
}
]
}
},
"required": ["key"],
"additionalProperties": false
}

0 comments on commit f2dee6a

Please sign in to comment.