Problem/Motivation

We currently use an entity key "status" for entity types such as "node", but the field has to be manually added in the entity type's base field definitions.

Proposed resolution

Remove "Status" field from the entity type base field definition when it has the "status" entity key, and use a generic definition in ContentEntityBase.

Remaining tasks

User interface changes

API changes

Data model changes

Comments

timmillwood created an issue. See original summary.

timmillwood’s picture

StatusFileSize
new1.59 KB

Initial patch.

timmillwood’s picture

Status: Active » Needs review
timmillwood’s picture

StatusFileSize
new2.8 KB
new1.2 KB

Adding comment entity type.

Status: Needs review » Needs work

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

timmillwood’s picture

ok, I guess comment module needs an upgrade path after adding status as an entity key.

timmillwood’s picture

StatusFileSize
new4.35 KB
new1.55 KB

Adding an upgrade path for comment module.

timmillwood’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 7: 2810381-7.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new4.53 KB
new415 bytes

Status: Needs review » Needs work

The last submitted patch, 10: 2810381-9.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new4.69 KB
new579 bytes

Fixing REST test.

amateescu’s picture

Note that we also have #2009958: Introduce EntityStatusInterface to standardize how entities can be disabled which is trying to move the status concept to an interface.

timmillwood’s picture

I think it'd be awesome to have an EntityStatusInterface, and I hope everyone agrees that this is a separate issue and could be done independently. Also this issue is specifically looking at status being published/unpublished, and not in the other way users, files, config entities, etc use a status field. Therefore entity types with a status entity key will get a field for published/unpublished, and could in fact have another status field for another purpose.

dawehner’s picture

Is there a reason this doesn't yet add a status() method or so?

timmillwood’s picture

@dawehner - I was looking at keeping this issue as a foundation, simply moving the base field and making use of the key. I guess #2009958: Introduce EntityStatusInterface to standardize how entities can be disabled would be a good place to add status().

dawehner’s picture

Sorry, my comment in #15 haven't seen #13 and #14 yet. I agree this makes sense.

timmillwood’s picture

plach’s picture

I think we should try to limit the amount of business logic we bake into ContentEntityBase to the very minimum, otherwise we are kind of limiting its purpose to very specific use cases, which is not what we want for a base class, I think. Or at very least we are adding code that won't be used in many cases.

It would make more sense to me to implement this as a trait that can be individually used by entity types implementing the EntityPublishedInterface.

timmillwood’s picture

@plach - I'm not sure if I agree, or not.

We currently have a couple of similar traits "EntityChangedTrait" and "RevisionLogEntityTrait", only "RevisionLogEntityTrait" defines base fields. So following that example it could work, however the part that makes it feel not right to me is the entity key.

In Node status is an entity key, and this patch makes status an entity key in Comment too. All base fields currently defined within ContentEntityBase are all entity keys, therefore I like the idea of carrying on putting entity key fields in ContentEntityBase.

I'd like to get to the point that the majority of entity types have a published/unpublished state and therefore use the status entity key and field.

timmillwood’s picture

After speaking to dixon_ and amateescu I think having the status field in ContentEntityBase is the best option. Mainly because it's linked to an entity key so the field is auto added to any entity type with that key, where as a trait would need to be manually added.

plach’s picture

In Node status is an entity key, and this patch makes status an entity key in Comment too. All base fields currently defined within ContentEntityBase are all entity keys, therefore I like the idea of carrying on putting entity key fields in ContentEntityBase.

Entity keys are meant to provide a way to map generic business logic to the fields required to implement it without "crippling" subclasses with reserved field names. So the fact that status is an entity key does not automatically imply it should be part of content entity base, it's just a pointer that it probably makes sense as being used by a base class. IMHO the node entity type is currently abusing the status entity key (and it's doing it wrong, btw: http://cgit.drupalcode.org/drupal/tree/core/modules/node/src/Entity/Node...).

I'd like to get to the point that the majority of entity types have a published/unpublished state and therefore use the status entity key and field.

What feels wrong to me here is the following bit: the majority of entity types. If we do this it must be something that we feel is an inherent and obvious part of the generic content entity business logic. However there are core entity types for which the concept of publishing simply makes no sense, at least according to their current implementation. In fact they may have a concept of status that does not map directly to the concept of publishing. If we add the status field to CEB, then the next natural step is making it implement EntityPublishedInterface, which may quickly lead to a very confusing API in this case. The User and File entity types are a clear example of this scenario.

These are a few where a publishing status would make sense to me mostly when thinking to the "full site revisioning/previewing" problem space:

  • Term
  • MenuLinkContent

And a few more where I'm not even sure about that:

  • ContentModerationState
  • Feed
  • Item
  • Message
  • Shortcut

So it seems to me that the end goal of this issue could use a broader discussion, unless it already happened and I missed it.

plach’s picture

I forgot: if a trait is deemed a suboptimal DX, it would make a lot more sense to me from an architectural POV to have a base class extending RevisionableContentEntityBase that all entity types for which it clearly makes sense the publishing concept can inherit from, e.g. EditorialContentEntityBase .

catch’s picture

Either the trait or the additional base class in #23 makes sense to me. This looks like a 50/50 use case given the list of entities in #22.

timmillwood’s picture

A trait makes sense for many reasons, but the main thing that sways be away from it and towards putting this in ContentEntityBase is the entity key.

