Skip to content

Commit f465f2f

Browse files
committed
wip
1 parent 11c8075 commit f465f2f

File tree

8 files changed

+80
-33
lines changed

8 files changed

+80
-33
lines changed

src/Factory.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ protected function normalizeParameter(string $field, mixed $value): mixed
235235
);
236236
}
237237

238-
return \is_object($value) ? $this->normalizeObject($value) : $value;
238+
return \is_object($value) ? $this->normalizeObject($field, $value) : $value;
239239
}
240240

241241
/**
@@ -253,7 +253,7 @@ protected function normalizeCollection(string $field, FactoryCollection $collect
253253
/**
254254
* @internal
255255
*/
256-
protected function normalizeObject(object $object): object
256+
protected function normalizeObject(string $field, object $object): object
257257
{
258258
return $object;
259259
}

src/ORM/OrmV2PersistenceStrategy.php

+6-10
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,13 @@ public function inversedRelationshipMetadata(string $parent, string $child, stri
4343
throw new \LogicException("Cannot find correct association named \"{$field}\" between classes [parent: \"{$parent}\", child: \"{$child}\"]");
4444
}
4545

46-
// exclude "owning" side of the association (owning OneToOne or ManyToOne)
47-
if (!\in_array(
48-
$inversedAssociation['type'],
49-
[ClassMetadataInfo::ONE_TO_MANY, ClassMetadataInfo::ONE_TO_ONE],
50-
true
51-
)
52-
|| !isset($inversedAssociation['mappedBy'])
53-
) {
46+
$inverseField = $inversedAssociation['isOwningSide'] ? $inversedAssociation['inversedBy'] ?? null : $inversedAssociation['mappedBy'] ?? null;
47+
48+
if (null === $inverseField) {
5449
return null;
5550
}
5651

57-
$association = $metadata->getAssociationMapping($inversedAssociation['mappedBy']);
52+
$association = $metadata->getAssociationMapping($inverseField);
5853

5954
// only keep *ToOne associations
6055
if (!$metadata->isSingleValuedAssociation($association['fieldName'])) {
@@ -66,7 +61,8 @@ public function inversedRelationshipMetadata(string $parent, string $child, stri
6661
return new InverseRelationshipMetadata(
6762
inverseField: $association['fieldName'],
6863
isCollection: $inversedAssociationMetadata->isCollectionValuedAssociation($inversedAssociation['fieldName']),
69-
collectionIndexedBy: $inversedAssociation['indexBy'] ?? null
64+
collectionIndexedBy: ($inversedAssociationMetadata->isCollectionValuedAssociation($inversedAssociation['fieldName']) && isset($inversedAssociation['indexBy'])) ? $inversedAssociation['indexBy'] : null,
65+
isOwningSide: !$inversedAssociation['isOwningSide']
7066
);
7167
}
7268

src/ORM/OrmV3PersistenceStrategy.php

+6-4
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,13 @@ public function inversedRelationshipMetadata(string $parent, string $child, stri
4141
throw new \LogicException("Cannot find correct association named \"{$field}\" between classes [parent: \"{$parent}\", child: \"{$child}\"]");
4242
}
4343

44-
// exclude "owning" side of the association (owning OneToOne or ManyToOne)
45-
if (!$inversedAssociation instanceof InverseSideMapping) {
44+
$inverseField = $inversedAssociation->isOwningSide() ? $inversedAssociation->inversedBy : $inversedAssociation->mappedBy;
45+
46+
if (null === $inverseField) {
4647
return null;
4748
}
4849

49-
$association = $metadata->getAssociationMapping($inversedAssociation->mappedBy);
50+
$association = $metadata->getAssociationMapping($inverseField);
5051

5152
// only keep *ToOne associations
5253
if (!$metadata->isSingleValuedAssociation($association->fieldName)) {
@@ -56,7 +57,8 @@ public function inversedRelationshipMetadata(string $parent, string $child, stri
5657
return new InverseRelationshipMetadata(
5758
inverseField: $association->fieldName,
5859
isCollection: $inversedAssociation instanceof ToManyAssociationMapping,
59-
collectionIndexedBy: $inversedAssociation->isIndexed() ? $inversedAssociation->indexBy() : null
60+
collectionIndexedBy: $inversedAssociation instanceof ToManyAssociationMapping && $inversedAssociation->isIndexed() ? $inversedAssociation->indexBy() : null,
61+
isOwningSide: !$inversedAssociation->isOwningSide()
6062
);
6163
}
6264

src/Persistence/InverseRelationshipMetadata.php

+6
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ public function __construct(
2222
public readonly string $inverseField,
2323
public readonly bool $isCollection,
2424
public readonly ?string $collectionIndexedBy,
25+
public readonly bool $isOwningSide,
2526
) {
2627
}
28+
29+
public function isInverseOneToOne(): bool
30+
{
31+
return !$this->isCollection && $this->isOwningSide;
32+
}
2733
}

src/Persistence/PersistentObjectFactory.php

+25-12
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ protected function normalizeParameter(string $field, mixed $value): mixed
302302
$inversedRelationshipMetadata = $pm->inverseRelationshipMetadata(static::class(), $value::class(), $field);
303303

304304
// handle inversed OneToOne
305-
if ($inversedRelationshipMetadata && !$inversedRelationshipMetadata->isCollection) {
305+
if ($inversedRelationshipMetadata?->isInverseOneToOne()) {
306306
$inverseField = $inversedRelationshipMetadata->inverseField;
307307

308308
// we need to handle the circular dependency involved by inversed one-to-one relationship:
@@ -340,7 +340,7 @@ protected function normalizeCollection(string $field, FactoryCollection $collect
340340

341341
$inverseRelationshipMetadata = $pm->inverseRelationshipMetadata(static::class(), $collection->factory::class(), $field);
342342

343-
if ($inverseRelationshipMetadata && $inverseRelationshipMetadata->isCollection) {
343+
if ($inverseRelationshipMetadata?->isCollection) {
344344
$this->tempAfterInstantiate[] = function(object $object) use ($collection, $inverseRelationshipMetadata, $field) {
345345
$inverseField = $inverseRelationshipMetadata->inverseField;
346346

@@ -374,24 +374,37 @@ protected function normalizeCollection(string $field, FactoryCollection $collect
374374
*
375375
* @internal
376376
*/
377-
protected function normalizeObject(object $object): object
377+
protected function normalizeObject(string $field, object $object): object
378378
{
379379
$configuration = Configuration::instance();
380380

381-
if (
382-
!$this->isPersisting()
383-
|| !$configuration->isPersistenceAvailable()
384-
) {
381+
if (!$configuration->isPersistenceAvailable()) {
385382
return $object;
386383
}
387384

388385
$object = unproxy($object, withAutoRefresh: false);
389386

390387
$persistenceManager = $configuration->persistence();
388+
391389
if (!$persistenceManager->hasPersistenceFor($object)) {
392390
return $object;
393391
}
394392

393+
$inverseRelationship = $persistenceManager->inverseRelationshipMetadata(static::class(), $object::class, $field);
394+
395+
if ($inverseRelationship && !$inverseRelationship->isCollection) {
396+
$this->tempAfterInstantiate[] = static function(object $newObject) use ($object, $inverseRelationship) {
397+
set($object, $inverseRelationship->inverseField, $newObject);
398+
};
399+
}
400+
401+
402+
if (
403+
!$this->isPersisting()
404+
) {
405+
return $object;
406+
}
407+
395408
if (!$persistenceManager->isPersisted($object)) {
396409
$persistenceManager->scheduleForInsert($object);
397410

@@ -439,11 +452,11 @@ static function(object $object, array $parameters, PersistentObjectFactory $fact
439452
Configuration::instance()->persistence()->scheduleForInsert($object, $afterPersistCallbacks);
440453
}
441454
)
442-
// ->afterPersist(
443-
// static function(object $object): void {
444-
// Configuration::instance()->persistence()->refresh($object);
445-
// }
446-
// )
455+
->afterPersist(
456+
static function(object $object): void {
457+
Configuration::instance()->persistence()->refresh($object);
458+
}
459+
)
447460
;
448461
}
449462

tests/Fixture/Entity/EdgeCases/InversedOneToOneWithNonNullableOwning/InverseSide.php

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ public function getOwningSide(): OwningSide
3636

3737
public function setOwningSide(OwningSide $owningSide): void
3838
{
39-
dump(__METHOD__.'()::'.__LINE__);
4039
$this->owningSide = $owningSide;
4140
$owningSide->inverseSide = $this;
4241
}

tests/Integration/ORM/EdgeCasesRelationshipTest.php

+26-1
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ static function (InversedOneToOneWithNonNullableOwning\OwningSide $o) use ($inve
231231
$inverseSideFactory::assert()->count(1);
232232

233233
self::assertNotNull($owningSide->inverseSide);
234-
self::assertSame($owningSide, $owningSide->inverseSide->owningSide);
234+
self::assertSame($owningSide, $owningSide->inverseSide->getOwningSide());
235235
}
236236

237237
/** @test */
@@ -254,6 +254,31 @@ public function object_with_union_type(): void
254254
self::assertInstanceOf(Collection::class, $object->collection);
255255
}
256256

257+
/** @test */
258+
#[Test]
259+
#[DataProvider('provideCascadeRelationshipsCombinations')]
260+
#[UsingRelationships(InversedOneToOneWithNonNullableOwning\OwningSide::class, ['inverseSide'])]
261+
#[RequiresPhpunit('>=11.4')]
262+
public function can_create_inverse_one_to_one_with_actual(): void
263+
{
264+
$owningSideFactory = persistent_factory(InversedOneToOneWithNonNullableOwning\OwningSide::class);
265+
$inverseSideFactory = persistent_factory(InversedOneToOneWithNonNullableOwning\InverseSide::class);
266+
267+
$owningSide = $owningSideFactory
268+
->afterInstantiate(
269+
static function (InversedOneToOneWithNonNullableOwning\OwningSide $o) use ($inverseSideFactory): void {
270+
$inverseSideFactory->create(['owningSide' => $o]);
271+
}
272+
)
273+
->create();
274+
275+
$owningSideFactory::assert()->count(1);
276+
$inverseSideFactory::assert()->count(1);
277+
278+
self::assertNotNull($owningSide->inverseSide);
279+
self::assertSame($owningSide, $owningSide->inverseSide->getOwningSide());
280+
}
281+
257282
/**
258283
* @test
259284
*/

tests/Integration/ORM/EntityRelationship/EntityFactoryRelationshipTestCase.php

+9-3
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,9 @@ public function after_instantiate_flushing_using_current_object_in_relationship_
613613
{
614614
$category = static::categoryFactory()
615615
->afterInstantiate(
616-
static fn(Category $c) => static::contactFactory()->create(['category' => $c])
616+
static function (Category $c): void {
617+
static::contactFactory()->create(['category' => $c]);
618+
}
617619
)
618620
->create();
619621

@@ -632,7 +634,9 @@ public function after_instantiate_flushing_using_current_object_in_relationship_
632634
{
633635
$contact = static::contactFactory()
634636
->afterInstantiate(
635-
static fn(Contact $c) => static::categoryFactory()->create(['contacts' => [$c]])
637+
static function (Contact $c): void {
638+
static::categoryFactory()->create(['contacts' => [$c]]);
639+
}
636640
)
637641
->create(['category' => null]);
638642

@@ -651,7 +655,9 @@ public function after_instantiate_flushing_using_current_object_in_relationship_
651655
{
652656
$address = static::addressFactory()
653657
->afterInstantiate(
654-
static fn(Address $a) => static::contactFactory()->create(['address' => $a])
658+
static function (Address $a): void {
659+
static::contactFactory()->create(['address' => $a]);
660+
}
655661
)->create();
656662

657663
static::contactFactory()::assert()->count(1);

0 commit comments

Comments
 (0)