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:

  1. \Drupal\comment\CommentInterface::getCreatedTime() and ::setCreatedTime()
  2. \Drupal\content_translation\ContentTranslationMetadataWrapperInterface::getCreatedTime() and ::setCreatedTime()
  3. \Drupal\file\FileInterface::getCreatedTime::getCreatedTime() and ::setCreatedTime()
  4. \Drupal\media\MediaInterface::getCreatedTime() and ::setCreatedTime()
  5. \Drupal\node\NodeInterface::getCreatedTime::getCreatedTime() and ::setCreatedTime()
  6. \Drupal\user\UserInterface::getCreatedTime::getCreatedTime() and ::setCreatedTime()
  7. \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.

Comments

pfrenssen created an issue. See original summary.

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.0-alpha1 will be released the week of January 30, 2017, which means new developments and disruptive changes should now be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

seanb’s picture

pfrenssen’s picture

Status: Active » Needs review
Issue tags: +drupaldevdays
StatusFileSize
new10.32 KB

Here is an initial implementation. I did not implement it for ContentTranslationMetadataWrapper since it is not an entity, even though it uses the same methods.

Status: Needs review » Needs work

The last submitted patch, 4: 2833378-4.patch, failed testing.

pfrenssen’s picture

Status: Needs work » Needs review
StatusFileSize
new11.3 KB
new1011 bytes

Forgot to add the trait to the User entity.

pol’s picture

Excellent idea! Thanks!

dawehner’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityCreatedTrait.php
@@ -0,0 +1,37 @@
+/**
+ * Provides a trait for getting and setting the created time of an entity.
+ */
+trait EntityCreatedTrait {

I'm wondering whether we could have a method for the base field as well. I know the EntityChangedTrait doesn't have it either but \Drupal\Core\Entity\RevisionLogEntityTrait does.

hchonov’s picture

Yes, 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.

phenaproxima’s picture

So we would need a new section e.g. entity_metadata_keys.

Is there any reason not to add 'created' to the existing entity_keys section?

hchonov’s picture

I am not really sure. Entity keys are required and there must always be a present value.

amateescu’s picture

@hchonov, nope, most entity keys are not actually required. That's why we have \Drupal\Core\Entity\EntityTypeInterface::hasKey() :)

pfrenssen’s picture

Assigned: Unassigned » pfrenssen

Is there any reason not to add 'created' to the existing entity_keys section?

I am not really sure. Entity keys are required and there must always be a present value.

Entity keys are documented in EntityTypeInterface::getKeys(). There are a couple of optional entity keys such as bundle and uuid but I don't think the created property 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 created property like is done in RevisionLogEntityTrait. I think this added complexity is overkill. I prefer the simple approach of providing a hardcoded key such as is done in EntityChangedTrait. 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.

I'm wondering whether we could have a method for the base field as well. I know the EntityChangedTrait doesn't have it either but \Drupal\Core\Entity\RevisionLogEntityTrait does.

In RevisionLogEntityTrait this is possible because the base fields are added in the base class RevisionableContentEntityBase. 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 in EntityPublishedTrait. This can then be easily included in baseFieldDefinitions():

public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
  $fields = parent::baseFieldDefinitions($entity_type);
  $fields += static::changedBaseFieldDefinitions($entity_type);
  return $fields;
}
pfrenssen’s picture

I 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:

  public static function publishedBaseFieldDefinitions(EntityTypeInterface $entity_type) {
    if (!is_subclass_of($entity_type->getClass(), EntityPublishedInterface::class)) {
      throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not implement \Drupal\Core\Entity\EntityPublishedInterface.');
    }
    if (!$entity_type->hasKey('published')) {
      throw new UnsupportedEntityTypeDefinitionException('The entity type ' . $entity_type->id() . ' does not have a "published" entity key.');
    }

    return [$entity_type->getKey('published') => BaseFieldDefinition::create('boolean')
      ->setLabel(new TranslatableMarkup('Publishing status'))
      ->setDescription(new TranslatableMarkup('A boolean indicating the published state.'))
      ->setRevisionable(TRUE)
      ->setTranslatable(TRUE)
      ->setDefaultValue(TRUE)];
  }

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.

pfrenssen’s picture

I discussed this with @amateescu and @tstoeckler and the approach with the hardcoded key from EntityChangedTrait is considered as "the old way", and the approach with the abusing of undocumented entity keys from EntityPublishedTrait is "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 convert EntityChangedTrait to the new way: #2209971: Automatically provide a changed field definition.

I'll update the patch so it follows the example from EntityPublishedTrait.

pfrenssen’s picture

Issue summary: View changes
pfrenssen’s picture

StatusFileSize
new18.02 KB
new8.66 KB

Updated patch with the approach from EntityPublishedTrait and taking some inspiration from #2209971: Automatically provide a changed field definition.

Status: Needs review » Needs work

The last submitted patch, 17: 2833378-17.patch, failed testing.

pfrenssen’s picture

Status: Needs work » Needs review
StatusFileSize
new18.6 KB
new809 bytes

Forgot to add the base field to the Node entity.

Status: Needs review » Needs work

The last submitted patch, 19: 2833378-19.patch, failed testing.

pfrenssen’s picture

Assigned: pfrenssen » Unassigned

This 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.

amateescu’s picture

pfrenssen’s picture

Status: Needs work » Postponed

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

mparker17’s picture

Issue summary: View changes

Added \Drupal\media\MediaInterface and \Drupal\workspaces\WorkspaceInterface to the "examples from core" (current as of commit dc5863eed1 - 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.

mparker17’s picture

StatusFileSize
new20.18 KB

Here's an attempt to reroll the patch in #19 from 8.4.x-dev to 9.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 EntityChangedInterface to \Drupal\media\MediaInterface and \Drupal\workspaces\WorkspaceInterface.

mparker17’s picture

StatusFileSize
new19.18 KB

Whoops, 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.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

avpaderno’s picture

Issue summary: View changes

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.