Problem/Motivation
We have multiple implementations of entities that have a created timestamp. Most of these currently use similar methods to set and get this timestamp.
Some examples from core:
\Drupal\comment\CommentInterface::getCreatedTime()and::setCreatedTime()\Drupal\content_translation\ContentTranslationMetadataWrapperInterface::getCreatedTime()and::setCreatedTime()\Drupal\file\FileInterface::getCreatedTime::getCreatedTime()and::setCreatedTime()\Drupal\media\MediaInterface::getCreatedTime()and::setCreatedTime()\Drupal\node\NodeInterface::getCreatedTime::getCreatedTime()and::setCreatedTime()\Drupal\user\UserInterface::getCreatedTime::getCreatedTime()and::setCreatedTime()\Drupal\workspaces\WorkspaceInterface::getCreatedTime()and::setCreatedTime()
This pattern is also very widespread in contributed modules.
Proposed resolution
Create a EntityCreatedInterface similar to the EntityChangedInterface which we already have. Rework the existing entities so they extend this interface. Provide an EntityCreatedTrait that contains the most common implementation and use this too to replace duplicated code in core where possible.
Remaining tasks
Discuss and implement.
User interface changes
None.
API changes
A new interface and trait will be available. No backwards compatibility breaks.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #33 | 2833378-33.patch | 19.18 KB | mparker17 |
Comments
Comment #3
seanbThis could be used in the new media module as well #2831274: Bring Media entity module to core as Media module.
Comment #4
pfrenssenHere is an initial implementation. I did not implement it for
ContentTranslationMetadataWrappersince it is not an entity, even though it uses the same methods.Comment #6
pfrenssenForgot to add the trait to the
Userentity.Comment #7
polExcellent idea! Thanks!
Comment #8
dawehnerI'm wondering whether we could have a method for the base field as well. I know the
EntityChangedTraitdoesn't have it either but\Drupal\Core\Entity\RevisionLogEntityTraitdoes.Comment #9
hchonovYes, it would be nice to provide a base field definition as well, but this means we would have to specify the created field name in the entity annotation. So we would need a new section e.g. entity_metadata_keys.
Comment #10
phenaproximaIs there any reason not to add 'created' to the existing entity_keys section?
Comment #11
hchonovI am not really sure. Entity keys are required and there must always be a present value.
Comment #12
amateescu commented@hchonov, nope, most entity keys are not actually required. That's why we have
\Drupal\Core\Entity\EntityTypeInterface::hasKey():)Comment #13
pfrenssenEntity keys are documented in
EntityTypeInterface::getKeys(). There are a couple of optional entity keys such asbundleanduuidbut I don't think thecreatedproperty is a good fit for the entity keys. The entity keys describe really basic properties from the entity object such as its ID, bundle and language. I feel like the created date is much too arbitrary to make sense as an entity key.I personally think we shouldn't account for differences in the naming of the
createdproperty like is done inRevisionLogEntityTrait. I think this added complexity is overkill. I prefer the simple approach of providing a hardcoded key such as is done inEntityChangedTrait. These kind of traits are intended to provide consistency and improve the developer experience. If people really want to use a different machine name they can simply opt to not use the trait and implement their own getter and setter.In
RevisionLogEntityTraitthis is possible because the base fields are added in the base classRevisionableContentEntityBase. We cannot do it in a similar way here because we can only have one base class. What we can do is provide a static method that returns the field definition, like inEntityPublishedTrait. This can then be easily included inbaseFieldDefinitions():Comment #14
pfrenssenI just found a trait where an undocumented entity key is used to make a similar property configurable:
EntityPublishedTrait- it uses the undocumented 'published' entity key:This changes my mind a bit about (ab)using entity keys for this purpose. If core is already doing it then we can follow this pattern.
Comment #15
pfrenssenI discussed this with @amateescu and @tstoeckler and the approach with the hardcoded key from
EntityChangedTraitis considered as "the old way", and the approach with the abusing of undocumented entity keys fromEntityPublishedTraitis "the new way". The uniqueness of the entity keys are not yet enforced in any way, but there is little danger for clashes. There is in fact already an issue to convertEntityChangedTraitto the new way: #2209971: Automatically provide a changed field definition.I'll update the patch so it follows the example from
EntityPublishedTrait.Comment #16
pfrenssenComment #17
pfrenssenUpdated patch with the approach from
EntityPublishedTraitand taking some inspiration from #2209971: Automatically provide a changed field definition.Comment #19
pfrenssenForgot to add the base field to the
Nodeentity.Comment #21
pfrenssenThis is now hit with the same failures as are happening in #2209971: Automatically provide a changed field definition. The added entity key causes a schema change. Not sure how to proceed right now.
Comment #22
amateescu commentedYou could wait for #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field to land and that problem will go away :)
Comment #23
pfrenssenNice, thanks! Let's do that :)
Postponing on #2841291: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field, it's RTBC so it might land pretty soon!
Comment #31
mparker17Added
\Drupal\media\MediaInterfaceand\Drupal\workspaces\WorkspaceInterfaceto the "examples from core" (current as of commitdc5863eed1- HEAD of 9.2.x on 2021-03-15); and fully-qualified all the interface names too.Didn't understand why ContentTranslationMetadataWrapperInterface was crossed out in #16 - it still exists, isn't deprecated, and the functions still appear to do what the other ones do - so I've un-deleted it.
Comment #32
mparker17Here's an attempt to reroll the patch in #19 from
8.4.x-devto9.2.x.There were merge conflicts: I've tried to resolve them as best I can (@pfrenssen, could I trouble you to re-read the patch to see if it makes sense?). However, I've made no other changes (and I'm not sure how to interdiff the merge conflict resolutions), so I'm not uploading an interdiff here.
Lets see if this passes tests - once it does, we can try adding
EntityChangedInterfaceto\Drupal\media\MediaInterfaceand\Drupal\workspaces\WorkspaceInterface.Comment #33
mparker17Whoops, git rebased that badly, causing me to resolve merge conflicts badly, and I didn't compare the resulting patch to the patch in #19 as closely as I should have - so #32 is indeed a broken re-roll. (merge conflicts are hard when you didn't make the original changes!)
Lets try this (re-)re-roll instead.
Comment #39
avpaderno