For moving on with #1374030: Remove entity_extract_ids() now that we have proper classed entity objects we'll need at least have an access for the revision id.

1) Terminology. I'd vote for going with "versions" instead of "revisions", as it's the generally used term.

2) Should we go with a separate EntityVersionInterface (or so), or just add version related methods to the EntityInterface?

Whatever variant we choose, I think it's important that an entity can be easily made versionable by an contrib extension, i.e. making comments versionable should not depend on the Comment class already implementing something.

Variant a)
I like the idea of clearly separating entity version objects from the "real" entity objects, thus have an EntityVersionInterface() alongside with an EntityVersion class, which is used for non-live entity versions. Taken the node example, we'd have a NodeVersion class implementing the EntityVersionInterface. But when we have a NodeVersion extend Node, we'd have to manually implement all EntityVersionInterface methods - ouch.

Variant b)
No separate classes, just extend the Entity class and interface with useful methods like getVersionId(), isCurrentVersion(). Those methods would have to work even if the entity is not versionable, e.g. getVersionId() would return NULL as entity_extract_ids() does now.

Variant c)
Like b), but don't bake the version related methods into the EntityInterface. Instead add a EntityVersionableInterface which adds the further methods. If we've Entity implementing the EntityVersionableInterface it would be still simple to make e.g. comments versionable in contrib. But it looks odd to have Comment implementing EntityVersionableInterface even it's not versionable yet.

Personally, I tend to think variant b) is best, as it seems to be the straight-forward approach to me.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

aspilicious’s picture

With the summary from above I do like (b), alhough I'm biased because I suggested something like that in the linked issue. This can be a fairly simple patch.

Any objections against b?

Berdir’s picture

1) After looking at the exact meaning/german translation of revision/version, I agree that it might make sense to use version.

b) sounds good to me as well. Maybe we can at some point (9.x), when we can rely on traits, revisit a) as it would be easy to include default implementations of the revision methods as a trait.

I guess to replace entity_extract_ids(), it's enough to being able to extract the current revision id, which this issue could do, a entity-generic revision implementation can be a follow-up. So this should be rather trivial?

aspilicious’s picture

Status: Active » Needs review
FileSize
2.62 KB

I need more help for the node_revision stuff. How do you implement that method?

aspilicious’s picture

FileSize
3.52 KB

Probably not there yet and we need some tests for this. Thats everything I can do. The build query stuff looks a bit like black magic :)

Berdir’s picture

Fetching the current revision id in buildQuery() was my suggestion, it doesn't cost us anything (well, a tiny but, but imho not a problem).

A function that currently does a is current revision check is node_delete_revision(). And it does a second node_load() to figure it out. I think we could already replace that check with a call to isCurrentVersion() to have some real usage of the new API.

Agreed that we need some tests here but other than that, this looks quite ok to me.

aspilicious’s picture

FileSize
5.1 KB

New try with tests.

When creating the tests I noticed that when doing a normal node_load without a vid key that node version id is always the current version id. I discussed this with Berder and we concluded this is ok.

<berdir> aspilicious: but you have old objects
<berdir> aspilicious: at the time the object was loaded, it *was* the active revision
...
<berdir> aspilicious: if you change the label but call $node->labe(), it is outdated
..
<berdir> aspilicious: you have the concurrency problem anyway, unless you do locking
<berdir> aspilicious: another process could change it between your check and the action that you are doing based on that check
Berdir’s picture

Status: Needs review » Needs work

To clarify this a bit, $nodes contained the original node objects after they were created. So current_vid always equals vid, because by the time it was loaded, it *was* the current vid.

IMHO, that behavior is ok, because it's the same for all method calls. If the second revision would change the label, the first object would still return the old label when calling $node->label(), so that's why we need to load them again.

Also, what we need to do, I guess in NodeStorageController for now, is making sure that current_vid/vid are correct after doing a save. Right now, if you would do a $entity->save() either for the first time or if a new revision is created, it would return FALSE.

