How to validate ORM relations in Silverstripe

Sometimes, the defined relations in our model can be incorrect. That's normal and we're just developers. Well, even AI-devs like ChatGPT or Copilot can make mistakes, not just us. That's where all those testing and validation tools are invaluable.

Silverstripe Framework comes with a tool called RelationValidationService that checks if the relationships are set up correctly in both directions. It's run on every dev/build. That's good. But unfortunately, you have to configure it a little bit, and then it may shout out a long red list when running dev/build?flush.

Common mistakes in relations

Like in real life, even if you think your relations are fine, they could be more perfect. It's always worth improving relations, so what are the common mistakes for Silverstripe ORM relations?

Relation to an unknown class or relation is no DataObject

Well, this is easy to spot. Dev/build will break and you're forced to fix it.

Back relation is missing

Common for has_one to e.g. Image. Technically it works, but for Silverstripe it's better to know what's the relation back, even if you don't use it in your code. Who knows, maybe a future developer (read: you) want's to crate a task that checks if an image is used somewhere or if the image can be deleted, then the relation back can be used.

Relation is not in the expected format

Sometimes, Silverstripe expects you to write ClassName.RelationName. to fully understand your relations.

Back relation is ambiguous

The back relations of a has_many is a has_one. And if, for some reason, the other class has two has_ones to the original class, we all get confused, not only Silverstripe`s ORM.

RelationValidationService's Configuration

In order to switch on RelationValidationService on every dev/build, you need to configure the namespaces of the DataObjects you want to be validated, and you need to enable output by default.

---
Only:
  environment: 'dev'
---

SilverStripe\Dev\Validation\RelationValidationService:
  output_enabled: true
  allow_rules:
    myNamespace: MyCompany

Then you see a bunch of errors, can go through your code and adjust the relation namings, e.g. convert

    public static array $has_many = [
        'PhotoGalleryImages' => PhotoGalleryImage::class
    ];

to

    public static array $has_many = [
        'PhotoGalleryImages' => PhotoGalleryImage::class. '.Album'
    ];

if the relation back is called "Album".

For third party DataObjects you refer to, you can use yaml config to add relations back, e.g.:

SilverStripe\Assets\Image:
  belongs_to:
    Event: MyProject\Model\Event.Image
    Notification: MyProject\Model\Notification.Image

I think this tool should be switched on by default in dev environments. In an ideal world you hook it to your unit tests to get warnings as soon as possible.

More information about RelationValidationService , e.g. how it can be included in unit tests, can be found in the documentation.