Skip to content

Commit

Permalink
Fix non-scalar value to be implied from definitive condition
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Feb 9, 2023
1 parent fdca788 commit 0b9d78a
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 8 deletions.
4 changes: 1 addition & 3 deletions src/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@ public function useAlias(): bool
public function getQueryArguments($operator, $value): array
{
$typecastField = $this;
$allowArray = true;
if (in_array($operator, [
Scope\Condition::OPERATOR_LIKE,
Scope\Condition::OPERATOR_NOT_LIKE,
Expand All @@ -421,12 +420,11 @@ public function getQueryArguments($operator, $value): array
$typecastField = new self(['type' => 'string']);
$typecastField->setOwner(new Model($this->getOwner()->getPersistence(), ['table' => false]));
$typecastField->shortName = $this->shortName;
$allowArray = false;
}

if ($value instanceof Persistence\Array_\Action) { // needed to pass hintable tests
$v = $value;
} elseif (is_array($value) && $allowArray) {
} elseif (is_array($value) && in_array($operator, [Scope\Condition::OPERATOR_IN, Scope\Condition::OPERATOR_NOT_IN], true)) {
$v = array_map(fn ($value) => $typecastField->typecastSaveField($value), $value);
} else {
$v = $typecastField->typecastSaveField($value);
Expand Down
12 changes: 7 additions & 5 deletions src/Model/Scope/Condition.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Atk4\Data\Exception;
use Atk4\Data\Field;
use Atk4\Data\Model;
use Atk4\Data\Persistence;
use Atk4\Data\Persistence\Sql\Expression;
use Atk4\Data\Persistence\Sql\Expressionable;
use Atk4\Data\Persistence\Sql\Sqlite\Expression as SqliteExpression;
Expand Down Expand Up @@ -145,11 +146,10 @@ protected function onChangeModel(): void
{
$model = $this->getModel();
if ($model !== null) {
// if we have a definitive scalar value for a field
// sets it as default value for field and locks it
// if we have a definitive equal condition set the value as default value for field
// new records will automatically get this value assigned for the field
// @todo: consider this when condition is part of OR scope
if ($this->operator === self::OPERATOR_EQUALS && !is_object($this->value) && !is_array($this->value)) {
// TODO: consider this when condition is part of OR scope
if ($this->operator === self::OPERATOR_EQUALS && !$this->value instanceof Expressionable) {
// key containing '/' means chained references and it is handled in toQueryArguments method
$field = $this->key;
if (is_string($field) && !str_contains($field, '/')) {
Expand All @@ -161,7 +161,9 @@ protected function onChangeModel(): void
// for now, do not set default at least for PK/ID
if ($field instanceof Field && $field->shortName !== $field->getOwner()->idField) {
$field->system = true;
$field->default = $this->value;
$fakePersistence = new Persistence\Array_();
$valueCloned = $fakePersistence->typecastLoadField($field, $fakePersistence->typecastSaveField($field, $this->value));
$field->default = $valueCloned;
}
}
}
Expand Down
123 changes: 123 additions & 0 deletions tests/ReferenceSqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
use Atk4\Data\Exception;
use Atk4\Data\Model;
use Atk4\Data\Schema\TestCase;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Types as DbalTypes;

/**
* Tests that condition is applied when traversing hasMany
Expand Down Expand Up @@ -280,6 +282,127 @@ public function testRelatedExpression(): void
);
}

public function testReferenceWithObjectId(): void
{
$this->setDb([
'file' => [
1 => ['id' => 1, 'name' => 'a.txt', 'parentDirectoryId' => null],
['id' => 2, 'name' => 'u', 'parentDirectoryId' => null],
['id' => 3, 'name' => 'v', 'parentDirectoryId' => 2],
['id' => 4, 'name' => 'w', 'parentDirectoryId' => 2],
['id' => 5, 'name' => 'b.txt', 'parentDirectoryId' => 2],
['id' => 6, 'name' => 'c.txt', 'parentDirectoryId' => 3],
['id' => 7, 'name' => 'd.txt', 'parentDirectoryId' => 2],
['id' => 8, 'name' => 'e.txt', 'parentDirectoryId' => 4],
],
]);

$integerWrappedType = new class() extends DbalTypes\Type {
public function getName(): string
{
return self::class;
}

public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
{
return DbalTypes\Type::getType(DbalTypes\Types::INTEGER)->getSQLDeclaration($fieldDeclaration, $platform);
}

public function convertToDatabaseValue($value, AbstractPlatform $platform): ?int
{
if ($value === null) {
return null;
}

return DbalTypes\Type::getType('integer')->convertToDatabaseValue($value->getId(), $platform);
}

public function convertToPHPValue($value, AbstractPlatform $platform): ?object
{
if ($value === null) {
return null;
}

return new class(DbalTypes\Type::getType('integer')->convertToPHPValue($value, $platform)) {
private int $id;

public function __construct(int $id)
{
$this->id = $id;
}

public function getId(): int
{
return $this->id;
}
};
}
};

DbalTypes\Type::addType($integerWrappedType->getName(), get_class($integerWrappedType));
try {
$file = new Model($this->db, ['table' => 'file']);
$file->getField('id')->type = $integerWrappedType->getName();
$file->addField('name');
$file->hasOne('parentDirectory', [
'model' => $file,
'type' => $integerWrappedType->getName(),
'ourField' => 'parentDirectoryId',
]);
$file->hasMany('childFiles', [
'model' => $file,
'theirField' => 'parentDirectoryId'
]);

$fileEntity = $file->loadBy('name', 'v')->ref('childFiles')->createEntity();
$fileEntity->save(['name' => 'x']);

$fileEntity = $fileEntity->ref('childFiles')->createEntity();
$fileEntity->save(['name' => 'y.txt']);

$createWrappedIntegerFx = function (int $v) use ($integerWrappedType): object {
return $integerWrappedType->convertToPHPValue($v, $this->getDatabasePlatform());
};

static::{'assertEquals'}([
['id' => $createWrappedIntegerFx(10), 'name' => 'y.txt', 'parentDirectoryId' => $createWrappedIntegerFx(9)],
], $fileEntity->getModel()->export());
static::assertSame([], $fileEntity->ref('childFiles')->export());

$fileEntity = $fileEntity->ref('parentDirectory');
static::{'assertEquals'}([
['id' => $createWrappedIntegerFx(9), 'name' => 'x', 'parentDirectoryId' => $createWrappedIntegerFx(3)],
], $fileEntity->getModel()->export());
static::{'assertEquals'}([
['id' => $createWrappedIntegerFx(10), 'name' => 'y.txt', 'parentDirectoryId' => $createWrappedIntegerFx(9)],
], $fileEntity->ref('childFiles')->export());

$fileEntity = $fileEntity->ref('parentDirectory');
static::{'assertEquals'}([
['id' => $createWrappedIntegerFx(3), 'name' => 'v', 'parentDirectoryId' => $createWrappedIntegerFx(2)],
], $fileEntity->getModel()->export());
static::{'assertEquals'}([
['id' => $createWrappedIntegerFx(6), 'name' => 'c.txt', 'parentDirectoryId' => $createWrappedIntegerFx(3)],
['id' => $createWrappedIntegerFx(9), 'name' => 'x', 'parentDirectoryId' => $createWrappedIntegerFx(3)],
], $fileEntity->ref('childFiles')->export());
static::{'assertEquals'}([
['id' => $createWrappedIntegerFx(6), 'name' => 'c.txt', 'parentDirectoryId' => $createWrappedIntegerFx(3)],
['id' => $createWrappedIntegerFx(8), 'name' => 'e.txt', 'parentDirectoryId' => $createWrappedIntegerFx(4)],
['id' => $createWrappedIntegerFx(9), 'name' => 'x', 'parentDirectoryId' => $createWrappedIntegerFx(3)],
], $fileEntity->ref('parentDirectory')->ref('childFiles')->ref('childFiles')->export());

$fileEntity = $fileEntity->ref('parentDirectory');
static::{'assertEquals'}([
['id' => $createWrappedIntegerFx(2), 'name' => 'u', 'parentDirectoryId' => null],
], $fileEntity->getModel()->export());
} finally {
\Closure::bind(function () use ($integerWrappedType) {
$dbalTypeRegistry = DbalTypes\Type::getTypeRegistry();
unset($dbalTypeRegistry->instances[$integerWrappedType->getName()]);
}, null, DbalTypes\TypeRegistry::class)();
}
}

public function testAggregateHasMany(): void
{
$vat = 0.23;
Expand Down

0 comments on commit 0b9d78a

Please sign in to comment.