Motivation
Extend the typed data API to allow for discovery of an objects data type based on its interface.
Proposed resolution
Allow for annotating the interface for data types (this will be useful for matching data types as well)
Add a method to the TypedDataManager
which determines the data type of an object based on the interfaces it implements. If multiple interface match (e.g. $node will match "EntityInterface" and NodeInterface) it should return the most specific match (i.e. entity:node
instead entity
).
Remaining tasks
Do it.
User interface changes
-
API changes
Only additions, no BC changes.
Original report by @fubhy
As a follow-up to #2002102: Move TypedData primitive types to interfaces and #1867856: Use annotation discovery for data type plugins we should consider relying on interfaces for defining typed data types. For me this is the next logical step as in case of typed data the interface should really be what matters instead of the class. This would also ensure that each typed data type is based on its own, unique interface while the class may still be swapped.
If we can depend on this it is going to make the implementation of the typed data parameter converter (@see #1906810: Require type hints for automatic entity upcasting as we could then reliably use reflection to read the desired typed data type of a route parameter from the corresponding controller argument typehint).
Task | Novice task? | Contributor instructions | Complete? |
---|
Comment | File | Size | Author |
---|---|---|---|
#35 | drupal_2028097_35.patch | 34.88 KB | Xano |
#35 | interdiff.txt | 4.18 KB | Xano |
#32 | drupal_2028097_31.patch | 38.56 KB | Xano |
#32 | interdiff.txt | 7.16 KB | Xano |
#20 | 2028097-20.patch | 32.5 KB | iMiksu |
Comments
Comment #1
fagoComment #2
BerdirAs discussed, keeping the annotations on the classes and require an interface definition is I think more natural for plugin annotations.
Comment #3
BerdirHm, thinking about it, we might not be able to/have to/want to make it required, as we simply can't provide interfaces for dynamic/derived types like entity bundles.
I would simply say that it is required *if* you want to make things like param converting work or other things that rely on it.
If that's the case, then this is also an API addition and not a change (I know it's required for param converted, we should still aim for avoiding API changes when possible)
Comment #4
fagoSo re-titling this to be less restrictive.
Comment #5
cosmicdreams CreditAttribution: cosmicdreams commented#1906810: Require type hints for automatic entity upcasting is progressing well without this issue. Do we still need it?
Comment #6
dawehnerLet's start with it.
Comment #7
tstoecklerThis is now major, because after #2190313: Add $EntityType::load() and loadMultiple() to simplify loading entities you can no longer replace an entity type without subclassing the base class.
I.e. if I want to provide a different implementation for nodes i.e.
BetterNode
, it should suffice to implementNodeInterface
, but since the aforementioned issue, loading nodes in that case won't work unless I also subclassNode
. I.e.Node::load()
won't work.That is because there is no relation from an entity type to its interface.
Comment #8
BerdirI don't think that will work in cases where we subclass entity types and expose them as different entities, the test entity classes for example.
And as discussed in that issue, one of the reasons we added those complex checks an verifications is to explicitly support things like ECK that will want to dynamically expose entity types based on configuration (this is also why we pass the entity type to baseFieldDefinitions() for example). Requiring a unique interface for each entity type will prevent that from working at all.
Yes, code generation/scaffolding tools will possibly become more common in 8.x than dynamically defining them, but I think we should still aim to support that. I have similar use cases in 7.x too, where I want to dynamically expose external entities to Drupal
And at least for content entities, there's no reason to subclass/replace entity classes IMHO. I don't think there's anything that you can't do by altering fields/hooks?
Comment #9
tim.plunkettAnd at least for content entities, there's no reason to subclass/replace entity classes IMHO
Well that's one half of the API.
I'm really frustrated about that issue going in, it flies in the face of all the work we've done to use interfaces properly.
Comment #10
cosmicdreams CreditAttribution: cosmicdreams commentedWhy wouldn't ECK and modules like it be able to discover entity types with reflection instead of configuration. Why does it have to be through configuration? I've often thought it would be better if ECK generated code anyway. That would make using it a lot more versatile.
Comment #11
dawehnerExtended but this is still not complete.
Comment #12
dawehnerThis is a complete list.
Comment #14
dawehnerUps.
Comment #17
dawehnerIs this issue still possible to be worked on? In general we need quite some reroll here.
Comment #18
xjmI think this is probably beta deadline? But still possible.
Comment #19
star-szrHere's a reroll. Not claiming to understand it :)
I'm not 100% sure about the new changes I made, they should definitely be looked over by someone who knows more about what's happening - see interdiff. But this should cover all @*EntityType annotations.
Comment #20
iMiksuOh darn, I was working on the reroll too. I should've assigned this to me :)
Well, it's good change to review by comparing our work, I did run our patches diff in my local and they were identical apart from the interface identified under
core/modules/system/tests/modules/entity_test
:I used
\Drupal\user\EntityOwnerInterface
whereas you used\Drupal\Core\Entity\ContentEntityInterface
.(+minor changes which was the
$interface
property declaration location)There were 16 cases which I/we checked on.
Files that were missing:
Files that were moved:
New
@ConfigEntityType
cases:New
@ContentEntityType
cases:I'm now going through and make sure that the interface names haven't actually changed.
Comment #21
iMiksuFound five namespace changes.
See interdiff.txt and new patch with updated ones (continuing from #19, ignore #20 patch but consider my our difference on the interface identified between #19 and #20).
Comment #22
iMiksuNow, there's documentation we probably need to update. Here's my initial change in
entity.api.php
.I'm not sure whether we should add interface entry into annotations example in
core.api.php:1560-1569
:Comment #23
star-szrOops, well no sweat and thanks for taking this even further @iMiksu! I was just inspired this morning to try and push a beta deadline issue forward.
Comment #24
alexpottCan we get a clear explanation of the problems this issue is solving? Especially since #1906810: Require type hints for automatic entity upcasting got solved without this.
Comment #25
fagoI do not think this should be in beta-deadline, as adding an entity interface can continue to be an optional thing. Yes it's best practice, but nothing is going to break if you not add it. You may not receive some of the benefits of APIs that build upon on it, but all the existing base-functionality like #1906810: Require type hints for automatic entity upcasting continuous to work without.
This would be very useful to allow token API to discover data types and work based on typed data API without breaking it. Related: #1920688: Support multiple instances of the same token type in token replacement.
Comment #28
XanoRe-roll.
Comment #29
fagoComment #32
XanoComment #33
XanoComment #34
fagoReviewed the existing code.
Looks like this interface does not belong to this entity type, so it should not be added.
Needs to be public.
Wrong docs. Maybe should be better named, e.g. discoverDataType().
Looks like this is the wrong one.
pointless.
should just use nullcache?
weird empty lines.
pointless.
pointless
pointless. (and quite some more of them ;)
Comment #35
XanoHow is that wrong exactly?
No, because we need to feed the definitions to the manager somehow.
What do you mean?
Comment #36
fagoNot sure how we'd move on here to also support type inheritance for primitives:
timestamp is a integer
integer -> IntegerInterface?
timestamp is a datetime
datetime -> DateTimeInterface?
For having a data type 'datetime' such that you can specfiy that as requirement (e.g. context) which then matches with a "timestamp" argument, we need to know that "datetime" does not simply implement DateTimeInterface, but basically is represented by DateTimeInterface. Same for being able to pass "email" typed data to a "string" slot.
Consequently, we need to have the interface annotated for those primitive types also. But then, we have once interfaces which are on the typed data classes and once interface on the data objects. So how to we handle that differentation best?
If it's only about type-matching, there we do not really care on where the interfaces are implemented - but then, still it would be a bit weird if it's not clearly specified where the interface actually is going to be implemented? Thus, I'd rather add two keys: 'value_interface' and 'data_interface' and leverage both for type-matching?
Comment #37
fago'data_interface' could be just 'type_interface' maybe? So far, it would mostly applies to primitive interfaces and datetime, duration - so type interface would fit here.
Comment #43
jhedstromBased on #36 and #37 this is needs work?