- π©πͺ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!