Allow the use of abstract classes in the DataObject hierarchy

<%- if @topic_view.topic.tags.present? %>
<%= t 'js.tagging.tags' %>: <%- @topic_view.topic.tags.each do |t| %> <%= t %> <%- end %>
<% end %>

I’ve been using Silverstripe for over 6 years now and 1 thing that always bothered me is that it’s impossible to use abstract classes in the Data Object hierarchy. This mostly boils down 2 “limitations” of the framework;

  1. The framework constantly tries to instantiate object to read their properties such as configs, which by itself in my opinion is already a less than ideal practice. A lot of head way has been done in this area where configs are now almost completely decoupled from the classes themselves so in this regard it shouldn’t be too hard to overcome.

  2. The way SS ORM builds the tables. It currently “simply” loops through objects parents until it hits “DataObject” so that it can merge fields and perform it’s magic. Basically relying on the idea that the classes extending from DataObject should have tables, rest of the classes it depends on the need and this is the major roadblock to using abstract classes. Obviously abstract classes cannot be instantiated and thus the whole ORM falls apart.

Suggestion: If the table schema and class hierarchy are pre-built during something like dev/build, the ORM would no longer need to perform any real-time checks to find “baseTable”, “baseClass” and so on, as these schemes would exist in some manifest and the ORM would simply need to read from it. Also this would decouple the whole DB management side of things from the normal execution of PHP thus allowing much better use of abstract classes, traits, interfaces, etc… and possibly improve performance as well.

Notes: I do plan to try to tackle this problem and suggest it as a PR, but in all honesty I’m not too familiar with the ClassManifest side of things so would need some help in that area.

What do you think?

Hey Dylan! From an OOP perspective, I understand the desire to use abstract classes like you do elsewhere. As a maintainer thinking about how much would need to change for that to happen, I have to question your use case though. Even if you could come up with a PR, the peer review on this (and avoiding a long tail of edge cases) will require significant effort from core committers. If you have the time to familiarise yourself enough with the ORM to create a well-tested and documented PR, this could have a chance. But on balance, my recommendation would be to find other ways to guide developers building on your code. Abstract classes is only one mechanism. You could refactor the behaviour requiring abstract into injected services on concrete DataObject classes? Or have a built-time sanity check on subclasses. Or just document what a properly implemented subclass would look like.

Thanks for taking the time to write this up!

Hi @chillu, thanks for the reply. I understand how difficult it will be, the amount of functions I found that rely on in instantiating an object is astounding but I see an opportunity here. Apart from the fact that I feel a framework should not limit the use of the underlying language, if we manage to achieve this it would drastically improve performance.

I’ve done only a few benchmarks up till now and I’ve seen upwards of 5x better performance when we let PHP check for method_exists instead of checking ourselves.

Also, the idea I’m leaning towards is to have some sort of service such as DataObjectSchema to map out the ORM hierarchy something like ClassSchema. If I actually manage to do this, effectively all DataExtensions can become traits and would also allow for on the fly injections, which again would help with performance and reduce workarounds.

As a side benefit, the more the framework uses PHP built-in mechanisms, the better IDE autocomplete works thus reducing the learning curve for new comers.

As for the use-case itself, I use SilverStripe for multiple projects but the one that really made me feel the need of abstract classes is an API middleware.