Relations
| OneToMany side | |
| ManyToOne side | |
| Cascade persist | |
| Orphan removal note |
OneToMany side
A shop author writes many posts, but each post has one author. The parent side holds a collection of children mapped by the field on the child side.
The mappedBy value names that child field, and targetEntity names the child class. Build an empty collection in the constructor so the property is never null.
#[ORM\OneToMany(mappedBy: "author", targetEntity: Post::class)] private Collection $posts; public function __construct() { $this->posts = new ArrayCollection(); }
ManyToOne side
The child side owns the link: it stores the foreign key column in the database. The inversedBy value names the collection on the parent side, so both sides stay in sync.
Join column settings describe the column itself. Marking it not nullable means every post must belong to an author.
#[ORM\ManyToOne(inversedBy: "posts")] #[ORM\JoinColumn(nullable: false)] private Author $author;
Cascade persist
Saving a parent with new children takes one extra word in the mapping: cascade with persist. New children then save together with the parent in one go.
Persist the parent once and flush. Doctrine inserts the parent row first, then the child rows with the right foreign keys.
$entityManager->persist($author); $entityManager->flush();
Orphan removal note
Removing a child from the collection breaks the link, but the row stays in the database by default. That default surprises beginners who expect a delete.
Set orphan removal on the parent mapping when a child without a parent makes no sense. Doctrine then deletes the row once it leaves the collection.
#[ORM\OneToMany(mappedBy: "author", targetEntity: Post::class, orphanRemoval: true)]
Next: Forms
Article author: Arthur Isaev