-
Notifications
You must be signed in to change notification settings - Fork 52
Feat: Custom document types #750
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughThis pull request introduces a custom document type mapping feature to the database layer. It allows developers to register custom Document subclasses for specific collections, enabling operations like create, get, and find to return instances of the mapped class instead of the base Document class. The feature includes public API methods for managing mappings and comprehensive test coverage. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Database
participant Mapping as Document Type<br/>Mapping
participant Doc as Document/<br/>Custom Class
User->>Database: setDocumentType('users', TestUser::class)
Database->>Mapping: Store mapping
User->>Database: createDocument('users', {email: 'test@example.com'})
Database->>Mapping: Check if 'users' mapped
Mapping-->>Database: Return TestUser::class
Database->>Doc: Instantiate TestUser
Doc-->>Database: TestUser instance
Database-->>User: Return TestUser instance
User->>Database: getDocument('users', docId)
Database->>Mapping: Check if 'users' mapped
Mapping-->>Database: Return TestUser::class
Database->>Doc: Instantiate TestUser from data
Doc-->>Database: TestUser instance
Database-->>User: Return TestUser instance
Note over User,Database: Unmapped collection<br/>falls back to Document
User->>Database: createDocument('posts', {title: 'Test'})
Database->>Mapping: Check if 'posts' mapped
Mapping-->>Database: No mapping found
Database->>Doc: Instantiate Document (default)
Doc-->>Database: Document instance
Database-->>User: Return Document instance
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (5)
src/Database/Database.php (4)
1195-1216: Harden registration: verify constructor compatibility of custom classGuard against subclasses that don’t accept the standard Document constructor. Add a reflection check (and keep throwing DatabaseException on failure).
public function setDocumentType(string $collection, string $className): static { if (!\class_exists($className)) { throw new DatabaseException("Class {$className} does not exist"); } if (!\is_subclass_of($className, Document::class)) { throw new DatabaseException("Class {$className} must extend " . Document::class); } + // Ensure constructor is compatible with Document::__construct(array $input = []) + try { + $rc = new \ReflectionClass($className); + $ctor = $rc->getConstructor(); + if ($ctor && $ctor->getNumberOfRequiredParameters() > 1) { + throw new DatabaseException("Class {$className} must have a compatible constructor (array \$input = [])."); + } + if ($ctor && $ctor->getNumberOfRequiredParameters() === 1) { + $p = $ctor->getParameters()[0]; + $hasArrayType = !$p->hasType() || (string)$p->getType() === 'array'; + if (!$hasArrayType) { + throw new DatabaseException("Class {$className} first constructor parameter must be of type array."); + } + } + } catch (\ReflectionException $e) { + throw new DatabaseException("Failed validating {$className} constructor: {$e->getMessage()}", 0, $e); + } + $this->documentTypes[$collection] = $className; return $this; }
1254-1266: Wrap instantiation errors with contextIf a custom class throws in its constructor, wrapping helps diagnostics.
- return new $className($data); + try { + return new $className($data); + } catch (\Throwable $e) { + throw new DatabaseException("Failed to instantiate {$className} for collection '{$collection}': {$e->getMessage()}", 0, $e); + }
3774-3782: Type mapping usage is correct; consider a tiny helper to DRYCurrent conversions are right (post-casting, pre-decode checks included). To avoid duplication, consider a small helper that maps if configured.
// Example: protected function mapIfConfigured(Document $collection, Document $doc): Document { return isset($this->documentTypes[$collection->getId()]) ? $this->createDocumentInstance($collection->getId(), $doc->getArrayCopy()) : $doc; }Then replace inline blocks with
$document = $this->mapIfConfigured($collection, $document);Also applies to: 3797-3807, 3815-3816
4550-4552: Ensure mapped types are returned from batch & upsert paths toocreateDocuments() and upsertDocumentsWithIncrease() currently emit base Document instances to callbacks; upsertDocument() may also return base instances when changes occur. Apply the same mapping before invoking callbacks/returning to keep behavior consistent across APIs.
*** In createDocuments(), inside: foreach ($batch as $document) { ... } before onNext *** - try { - $onNext && $onNext($document); + // Convert to custom document type if mapped + if (isset($this->documentTypes[$collection->getId()])) { + $document = $this->createDocumentInstance($collection->getId(), $document->getArrayCopy()); + } + try { + $onNext && $onNext($document); } catch (\Throwable $e) { *** In upsertDocumentsWithIncrease(), inside: foreach ($batch as $index => $doc) { ... } before onNext *** - $doc = $this->decode($collection, $doc); + $doc = $this->decode($collection, $doc); + if (isset($this->documentTypes[$collection->getId()])) { + $doc = $this->createDocumentInstance($collection->getId(), $doc->getArrayCopy()); + } *** Optionally, in upsertDocument(), after computing $result *** - return $result; + if (isset($this->documentTypes[$collection])) { + $result = $this->createDocumentInstance($collection, $result->getArrayCopy()); + } + return $result;Also consider mapping in increaseDocumentAttribute()/decreaseDocumentAttribute() before returning for full parity.
Also applies to: 5238-5242, 7254-7258
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php (1)
200-212: Drop unused locals in setup
$post1and$post2are never referenced after creation, so PHPMD flags them. You can safely remove the assignments (or assert on them) to keep the test lean and static-analysis clean.- $post1 = $database->createDocument('customPosts', new Document([ + $database->createDocument('customPosts', new Document([ '$id' => ID::unique(), 'title' => 'First Post', 'content' => 'This is the first post', '$permissions' => [Permission::read(Role::any())], ])); - $post2 = $database->createDocument('customPosts', new Document([ + $database->createDocument('customPosts', new Document([ '$id' => ID::unique(), 'title' => 'Second Post', 'content' => 'This is the second post', '$permissions' => [Permission::read(Role::any())], ]));
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
README.md(1 hunks)src/Database/Database.php(11 hunks)tests/e2e/Adapter/Base.php(2 hunks)tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-25T10:26:49.291Z
Learnt from: ArnabChatterjee20k
Repo: utopia-php/database PR: 715
File: tests/e2e/Adapter/Scopes/DocumentTests.php:6038-6066
Timestamp: 2025-09-25T10:26:49.291Z
Learning: Database::addFilter() is a static method that stores filters in a global static map (self::$filters). This means filters persist across all Database instances and test runs, potentially causing test interdependence issues when the same filter name is used by multiple tests.
Applied to files:
README.md
📚 Learning: 2025-10-03T02:04:17.803Z
Learnt from: abnegate
Repo: utopia-php/database PR: 721
File: tests/e2e/Adapter/Scopes/DocumentTests.php:6418-6439
Timestamp: 2025-10-03T02:04:17.803Z
Learning: In tests/e2e/Adapter/Scopes/DocumentTests::testSchemalessDocumentInvalidInteralAttributeValidation (PHP), when the adapter reports getSupportForAttributes() === false (schemaless), the test should not expect exceptions from createDocuments for “invalid” internal attributes; remove try/catch and ensure the test passes without exceptions, keeping at least one assertion.
Applied to files:
tests/e2e/Adapter/Base.phptests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php
🧬 Code graph analysis (2)
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php (1)
src/Database/Database.php (10)
Database(37-8604)setDocumentType(1203-1216)getDocumentType(1224-1227)clearDocumentType(1235-1240)clearAllDocumentTypes(1247-1252)create(1408-1427)createDocument(4455-4557)getDocument(3717-3846)find(7124-7269)updateDocument(5013-5246)
src/Database/Database.php (4)
src/Database/Document.php (5)
Document(12-470)getId(63-66)getRead(101-104)getArrayCopy(423-458)setAttribute(244-261)src/Database/Validator/Structure.php (1)
isValid(211-245)src/Database/Adapter/Mongo.php (1)
castingAfter(1194-1269)src/Database/Adapter.php (1)
castingAfter(1376-1376)
🪛 GitHub Actions: CodeQL
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php
[error] 76-76: Parameter #2 $className of method Utopia\Database\Database::setDocumentType() expects class-string<Utopia\Database\Document>, string given.
[error] 87-87: Parameter #2 $className of method Utopia\Database\Database::setDocumentType() expects class-string<Utopia\Database\Document>, string given.
src/Database/Database.php
[error] 3717-3717: Template type T of method Utopia\Database\Database::getDocument() is not referenced in a parameter.
[error] 4455-4455: Template type T of method Utopia\Database\Database::createDocument() is not referenced in a parameter.
[error] 5013-5013: Template type T of method Utopia\Database\Database::updateDocument() is not referenced in a parameter.
[error] 7124-7124: Template type T of method Utopia\Database\Database::find() is not referenced in a parameter.
🪛 PHPMD (2.15.0)
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php
200-200: Avoid unused local variables such as '$post1'. (undefined)
(UnusedLocalVariable)
207-207: Avoid unused local variables such as '$post2'. (undefined)
(UnusedLocalVariable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Setup & Build Docker Image
🔇 Additional comments (8)
tests/e2e/Adapter/Base.php (1)
8-8: LGTM! Test coverage extended correctly.The import and trait usage follow the established pattern in the file, correctly adding test coverage for the custom document types feature.
Also applies to: 26-26
README.md (3)
35-37: Clear and well-structured documentation.The section introduces the custom document types feature effectively, positioning it within a domain-driven design context.
39-63: Excellent code example demonstrating the feature.The example clearly illustrates the complete workflow: defining a custom document class, registering it, and using domain-specific methods. The code is syntactically correct and demonstrates proper usage patterns.
65-69: Well-articulated benefits.The benefits section effectively communicates the value proposition of custom document types, covering developer experience, code quality, and compatibility.
src/Database/Database.php (4)
417-422: LGTM: internal map for custom document typesProperty definition and typing look correct.
1218-1227: LGTM: accessor for mapped classSimple and correct.
1229-1240: LGTM: clear mapping for a collection
1242-1252: LGTM: clear all mappings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php (1)
73-82: Fix CodeQL type violation with dynamic dispatch.The string literal
'NonExistentClass'violates theclass-string<Document>parameter type insetDocumentType, causing a CodeQL pipeline failure. Use dynamic dispatch to test the runtime exception path while satisfying the static type checker.Apply this diff:
public function testSetDocumentTypeWithInvalidClass(): void { /** @var Database $database */ $database = static::getDatabase(); $this->expectException(DatabaseException::class); $this->expectExceptionMessage('does not exist'); - // @phpstan-ignore-next-line - Testing with invalid class name - $database->setDocumentType('users', 'NonExistentClass'); + /** @var mixed $invalidClass */ + $invalidClass = 'NonExistentClass'; + \call_user_func([$database, 'setDocumentType'], 'users', $invalidClass); }
🧹 Nitpick comments (8)
src/Database/Database.php (6)
1227-1249: Guard against incompatible constructors in custom Document subclassesIf a mapped subclass defines a non-compatible constructor,
new $className($data)will fatal. Add a reflection check to fail fast with a DatabaseException.public function setDocumentType(string $collection, string $className): static { if (!\class_exists($className)) { throw new DatabaseException("Class {$className} does not exist"); } if (!\is_subclass_of($className, Document::class)) { throw new DatabaseException("Class {$className} must extend " . Document::class); } + // Validate constructor compatibility: must accept array (or mixed) first param, or no required params + try { + $rc = new \ReflectionClass($className); + $ctor = $rc->getConstructor(); + if ($ctor && $ctor->getNumberOfRequiredParameters() > 0) { + $p = $ctor->getParameters()[0]; + $t = $p->getType(); + $acceptsArray = true; + if ($t instanceof \ReflectionNamedType) { + $acceptsArray = \in_array($t->getName(), ['array', 'mixed'], true); + } elseif ($t instanceof \ReflectionUnionType) { + $acceptsArray = \count(\array_filter($t->getTypes(), fn($n) => $n->getName()==='array'||$n->getName()==='mixed')) > 0; + } + if (!$acceptsArray) { + throw new DatabaseException("Custom Document constructor must accept an array (or mixed) as first parameter."); + } + } + } catch (\ReflectionException $e) { + throw new DatabaseException($e->getMessage(), $e->getCode(), $e); + } $this->documentTypes[$collection] = $className; return $this; }Optional: consider rejecting mapping for
_metadatato avoid surprising typing there.
1293-1299: Harden factory with error wrappingWrap instantiation to surface a DatabaseException instead of a fatal if subclass construction fails.
protected function createDocumentInstance(string $collection, array $data): Document { $className = $this->documentTypes[$collection] ?? Document::class; - return new $className($data); + try { + return new $className($data); + } catch (\Throwable $e) { + throw new DatabaseException('Failed to instantiate custom document: ' . $e->getMessage(), (int)$e->getCode(), $e); + } }
4688-4699: Surface mapped type to createDocuments onNext callbackonNext currently receives base Document. Convert to mapped type for consistency with single create/get.
foreach ($batch as $document) { $document = $this->adapter->castingAfter($collection, $document); $document = $this->casting($collection, $document); $document = $this->decode($collection, $document); + if (isset($this->documentTypes[$collection->getId()])) { + $document = $this->createDocumentInstance($collection->getId(), $document->getArrayCopy()); + } try { $onNext && $onNext($document); } catch (\Throwable $e) {
5492-5501: Also map types for updateDocuments callbacks (new and old)Ensure both
$docand$old[$index]passed to onNext are mapped when a type is registered.foreach ($batch as $index => $doc) { $doc = $this->adapter->castingAfter($collection, $doc); $doc->removeAttribute('$skipPermissionsUpdate'); $this->purgeCachedDocument($collection->getId(), $doc->getId()); $doc = $this->decode($collection, $doc); + if (isset($this->documentTypes[$collection->getId()])) { + $doc = $this->createDocumentInstance($collection->getId(), $doc->getArrayCopy()); + } try { - $onNext && $onNext($doc, $old[$index]); + $oldDoc = $old[$index]; + if (!$oldDoc->isEmpty()) { + $oldDoc = $this->adapter->castingAfter($collection, $oldDoc); + $oldDoc = $this->decode($collection, $oldDoc); + if (isset($this->documentTypes[$collection->getId()])) { + $oldDoc = $this->createDocumentInstance($collection->getId(), $oldDoc->getArrayCopy()); + } + } + $onNext && $onNext($doc, $oldDoc); } catch (Throwable $th) {
6235-6261: Map types for upsert onNext callbackReturn mapped instances to callbacks for parity with CRUD.
foreach ($batch as $index => $doc) { $doc = $this->adapter->castingAfter($collection, $doc); if (!$hasOperators) { $doc = $this->decode($collection, $doc); } + if (isset($this->documentTypes[$collection->getId()])) { + $doc = $this->createDocumentInstance($collection->getId(), $doc->getArrayCopy()); + } if ($this->getSharedTables() && $this->getTenantPerDocument()) { $this->withTenant($doc->getTenant(), function () use ($collection, $doc) { $this->purgeCachedDocument($collection->getId(), $doc->getId()); }); } else { $this->purgeCachedDocument($collection->getId(), $doc->getId()); } $old = $chunk[$index]->getOld(); if (!$old->isEmpty()) { $old = $this->adapter->castingAfter($collection, $old); $old = $this->decode($collection, $old); + if (isset($this->documentTypes[$collection->getId()])) { + $old = $this->createDocumentInstance($collection->getId(), $old->getArrayCopy()); + } } try { $onNext && $onNext($doc, $old->isEmpty() ? null : $old); } catch (\Throwable $th) {
6367-6370: Return mapped type from increase/decrease helpersThese helpers currently return base Document. Convert before returning for consistency.
$this->trigger(self::EVENT_DOCUMENT_INCREASE, $document); - return $document; + if (isset($this->documentTypes[$collection->getId()])) { + $document = $this->createDocumentInstance($collection->getId(), $document->getArrayCopy()); + } + return $document;$this->trigger(self::EVENT_DOCUMENT_DECREASE, $document); - return $document; + if (isset($this->documentTypes[$collection->getId()])) { + $document = $this->createDocumentInstance($collection->getId(), $document->getArrayCopy()); + } + return $document;Also applies to: 6467-6470
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php (2)
83-83: Fix formatting: add newline before function declaration.The closing brace from the previous function and this function declaration should be separated by a blank line for proper code formatting.
Apply this diff:
$database->setDocumentType('users', 'NonExistentClass'); - } public function testSetDocumentTypeWithNonDocumentClass(): void + } + + public function testSetDocumentTypeWithNonDocumentClass(): void
190-235: LGTM! Consider simplifying variable assignments.The test correctly validates find operations with custom document types. The static analysis warning about unused variables
$post1and$post2is a false positive—these calls are necessary to populate the database. However, if you prefer to silence the warning, you can remove the variable assignments since the return values aren't used.Optional simplification:
- $post1 = $database->createDocument('customPosts', new Document([ + $database->createDocument('customPosts', new Document([ '$id' => ID::unique(), 'title' => 'First Post', 'content' => 'This is the first post', '$permissions' => [Permission::read(Role::any())], ])); - $post2 = $database->createDocument('customPosts', new Document([ + $database->createDocument('customPosts', new Document([ '$id' => ID::unique(), 'title' => 'Second Post', 'content' => 'This is the second post', '$permissions' => [Permission::read(Role::any())], ]));
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
README.md(1 hunks)src/Database/Database.php(7 hunks)tests/e2e/Adapter/Base.php(2 hunks)tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-25T10:26:49.291Z
Learnt from: ArnabChatterjee20k
Repo: utopia-php/database PR: 715
File: tests/e2e/Adapter/Scopes/DocumentTests.php:6038-6066
Timestamp: 2025-09-25T10:26:49.291Z
Learning: Database::addFilter() is a static method that stores filters in a global static map (self::$filters). This means filters persist across all Database instances and test runs, potentially causing test interdependence issues when the same filter name is used by multiple tests.
Applied to files:
README.md
📚 Learning: 2025-10-03T02:04:17.803Z
Learnt from: abnegate
Repo: utopia-php/database PR: 721
File: tests/e2e/Adapter/Scopes/DocumentTests.php:6418-6439
Timestamp: 2025-10-03T02:04:17.803Z
Learning: In tests/e2e/Adapter/Scopes/DocumentTests::testSchemalessDocumentInvalidInteralAttributeValidation (PHP), when the adapter reports getSupportForAttributes() === false (schemaless), the test should not expect exceptions from createDocuments for “invalid” internal attributes; remove try/catch and ensure the test passes without exceptions, keeping at least one assertion.
Applied to files:
tests/e2e/Adapter/Base.phptests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php
📚 Learning: 2025-10-16T09:37:33.531Z
Learnt from: fogelito
Repo: utopia-php/database PR: 733
File: src/Database/Adapter/MariaDB.php:1801-1806
Timestamp: 2025-10-16T09:37:33.531Z
Learning: In the MariaDB adapter (src/Database/Adapter/MariaDB.php), only duplicate `_uid` violations should throw `DuplicateException`. All other unique constraint violations, including `PRIMARY` key collisions on the internal `_id` field, should throw `UniqueException`. This is the intended design to distinguish between user-facing document duplicates and internal/user-defined unique constraint violations.
Applied to files:
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php
🧬 Code graph analysis (2)
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php (1)
src/Database/Database.php (14)
Database(38-8626)setDocumentType(1235-1248)getDocumentType(1256-1259)clearDocumentType(1267-1272)clearAllDocumentTypes(1279-1284)createCollection(1527-1665)create(1440-1459)delete(1498-1512)createAttribute(1895-1954)createDocument(4485-4589)getDocument(3748-3877)deleteCollection(1826-1869)find(7153-7298)updateDocument(5045-5282)
src/Database/Database.php (5)
src/Database/Document.php (5)
Document(12-470)getId(63-66)getRead(101-104)getArrayCopy(423-458)setAttribute(244-261)src/Database/Validator/Structure.php (1)
isValid(211-245)src/Database/Validator/Authorization/Input.php (1)
Input(5-49)src/Database/Adapter/Mongo.php (1)
castingAfter(1194-1269)src/Database/Adapter.php (1)
castingAfter(1398-1398)
🪛 PHPMD (2.15.0)
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php
208-208: Avoid unused local variables such as '$post1'. (undefined)
(UnusedLocalVariable)
215-215: Avoid unused local variables such as '$post2'. (undefined)
(UnusedLocalVariable)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Adapter Tests (Schemaless/MongoDB)
- GitHub Check: Adapter Tests (SharedTables/SQLite)
- GitHub Check: Adapter Tests (SharedTables/Postgres)
- GitHub Check: Adapter Tests (SharedTables/MongoDB)
- GitHub Check: Adapter Tests (SharedTables/MariaDB)
- GitHub Check: Adapter Tests (SharedTables/MySQL)
- GitHub Check: Adapter Tests (Pool)
- GitHub Check: Adapter Tests (MongoDB)
- GitHub Check: Adapter Tests (SQLite)
- GitHub Check: Adapter Tests (Postgres)
- GitHub Check: Adapter Tests (MariaDB)
- GitHub Check: Adapter Tests (Mirror)
- GitHub Check: Adapter Tests (MySQL)
- GitHub Check: Unit Test
🔇 Additional comments (14)
tests/e2e/Adapter/Base.php (1)
8-8: LGTM! Clean integration of the new test trait.The addition of the
CustomDocumentTypeTeststrait follows the established pattern in this test base class, maintaining consistency with other test scopes.Also applies to: 26-26
README.md (1)
35-70: Excellent documentation of the new feature!The Custom Document Types section is well-written with:
- Clear explanation of the feature and its purpose
- Practical, easy-to-follow example code
- Well-articulated benefits that highlight the value proposition
- Clean formatting consistent with the rest of the README
The documentation effectively communicates both the "how" and the "why" of this feature.
src/Database/Database.php (6)
424-429: LGTM: mapping registry addedThe dedicated per-collection type map is clear and scoped to the Database instance.
1250-1285: LGTM: simple, chainable management APIGet/clear/clearAll are straightforward and fluent.
3804-3813: Consistent typing applied in getDocument paths (cache, empty, post-cast, unauthorized)Good use of the factory in all branches; ordering preserves decode/cast semantics and sets $collection after conversion.
Also applies to: 3828-3830, 3834-3838, 3846-3847
4581-4585: Create returns mapped typeReturning the mapped class after decode is correct and aligns with getDocument/find behavior.
5274-5278: Update returns mapped typePost-decode conversion keeps return types consistent across CRUD.
7283-7287: Find yields mapped instancesPer-item conversion after decode is correct; preserves selection filtering.
tests/e2e/Adapter/Scopes/CustomDocumentTypeTests.php (6)
13-43: LGTM! Clean test document classes.The
TestUserandTestPostclasses provide appropriate typed accessors for testing the custom document type feature. The implementations are straightforward and suitable for end-to-end testing.
47-61: LGTM! Proper cleanup included.The test correctly verifies setting and retrieving document types, and includes cleanup to prevent test pollution.
124-142: LGTM! Fluent interface tested with cleanup.The test properly validates method chaining and includes cleanup to prevent test pollution.
144-188: LGTM! Comprehensive integration test.This test thoroughly validates the custom document type feature across create and get operations, verifying both type casting and custom method functionality. Proper cleanup is included.
237-284: LGTM! Update operation thoroughly tested.The test validates that custom document types are preserved through update operations, including verification of custom methods with updated values. Proper cleanup is included.
286-312: LGTM! Default behavior validated.This test correctly verifies that collections without custom type mappings continue to return standard
Documentinstances, ensuring backward compatibility.
|
closed in favor of |
setDocumentTypeSummary by CodeRabbit
New Features
Documentation