diff --git a/src/Relation/BelongsTo.php b/src/Relation/BelongsTo.php index fdad6bf35..293b122f1 100644 --- a/src/Relation/BelongsTo.php +++ b/src/Relation/BelongsTo.php @@ -91,6 +91,10 @@ public function queue(Pool $pool, Tuple $tuple): void if ($related instanceof ReferenceInterface && $related->hasValue()) { $related = $related->getValue(); $state->setRelation($relName, $related); + if ($related === null) { + $state->setRelationStatus($this->getName(), RelationInterface::STATUS_RESOLVED); + return; + } } if ($related === null) { $this->setNullFromRelated($tuple, false); diff --git a/src/Relation/Morphed/BelongsToMorphed.php b/src/Relation/Morphed/BelongsToMorphed.php index 96cf6aeb7..4f796a244 100644 --- a/src/Relation/Morphed/BelongsToMorphed.php +++ b/src/Relation/Morphed/BelongsToMorphed.php @@ -32,10 +32,8 @@ public function initReference(Node $node): ReferenceInterface { $scope = $this->getReferenceScope($node); $nodeData = $node->getData(); - if ($scope === null || !isset($nodeData[$this->morphKey])) { - $result = new Reference($node->getRole(), []); - $result->setValue(null); - return $result; + if (!isset($nodeData[$this->morphKey], $scope)) { + return new EmptyReference('?', null); } // $scope[$this->morphKey] = $nodeData[$this->morphKey]; $target = $nodeData[$this->morphKey]; @@ -48,7 +46,7 @@ public function prepare(Pool $pool, Tuple $tuple, mixed $related, bool $load = t parent::prepare($pool, $tuple, $related, $load); $related = $tuple->state->getRelation($this->getName()); - if ($related === null) { + if ($related === null || $related instanceof EmptyReference) { return; } @@ -70,8 +68,11 @@ protected function assertValid(Node $related): void protected function setNullFromRelated(Tuple $tuple, bool $isPreparing): void { - // Set morph key to null - $tuple->state->register($this->morphKey, null); + if ($tuple->node->getRelation($this->getName()) !== null) { + // Set morph key to null if the relation was changed to null + $tuple->state->register($this->morphKey, null); + } + parent::setNullFromRelated($tuple, $isPreparing); } } diff --git a/src/Select.php b/src/Select.php index 4aaabb15d..86d506201 100644 --- a/src/Select.php +++ b/src/Select.php @@ -210,7 +210,7 @@ public function offset(int $offset): self * Available DTO classes (one per relation type): * - {@see \Cycle\ORM\Select\Options\HasOneLoadOptions} * - {@see \Cycle\ORM\Select\Options\HasManyLoadOptions} - * - {@see \Cycle\ORM\Select\Options\BelongsToLoadOptions} + * - {@see \Cycle\ORM\Select\Options\BelongsToLoadOptions} applicable for both Belongs To and Refers To relations * - {@see \Cycle\ORM\Select\Options\ManyToManyLoadOptions} * - {@see \Cycle\ORM\Select\Options\MorphedHasOneLoadOptions} * - {@see \Cycle\ORM\Select\Options\MorphedHasManyLoadOptions} diff --git a/tests/ORM/Fixtures/Image.php b/tests/ORM/Fixtures/Image.php index fc168bca8..93b844ecb 100644 --- a/tests/ORM/Fixtures/Image.php +++ b/tests/ORM/Fixtures/Image.php @@ -8,6 +8,7 @@ class Image { public $id; + public $parentType; public $parent; public $url; } diff --git a/tests/ORM/Functional/Driver/Common/Relation/Morphed/BelongsToMorphedRelationTest.php b/tests/ORM/Functional/Driver/Common/Relation/Morphed/BelongsToMorphedRelationTest.php index c0c731f50..de5f26f22 100644 --- a/tests/ORM/Functional/Driver/Common/Relation/Morphed/BelongsToMorphedRelationTest.php +++ b/tests/ORM/Functional/Driver/Common/Relation/Morphed/BelongsToMorphedRelationTest.php @@ -289,6 +289,7 @@ public function testSetNullShouldClearMorphType(): void $row = $this->getDatabase()->table('image')->select()->where('id', 1)->fetchAll(); $this->assertSame('user', $row[0]['parent_type']); $this->assertNotNull($row[0]['parent_id']); + $this->save($c); $c->parent = null; $this->save($c); @@ -318,6 +319,43 @@ public function testCreateWithoutParentShouldNotSetMorphType(): void $this->assertNull($row[0]['parent_type'], 'parent_type should be NULL for entity without parent'); } + public function testUsingFactoryCreateWithoutRelatedButSetMorphTypeManually(): void + { + $schemaArray = $this->getNullableMorphedSchemaArray(); + $this->orm = $this->orm->with( + schema: new Schema($schemaArray), + ); + + /** @var Image $c */ + $c = $this->orm->make(Image::class); + $c->url = 'no-parent.png'; + $c->parentType = 'user'; + + $this->save($c); + + $row = $this->getDatabase()->table('image')->select()->where('id', $c->id)->fetchAll(); + $this->assertNull($row[0]['parent_id'], 'parent_id should be NULL for entity without parent'); + $this->assertSame('user', $row[0]['parent_type'], 'parent_type should be NULL for entity without parent'); + } + + public function testCreateWithoutRelatedButSetMorphTypeManually(): void + { + $schemaArray = $this->getNullableMorphedSchemaArray(); + $this->orm = $this->orm->with( + schema: new Schema($schemaArray), + ); + + $c = new Image(); + $c->url = 'no-parent.png'; + $c->parentType = 'user'; + + $this->save($c); + + $row = $this->getDatabase()->table('image')->select()->where('id', $c->id)->fetchAll(); + $this->assertNull($row[0]['parent_id'], 'parent_id should be NULL for entity without parent'); + $this->assertSame('user', $row[0]['parent_type'], 'parent_type should be NULL for entity without parent'); + } + public function testUpdateRelation(): void { $this->captureReadQueries();