From 23178debab2f84ce4edd427f332c142a94afad34 Mon Sep 17 00:00:00 2001 From: Jake Barnby Date: Fri, 11 Sep 2026 17:35:29 +1200 Subject: [PATCH] fix(attributes): attach the auto filter in the batch creation path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit createAttributes() is the third public path that creates attributes, and it was the only one that never attached the type-named filter listed in Database::ATTRIBUTE_FILTER_TYPES. createAttribute() and createCollection() both attach it before validating. The attribute validator requires that filter to be present, so a datetime, point, linestring, polygon, vector or object attribute created through the batch path was rejected outright with "Attribute of type: datetime requires the following filters: datetime". Had validation let it through, the value would have been stored without the encode/decode the other two paths apply — a datetime written as +05:00 would never have been normalised to +00:00. Co-Authored-By: Claude Opus 5 --- src/Database/Database.php | 6 ++-- src/Database/Traits/Attributes.php | 6 ++++ tests/e2e/Adapter/Scopes/AttributeTests.php | 35 +++++++++++++++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/Database/Database.php b/src/Database/Database.php index 5da5573d9..dcffff33e 100644 --- a/src/Database/Database.php +++ b/src/Database/Database.php @@ -92,9 +92,9 @@ class Database public const DELETE_BATCH_SIZE = 1_000; /** - * Attribute types whose stored value is produced by a filter of the same name. Both public - * creation paths add it, so an attribute made through createCollection() encodes and decodes - * the same way as the identical one made through createAttribute(). + * Attribute types whose stored value is produced by a filter of the same name. Every public + * creation path adds it, so an attribute made through createCollection(), createAttribute() + * or createAttributes() encodes and decodes the same way as any identical one. * * @var list */ diff --git a/src/Database/Traits/Attributes.php b/src/Database/Traits/Attributes.php index 2ea245f63..a4b719bee 100644 --- a/src/Database/Traits/Attributes.php +++ b/src/Database/Traits/Attributes.php @@ -227,6 +227,12 @@ public function createAttributes(string $collection, array $attributes): bool throw new DatabaseException('Missing attribute key'); } + if (in_array($attribute->type, Database::ATTRIBUTE_FILTER_TYPES, true)) { + $attribute->filters = array_values( + array_unique(array_merge($attribute->filters, [$attribute->type->value])) + ); + } + $existsInSchema = false; try { diff --git a/tests/e2e/Adapter/Scopes/AttributeTests.php b/tests/e2e/Adapter/Scopes/AttributeTests.php index e8692aded..b4c66bec0 100644 --- a/tests/e2e/Adapter/Scopes/AttributeTests.php +++ b/tests/e2e/Adapter/Scopes/AttributeTests.php @@ -1935,6 +1935,41 @@ public function testCreateDatetimeAddingAutoFilter(): void $database->deleteCollection($collection); } + public function testCreateAttributesAddingAutoFilter(): void + { + /** @var Database $database */ + $database = $this->getDatabase(); + + if (! $database->getAdapter()->supports(Capability::BatchCreateAttributes)) { + $this->markTestSkipped('Adapter does not support batch attribute creation'); + } + + $collection = 'datetime_batch_auto_filter'; + + $database->createCollection(new Collection( + id: $collection, + permissions: [ + Permission::create(Role::any()), + Permission::read(Role::any()), + ], + documentSecurity: false, + )); + + $database->createAttributes($collection, [Attribute::datetime(key: 'batch')]); + + $database->createDocument($collection, new Document([ + Document::ID => 'offset', + 'batch' => '2024-01-02T03:04:05.000+05:00', + ])); + + $this->assertSame( + '2024-01-01T22:04:05.000+00:00', + $database->getDocument($collection, 'offset')->getAttribute('batch') + ); + + $database->deleteCollection($collection); + } + public function testCreateAttributesBigIntIgnoresSizeMetadata(): void { /** @var Database $database */