Problem/Motivation

In #2810381: Add generic status field to ContentEntityBase we're looking to make the status field, used for published/unpublished status on nodes and comments more generic. Although there is currently no generic interface for this.

Proposed resolution

Introduce EntityPublishedInterface and name use of it in NodeInterface and CommentInterface

Remaining tasks

User interface changes

API changes

Data model changes

Comments

timmillwood created an issue. See original summary.

timmillwood’s picture

Status: Active » Needs review
StatusFileSize
new7.64 KB
new579 bytes

Initial patch adding the EntityPublishedInterface.

Note: #2009958: Introduce EntityStatusInterface to standardize how entities can be disabled adds a very similar interface but focuses on config entities (enabled/disabled state), where as this focuses on content entities (published/unpublished state).

Status: Needs review » Needs work

The last submitted patch, 2: 2811667-2.patch, failed testing.

yogeshmpawar’s picture

Assigned: Unassigned » yogeshmpawar
timmillwood’s picture

Assigned: yogeshmpawar » timmillwood
Status: Needs work » Needs review
StatusFileSize
new7.58 KB

Fixing patch

Status: Needs review » Needs work

The last submitted patch, 5: 2811667-5.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new6.92 KB
new1.13 KB

fixing bug introduced in #5

Status: Needs review » Needs work

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

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new8.02 KB
new7.31 KB

Actually tested it this time.

Status: Needs review » Needs work

The last submitted patch, 9: 2811667-9.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new8.04 KB
new1.34 KB

Fingers crossed

Status: Needs review » Needs work

The last submitted patch, 11: 2811667-11.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new8.05 KB
new580 bytes

Forgot to update Node.

phenaproxima’s picture

+1 to this idea. I have a question, though: why are the STATUS_PUBLISHED and STATUS_UNPUBLISHED constants in ContentEntityBase? Would it maybe make more sense for them to be part of EntityPublishedInterface and renamed PUBLISHED and UNPUBLISHED?

Anonymous’s picture

This is a great idea, however the issue I have found that needed to be solved in the past is actually tracking the date that the item was published, which is separate to the date the item was created. Solutions currently bring the created date forward, which is semantically incorrect. This is particularly relevant when listing content in order of publication date.

Would it be possible to discuss adding this property to the node entity as part of this feature? It would be recorded on the first transition change from unpublished to published, or be identical to the created date if published immediately. Items that are then unpublished will retain this date, however would be required to bring it forward if necessary. As a result of this, the created date should then be ideally be made immutable.

interface EntityPublishedInterface {

  /**
   * Get the date that the entity was first published.
   *
   * @return \DateTime
   * @throws EntityNeverPublishedException
   *   If the entity has never been published.
   */
  public function getPublishedDate();

}
timmillwood’s picture

@GroovyCarrot - This is a great idea (but a separate issue).

Anonymous’s picture

Makes sense, in which case should probably be deferred until after this is merged in.

@phenaproxima there's probably no value in adding constants to represent TRUE and FALSE for published status at the interface level. It does make sense to have the status constants if there are multiple statuses, however since there is only 2 statuses (published and not) there is no immediate added value. I guess it does accommodate for something like a 'needs review' status, which is still unpublished.

Off the back of this, if published status for an entity is a result of a series of factors (status, created date in the past, etc), then perhaps EntityPublishedInterface::setPublished() should be removed, and the entity allowed to decide what it considers to mean published - such as a simple underlying status property. If a service requires the ability to set an entity to 'published' state, then it should be segregated into a separate interface (EntityPublishableInterface) for readability, and to be explicit in that the service intends to mutate that state.

timmillwood’s picture

StatusFileSize
new8.33 KB
new5.81 KB

I agree with #14, the constants should be in EntityPublishedInterface (they were in an earlier patch, but moved them during debugging).

We can't however called the constant PUBLISHED because CommentInterface as a constant PUBLISHED and if CommentInterface implements EntityPublishedInterface we can an override error (as seen in some earlier failing tests).

I'm not sure I understand the final paragraph in #17.

ebeyrent’s picture

+++ b/core/modules/comment/src/CommentInterface.php
@@ -3,23 +3,24 @@
+  const NOT_PUBLISHED = EntityPublishedInterface::STATUS_UNPUBLISHED;

Do we need to declare these constants again? It looks like this patch removes the use of PUBLISHED and NOT_PUBLISHED in favor of the constants declared in EntityPublishedInterface, which I think is the better way to go. Can we simply remove these?