+++ b/core/modules/node/lib/Drupal/node/Tests/NodeRevisionsTest.phpundefined
@@ -75,6 +75,9 @@ class NodeRevisionsTest extends NodeTestBase {
 
+    // Confirm that this is the current version
+    $this->assertTrue($node->isCurrentVersion(), 'Third node revision is the active one.');
+
     // Confirm that revisions revert properly.
     $this->drupalPost("node/$node->nid/revisions/{$nodes[1]->vid}/revert", array(), t('Revert'));
     $this->assertRaw(t('@type %title has been reverted back to the revision from %revision-date.',
@@ -83,6 +86,10 @@ class NodeRevisionsTest extends NodeTestBase {

@@ -83,6 +86,10 @@ class NodeRevisionsTest extends NodeTestBase {
     $reverted_node = node_load($node->nid);
     $this->assertTrue(($nodes[1]->body[LANGUAGE_NOT_SPECIFIED][0]['value'] == $reverted_node->body[LANGUAGE_NOT_SPECIFIED][0]['value']), t('Node reverted correctly.'));
 
+    //Confirm that this is not the current version

Second comment needs a space, both need a . :)

Berdir’s picture

Another idea:

If we make the current version id property name static (e.g. current_version_id), then we could have a default implementation that returns TRUE if there is no revision key, and if not, compares $this->getVersionid() to $this->current_version_id. Then we don't need a custom implementation of that for every entity that supports revisions.

Thinking about that, if we go with the revision => version rename, we also need to update the corresponding hook_entity_info() properties. Can of course happen in a follow up, or while writing a generic Version save/delete implementation.

fago’s picture

Status: Needs work » Needs review

When creating the tests I noticed that when doing a normal node_load without a vid key that node version id is always the current version id. I discussed this with Berder and we concluded this is ok.

Yep, from loading we know whether we loaded the current version or not. So why not just set a protected "isCurrent" property and we are done? Just reflecting the information state of the entity-load time should be fine - the whole entity reflects just that state anyways.

Berdir’s picture

Ok, here's a re-roll.

- Fixed the comments.
- Went back to getRevisionId(). I agree that version is the better term but it makes *no* sense to have getVersionId() and 142 references to revision in *entity.module alone*. Changing all of them would make the page *huge*, which is not necessary at this point IMHO.
- Updated the sql magic to an expression that automatically sets isCurrentRevision.
- See @todo in NodeStorageController, is it really so that saving a new revision always forcefully makes this the active one. I thought that in 7.x contrib it's possible to have newer, non-active revisions. Do they reset the current revision after save or did we introduce a regression there?

catch’s picture

Revision munging in D7 regressed compared to D6, there's some background at #1184318: Avoid using vid munging for pending revisions. However we should worry about that in #218755: Support revisions in different states I think.

Berdir’s picture

Status: Needs review » Needs work

The last submitted patch, version-id-interface-1612014-10.patch, failed testing.

Berdir’s picture

Status: Needs work » Needs review
FileSize
5.31 KB

Simple re-roll, git++.

fago’s picture

Status: Needs review » Needs work

- Went back to getRevisionId(). I agree that version is the better term but it makes *no* sense to have getVersionId() and 142 references to revision in *entity.module alone*. Changing all of them would make the page *huge*, which is not necessary at this point IMHO.

Indeed. I guess doing the version-revision change in a separate issue makes sense anyway.

+++ b/core/modules/entity/lib/Drupal/entity/EntityInterface.php
@@ -182,4 +182,22 @@ interface EntityInterface {
+   * Checks if this entity is the active revision.
+   *
+   * @return bool
+   *   TRUE if the entity is the active revision, FALSE otherwise.
+   */
+  public function isCurrentRevision();

active or current? We should use a consistent wording here. Same in the tests.

+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -149,6 +149,10 @@ class NodeStorageController extends DatabaseStorageController {
+    // @todo: According to the above line, a newly saved revision is always the
+    //        current one. Is that true?

Yes, afaik workbench works around that by re-saving the non-latest current revision :D

Berdir’s picture

Status: Needs work » Needs review
FileSize
2.65 KB
5.24 KB

Changed all comments to current and replaced the @todo with a comment that explains what it does.

aspilicious’s picture

stevector’s picture

As catch mentioned in #11, the patch in #218755: Support revisions in different states deals with similar functionality and uses slightly different property names. Currently that patch is dealing directly with an "isLive" property. I like that the interface here uses methods to determine isCurrentRevision(). Perhaps there needs to be a separate method for "MakeCurrentRevision()" for use in entity saving. For instance, if a user loads an old revision into an edit form, makes changes, and wants to make those changes live/current, then isCurrentRevision() and MakeCurrentRevision() would return FALSE and TRUE respectively until the actual database writing happens. Does that make sense?

aspilicious’s picture

There is no point in discussion the same thing in two issues. Can we push this is and revise it in one issue if needed?

aspilicious’s picture

Issue tags: +Entity system, +sprint

tags...

fago’s picture

Status: Needs review » Reviewed & tested by the community

Patch looks good and comes with some basic test coverage.

There is no point in discussion the same thing in two issues. Can we push this is and revise it in one issue if needed?

Yep, let's move on with this and take care of the "making a revision live" stuff in the other issue. Let's handle the current vs live term discussion as follow-up just as revision vs version. Changing the respective code afterwards should be straight-forward and that way we don't further postpone other cleanup work on this (it's required by #1374030: Remove entity_extract_ids() now that we have proper classed entity objects).

fago’s picture

Status: Reviewed & tested by the community » Needs review
fago’s picture

Status: Needs review » Reviewed & tested by the community
Dries’s picture

Status: Reviewed & tested by the community » Fixed

I reviewed this patch and it is a simple and handy extension to the API. Committed to 8.x. Thanks.

Status: Fixed » Closed (fixed)

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

fago’s picture

Issue tags: -sprint

Removing sprint tag.