Create a generic way to decorate Entity classes

Created on 19 November 2017, about 7 years ago
Updated 26 February 2024, 10 months ago

Problem/Motivation

The Decorator pattern can be used to add additional functionality to a class at runtime.

It is currently used by the Views UI to add functionality to a View entity that is only relevant for use by the UI.

A large majority of the code is pass-through implementations of methods to satisfy the interface.

Due to EntityInterface being very large, this boilerplate makes the decorator very large.

Proposed resolution

Introduce a generic EntityDecorator to satisfy EntityInterface, allowing the actual needs of the class to be separate from the majority of the boilerplate.

This will also be used by the Layout Initiative.

Remaining tasks

User interface changes

API changes

Data model changes

πŸ“Œ Task
Status

Active

Version

11.0 πŸ”₯

Component
EntityΒ  β†’

Last updated about 17 hours ago

Created by

πŸ‡ΊπŸ‡ΈUnited States tim.plunkett Philadelphia

Live updates comments and jobs are added and updated live.
  • Blocks-Layouts

    Blocks and Layouts Initiative. See the #2811175 Add layouts to Drupal issue.

  • Needs subsystem maintainer review

    It is used to alert the maintainer(s) of a particular core subsystem that an issue significantly impacts their subsystem, and their signoff is needed (see the governance policy draft for more information). Also, if you use this tag, make sure the issue component is set to the correct subsystem. If an issue significantly impacts more than one subsystem, use needs framework manager review instead.

Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡©πŸ‡ͺGermany Anybody Porta Westfalica

    Nice one, I think this is still relevant. To add a use-case:

    In a contrib module I'd like to extend entities with further API methods, for example for a user isSubscribed(): bool for developer convenience.

  • πŸ‡©πŸ‡ͺGermany geek-merlin Freiburg, Germany

    @Anybody: No, that's a different party. In simple words: If you add methods to a decorating class, the next decorator can wrap and hide your methods.

    I guess you want BundleClasses: https://www.drupal.org/node/3191609 β†’

    ;-)

  • πŸ‡©πŸ‡ͺGermany Anybody Porta Westfalica

    Thanks @geek-merlin! Great reminder, I had already forgotten that I read about it once!
    [Oh man, the Drupal('s great features) documentation. Probably the most important area left behind (by us all as community).] BTT!

  • πŸ‡¨πŸ‡­Switzerland berdir Switzerland

    > In a contrib module

    No, in a *contrib* module you don't want to use bundle classes for user or any other entity type that's not your own IMHO, exactly because these are not decorators. You can only have exactly one bundle class active for a given bundle. So if you have two contrib modules doing that, one of them would win and the other would not be used, breaking code that depends on it.

    Bundle classes are for custom modules and I guess possibly your own entity types/bundles in contrib modules if you have predefined, specific bundles.

    If we want to allow contrib modules to add some methods to other entity type classes then we would indeed need such a decorator system, but that IMHO comes at a price of complexity, decorators need to be correctly implemented and add extra method calls for every single decorator that is there, as each method call is passed through all decorated classes, which also makes things even harder to debug around entities.

    A better approach for that might be an Adapter pattern IMHO, so if you have subscriber related logic for a user, you can do new SubscriberUserAdapter($user), and then use your methods and just those on a user when you need them. It's an extra line in your code and then only exposes the specific methods you defined, but it's also not overhead to pass through every single time anyone does something with a user.

  • πŸ‡©πŸ‡ͺGermany Anybody Porta Westfalica

    @Berdir: Thanks for the additional explanation! Like you wrote, there's no silver bullet for such cases and all solutions come with pro's and con's.

    What I currently use is kind of Proxy pattern. And I think my case isn't that far away from what the issue summary described. But the DX downside is that you have to implement all methods and delegate them to the proxied object, which causes lots of boilerplate and is problematic in evolving systems like Drupal, where the Interface may be subject to change and requires to proxy to adapt these changes.

    I was also thinking about, if Traits might help, but it then seems to end up similar to extending the base class... so I think let's come back to the original issue here and keep the cases like described in #34 in mind. Being able to "extend" entity classes is surely helpful and already impossible, perhaps we'll come to better out of the box solutions and patterns for Drupal here in the future.
    Thanks a lot for your feedback and support, much appreciated!

  • πŸ‡©πŸ‡ͺGermany geek-merlin Freiburg, Germany

    @Anybody: There is a package for that too: https://packagist.org/packages/geeks4change/composable-inheritance
    (Feel free to contact me on another channel if it's useful for you.)

  • πŸ‡©πŸ‡ͺGermany Anybody Porta Westfalica

    Thank you very much @geek-merlin that looks super interesting! :)
    I'm done with my project where I ran into this, but will definitely consider it in the future!

Production build 0.71.5 2024