timmillwood’s picture

We can't remove them for backwards compatibility.

martin107’s picture

StatusFileSize
new786 bytes
new8.42 KB

Fixing ultra trivial coding standard fixes to our shiny new EntityPublishedInterface, move along nothing to see here :)

Anonymous’s picture

+++ b/core/lib/Drupal/Core/Entity/EntityPublishedInterface.php
@@ -0,0 +1,38 @@
+interface EntityPublishedInterface {
+
+  /**
+   * Denotes that the entity is published.
+   */
+  const STATUS_PUBLISHED = 1;
+
+  /**
+   * Denotes that the entity is not published.
+   */
+  const STATUS_UNPUBLISHED = 0;
+
+  /**
+   * Returns the entity published status indicator.
+   *
+   * Unpublished entities are only visible to their authors and to administrators.
+   *
+   * @return bool
+   *   TRUE if the entity is published.
+   */
+  public function isPublished();

Constants here are unnecessary as they are meaningless to the interface; the reason for this is because isPublished() returns bool, and not one of STATUS_PUBLISHED|STATUS_UNPUBLISHED. Using status constants would overcomplicate this, since there's nothing wrong with TRUE or FALSE regarding whether or not something is in a published state.

The interface is an abstraction for a client, and shouldn't dictate how the object implements the method. To elaborate this a bit more: the node entity has chosen to use a status property to track whether or not it is published, at the moment this is either published (1), or unpublished (0). isPublished() returns (bool) $this->getEntityKey('status'), so the method still conforms to the interface and returns a boolean, without you needing to care what the underlying state codes mean. This allows them to be changed by the entity at any point in future (so you don't have to worry about BC breaks).

+++ b/core/lib/Drupal/Core/Entity/EntityPublishedInterface.php
@@ -0,0 +1,38 @@
+  /**
+   * Sets the published status of an entity.
+   *
+   * @param bool $published
+   *   TRUE to set this entity to published, FALSE to set it to unpublished.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface
+   *   The called entity.
+   */
+  public function setPublished($published);

Just because an entity has a recognised published state, does not necessarily mean that it can, or should be toggled. An interface should not require that the state can be changed / is mutable by the API, unless that is an explicit requirement by the client; in which case it should require a different interface that allows that.

For example, the following interfaces are more explicit, and are better suited to what the client would expect to do with the entity.

interface EntityPublishableInterface extends EntityPublishedInterface {

  /**
    * Transition the publication into the published state.
    *
    * @throws EntityAlreadyPublishedException
    */
  public function publish();

}

interface EntityRetractableInterface extends EntityPublishableInterface {

  /**
    * Retract the publication status of this entity.
    *
    * @throws EntityNeverPublishedException
    */
  public function retract();

}
timmillwood’s picture

I'm not sure I agree with the comments in #22.

The plan for this patch is to give a common interface for both node and comment entities, and possibly others in the future. We are not looking to redesign the platform, just abstract the common functionality and remove duplication.

Anonymous’s picture

StatusFileSize
new8.53 KB

I'd argue that #13 was right then, with the implementation constants on the abstract class; however I'd still suggest segregating the interface better so that it's more specific towards the client, and there's room for improvement.

timmillwood’s picture

@GroovyCarrot - Because of the way constants have been used in node.module and CommentInterface historically I think these should be moved to EntityPublishedInterface and not ContentEntityBase. I don't think we can or should have entity types with a published/unpublished status with anything different than 1/0, it should be consistent.

Anonymous’s picture

The interface just doesn't make sense on it's own with the constants that are integers, and methods that return or accept a boolean. The behaviour you are describing is better placed with a trait if it is to direct how common functionality is implemented?

Status: Needs review » Needs work

The last submitted patch, 24: 2811667-24.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review
amateescu’s picture

@timmillwood, isn't this a duplicate of #2789315: Create EntityPublishedInterface and use for Node and Comment? The initial patch over there introduces EntityPublishedInterface but in comments #3 and #10 you advocated for EntityStatusInterface instead.. it's a bit confusing to have so many duplicate issues around this :/

timmillwood’s picture

Title: Introduce EntityPublishedInterface for content entities using the status entity key » Introduce EntityPublishedInterface for content entities

After discussions in #2810381: Add generic status field to ContentEntityBase I'm not sure this needs to specifically focus on the 'status' entity key.

timmillwood’s picture

Status: Needs review » Closed (duplicate)