My main hit list for adding 'status' to are BlockContent, Term, and MenuLinkContent, this fix it to work properly in Node and Comment. In core (and in contrib) there are already fields called 'status', therefore we need to allow for alternative field names than 'status', thus I feel the entity key is needed. However we could equally do it without the entity key, as long as other entity types use the EntityPublishedInterface it doesn't really matter what they call the field setPublished() and isPublished() will always work the same.

Why does node even have status as an entity key?

timmillwood’s picture

StatusFileSize
new6.62 KB
new5.6 KB

Here's an initial look at how it could work as a trait.

Status: Needs review » Needs work

The last submitted patch, 26: interdiff-12-26.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review

Note to self: pay attention when naming interdiffs.

The last submitted patch, 26: 2810381-26.patch, failed testing.

timmillwood’s picture

StatusFileSize
new7.72 KB
new6.74 KB

also helps if I add the trait to the patch.

Status: Needs review » Needs work

The last submitted patch, 30: 2810381-30.patch, failed testing.

plach’s picture

Thanks for trying the trait approach!

  1. +++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
    @@ -0,0 +1,34 @@
    +      ->setDescription(t('A boolean indicating the published state.'))
    

    This t() call is inconsistent with the previous line.

  2. +++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
    @@ -0,0 +1,34 @@
    +    return ['status' => BaseFieldDefinition::create('boolean')
    

    We can use $entity_type->getKey('status') here to get the field name.

  3. +++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
    @@ -0,0 +1,34 @@
    +    return (bool) $this->get('status')->value;
    ...
    +    $this->set('status', $published ? NODE_PUBLISHED : NODE_NOT_PUBLISHED);
    

    And here we can assume the existence of a ::getEntityKey() method. We may even declare it as abstract if we want to be super strict.

timmillwood’s picture

@plach - as part of the patch in #30 I removed the entity keys. It doesn't seem like status really needs to be one, and as we're now adding it as a trait there is less chance of accidental field name clashes.

plach’s picture

Removing the status key sounds like an API/BC break to me. Why can't we just keep it since it's not hard to do?

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new8.07 KB
new2.68 KB

This should fix all the test fails in #30.
It also keeps the status entity key in Node, and doesn't add one to Comment.

plach’s picture

Status: Needs review » Needs work

Thanks, almost there!

  1. +++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
    @@ -0,0 +1,42 @@
    +trait EntityPublishedTrait {
    ...
    +  public static function publishedBaseFieldDefinitions(EntityTypeInterface $entity_type) {
    

    Missing PHP docs.

  2. +++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
    @@ -0,0 +1,42 @@
    +  public function isPublished() {
    +    return $this->getEntityType()->hasKey('status') ?
    +      (bool) $this->getEntityKey('status') :
    +      (bool) $this->get('status')->value;
    +  }
    

    We could save a few method calls if we did something like this:

      $status = $this->getEntityKey('status');
      return (bool) (isset($status) ? $status : $this->get('status')->value);
    

    Since we have a default value, I guess ::getEntityKey() will return NULL only if the entity key is not defined.

    Same goes for the setter.

  3. +++ b/core/modules/rest/src/Tests/UpdateTest.php
    @@ -292,10 +292,10 @@ public function testUpdateComment() {
         $read_only_fields = [
    +      'status',
           'name',
           'created',
           'changed',
    -      'status',
    
    @@ -311,6 +311,7 @@ public function testUpdateComment() {
    +      'status',
           'pid', // Extra compared to HAL+JSON.
           'entity_id',
           'uid',
    @@ -318,7 +319,6 @@ public function testUpdateComment() {
    
    @@ -318,7 +319,6 @@ public function testUpdateComment() {
           'homepage', // Extra compared to HAL+JSON.
           'created',
           'changed',
    -      'status',
    

    Are these changes required?

plach’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
@@ -0,0 +1,42 @@
+      ->setLabel(new TranslatableMarkup('Publishing status'))
+      ->setDescription(t('A boolean indicating the published state.'))

Can we always use TranslatableMarkup here?

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new8.68 KB
new2.54 KB

#36 1. Done
#36 2. Done
#36 3. Yes, these changes are required, otherwise it fails the Rest module UpdateTest.
#37 Done

plach’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
@@ -5,34 +5,53 @@
+    return (bool) ($status ?: $this->get('status')->value);

Won't this break if the entity type has a status key different from status and the field value is 0 or FALSE?Shouldn't we use isset() to ensure we have a NULL value?

timmillwood’s picture

StatusFileSize
new8.69 KB
new608 bytes

I guess.

plach’s picture

Status: Needs review » Reviewed & tested by the community

Looks good, thanks

timmillwood’s picture

Assigned: timmillwood » Unassigned
amateescu’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityPublishedTrait.php
@@ -0,0 +1,61 @@
+    // similar when introduced. https://www.drupal.org/node/2811667

This references #2811667: Introduce EntityPublishedInterface for content entities, which has been closed as a duplicate of #2789315: Create EntityPublishedInterface and use for Node and Comment so we might want to update the reference to point to the active issue.

  • catch committed 6ddd0ba on 8.3.x
    Issue #2810381 by timmillwood, plach: Add generic status field to...
catch’s picture

Status: Reviewed & tested by the community » Fixed

This looks really tidy to me. Committed/pushed to 8.3.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

hchonov’s picture

The update function for the comment entity type introduced here is not really updating the cached field storage definition. A followup for this - #2841614: comment_update_8300 is not updating correctly the field storage definition of the status field.

timmillwood’s picture