Mastering Doctrine ORM relations

Marco Pfeiffer on 2021-11-17

There are only 2 types of relations. All other types are usually bugs.

Photo by John Schnobrich on Unsplash

Owning Relations

You can easily identify this relation by these properties:

Let’s say you have a Product, which has ProductVariants. Those variants are basically part of the Product and not stand alone entities.

class Product {
  /**
   * @ORM\OneToMany(
   *   targetEntity=ProductVariant::class,
   *   mappedBy="product",
   *   orphanRemoval=true,
   *   cascade={"ALL"}
   * )
   * @Assert\Valid()
   */
  private Collection $variants;
}
class ProductVariant {
  /**
   * @ORM\ManyToOne(
   *   targetEntity=Product::class,
   *   inversedBy="variants"
   * )
   * @ORM\JoinColumn(
   *   onDelete="CASCADE",
   *   nullable=false
   * )
   */
  private Product $product;
}

So what does that mean?:

This consistency is enforced both on the doctrine side and on the database side, so you can also easily delete a product in the database without checking the relations first.

As a side note: Bidirectional relations require a lot of logic in their getters and setters. I have created PHPStorm live templates to ease writing those.

Referencing Relations

These relations have the following properties:

Let’s use a Product example and think of a Category. A Product can have 1 or many Categoryies.

class Product {
  /**
   * If there can be many
   * @ORM\ManyToMany(targetEntity=Category::class)
   */
  private Collection $categories;
  /**
   * If there can only be one
   * @ORM\ManyToOne(targetEntity=Category::class)
   * @ORM\JoinColumn(onDelete="SET NULL")
   */
  private ?Category $category;
}
class Category {
  // The category usually does not know about the Product
}

There is nothing really special to this type. Just note that you usually never have a OneToMany here, as this implies an ownership of the related entity which you do not have here.

Other relation types?

Other relation type are rare. You should stick with those 2 unless you really know what you are doing.

How to use them consistently

I’d really like some meta annotations that is just correct by default. Something like @OwningRelation(ProductVariant::class) and that would be it, but unfortunately that doesn’t exist.

In the meantime, what I do is use live templates/macros in my IDE so I don’t have to think about the options every time I define a relation:

Use PHPStorm Live Templates for Doctrine fields The Doctrine ORM requires you to use a very verbose programming style where you can easily make needless mistakes if…medium.marco.zone