Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/Util/ClassSourceManipulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -531,12 +531,14 @@ private function addSingularRelation(BaseRelation $relation): void
$setterNodeBuilder = $this->createSetterNodeBuilder(
$relation->getPropertyName(),
$typeHint,
// make the type-hint nullable always for ManyToOne to allow the owning
// side to be set to null, which is needed for orphanRemoval
// (specifically: when you set the inverse side, the generated
// code will *also* set the owning side to null - so it needs to be allowed)
// e.g. $userAvatarPhoto->setUser(null);
$relation instanceof RelationOneToOne ? $relation->isNullable() : true
// A ManyToOne setter must stay nullable as soon as the relation maps an
// inverse side: the collection remove*() generated on that side sets the
// owning side back to null (orphanRemoval), e.g. $userAvatarPhoto->setUser(null);
// so null must be allowed there. A non-nullable, *unidirectional* ManyToOne
// has no such code path, so its setter can use a strict, non-nullable type-hint.
$relation instanceof RelationOneToOne
? $relation->isNullable()
: ($relation->isNullable() || $relation->getMapInverseRelation())
);

// set the *owning* side of the relation
Expand Down
14 changes: 14 additions & 0 deletions tests/Util/ClassSourceManipulatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,20 @@ public static function getAddManyToOneRelationTests(): \Generator
isNullable: true,
),
];

// A non-nullable, unidirectional ManyToOne has no inverse remove*()
// setting the owning side back to null, so its setter can be strict.
yield 'many_to_one_not_nullable_no_inverse' => [
'User_simple.php',
'User_simple_not_nullable_no_inverse.php',
new RelationManyToOne(
propertyName: 'category',
targetClassName: \App\Entity\Category::class,
targetPropertyName: 'foods',
mapInverseRelation: false,
isOwning: true,
),
];
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class User
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column()]
private ?int $id = null;

#[ORM\ManyToOne]
#[ORM\JoinColumn(nullable: false)]
private ?Category $category = null;

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

public function getCategory(): ?Category
{
return $this->category;
}

public function setCategory(Category $category): static
{
$this->category = $category;

return $this;
}
}
Loading