Problem/Motivation
In #2810381: Add generic status field to ContentEntityBase we introduced EntityPublishedTrait, however this does not yet have a common interface.
Proposed resolution
- Add \Drupal\Core\Entity\EntityPublishedInterface
- Add a published
entity key
- Only add the base field via the trait if the entity implements the interface and has the entity key
- Update setPublished() to have an optional parameter (for backwards compatibility)
- Add setUnpublished() to unpublish entities.
- Update Node and Comment modules with interface and entity key changes.
- Add Node and Comment update tests
Remaining tasks
- Review
User interface changes
None
API changes
- New EntityPublishedInterface added and implemented by Node and Comment
- The parameter for setPublished() is now optional. No parameter will just set the entity as published, but the old TRUE or FALSE parameters will work as they always have for backwards compatibility. This parameter will be removed in 9.0.0.
Data model changes
No change, published state is still stored in the status field for Node and Comment as a 0 or 1 integer.
Comment | File | Size | Author |
---|---|---|---|
#118 | interdiff.txt | 2.3 KB | amateescu |
#118 | 2789315-117.patch | 15.74 KB | amateescu |
#108 | 2789315-97.patch | 15.34 KB | timmillwood |
#33 | 2789315-33.patch | 7.95 KB | amateescu |
#24 | interdiff.txt | 1.96 KB | timmillwood |
Comments
Comment #2
sandervd CreditAttribution: sandervd commentedComment #3
timmillwoodIn #2725463: WI: Add StatusItem field type to store archived state we're looking to overhaul the status field, but I don't think the case for this interface would change.
If anything I might suggest changing the interface to EntityStatusInterface, so we can add in there new things relating to the status field, but not relating to published.
Comment #4
catchUser also has a similar concept via 'created', 'active', 'blocked'. EntityStatusInterface would be more suited to that as well.
Comment #5
timmillwood@catch - right, but isPublished() and setPublished() wouldn't make much sense on users, would it?
Comment #6
catchIt wouldn't but something like getStatus() and setStatus() might?
Comment #7
sandervd CreditAttribution: sandervd commentedIt would indeed be a nice addition to include the UserInterface in here...
For the NodeInterface and the CommentInterface we can extract them without breaking the contract of the interface, as long as we keep the naming of 'isPublished' and 'setPublished'. For the user it is a more difficult as it would imply changing the interface.
Comment #8
sandervd CreditAttribution: sandervd commentedSettings to needs work as this needs further discussion...
Comment #9
sandervd CreditAttribution: sandervd commentedbackport of the initial patch to 8.1.x...
Comment #10
timmillwoodNo more 8.1.x releases are going to be made, and as this is not a bug it won't go in 8.2.x either, so switching to 8.3.x.
I still think the interface should be named
EntityStatusInterface
.Comment #11
pfrenssenI think also that this should be
EntityStatusInterface
with methodsgetStatus()
andsetStatus()
.I think the proper way to do this while retaining backwards compatibility, is to provide the new methods on the comment and node classes, and deprecate
isPublished()
andsetPublished()
, with a message that the new methods should be used instead.Comment #12
timmillwoodI accidentally opened a new issue for this #2811667: Introduce EntityPublishedInterface for content entities where I have contradicted my statements in this issues.
If the status field in Node and Comment are starting a boolean published / unpublished field I think we'd be better of with EntityPublishedInterface for them, then maybe for config entities we add EntityStatusInterface which is pretty much what's being discusses in #2009958: Introduce EntityStatusInterface to standardize how entities can be disabled.
Comment #13
timmillwoodPulling over the latest patch from #2811667: Introduce EntityPublishedInterface for content entities.
This patch defines EntityPublishedInterface to be used for all entity types wanting a published/unpublished status. It also creates generic constants rather than having separate ones currently in use by node and comment modules.
Comment #14
timmillwoodNow that #2810381: Add generic status field to ContentEntityBase has been committed we should get this in too.
Should
EntityPublishedTrait
implementEntityPublishedInterface
, or just the entity type class which is using the trait?Comment #15
catchentity's.
Does the STATUS_ prefix gain us anything?
I wonder a bit whether this should be function publish() and function unpublish() with no params. That's not how Node works now though.
Also the param is bool, but we define the constants as int - is a bit confusing. If we removed the param from the function, it'd at least partially reduce that confusion, although we still have the bool return value from isPublished() vs. the constants.
Comment #16
timmillwood1) Will update
2) In
CommentInterface
we already haveconst PUBLISHED
, I was getting override issues if we hadCommentInterface
andEntityPublishedInterface
both having the same constants.3) I didn't really want to change the existing functionality, just provide a common interface for it. I'm happy to add
publish()
andunpublish()
in addition to the traditionalsetPublished($published)
. Then with the bool vs. int, I think this is just a Drupalism we embrace until D9.Comment #17
pfrenssenComment #18
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented#16.2) Can't we just remove the constants from
CommentInterface
since it will inherit them from the new interface?Comment #19
timmillwood@amateescu - Hrm... I suppose you're right.
Comment #20
timmillwoodFixes #15, updated based on EntityPublishedTrait, and trying suggestion from #18.
Comment #21
catchTraits can't implement interfaces, so it'll just be the entity type classes doing it as well as using the trait.
Comment #22
timmillwood@catch - Yes, I worked this out when I tried it. Still getting used to the OOP world.
Comment #23
pfrenssenI would rename this to:
The way it is currently worded I would expect to get back some kind of
PublicationStatusIndicator
object.Is this true under all circumstances? This is copied from the current implementation but this would not necessarily be true for all kinds of entities.
For example an entity might not have an author associated with it, but it might have a publication state.
Why is this type hint for
$entity_type
here? This variable is not defined in this scope.I think we should probably throw an exception if the status key is not defined, rather than just blindly overwriting the 'status' field. It's quite a remote possibility but this might cause data loss.
This is also a bit ambiguous. We are now making a clear difference between 'published' and 'status', but we are still relying on the entity key called 'status'. This will come back to bite us in #2009958: Introduce EntityStatusInterface to standardize how entities can be disabled.
This needs a bit more thought I guess.
Same here,
$entity_type
is not defined.Comment #24
timmillwoodFixed items from #23, apart from 4. In #2810381: Add generic status field to ContentEntityBase we introduced EntityPublishedTrait and after working it through with Plach we decided that publishing status fields may or may not have an entity key. For example Node does, but comment doesn't. so we support a 'status' entity key if there is one, otherwise we just just a field called 'status'.
Comment #25
Anonymous (not verified) CreditAttribution: Anonymous commentedThere's quite a lot of issues floating around the same point here:
#2811667: Introduce EntityPublishedInterface for content entities
#2810381: Add generic status field to ContentEntityBase
#2009958: Introduce EntityStatusInterface to standardize how entities can be disabled
At risk of repeating myself (#2009958-14: Introduce EntityStatusInterface to standardize how entities can be disabled and #2811667-22: Introduce EntityPublishedInterface for content entities), the interface should look like this, with no magic numbers exposed to the codebase:
And really if you were to do this using SOLID OO principals you would want to segregate that interface better for the client:
So then the dependent service can be implemented with a semantically relevant interface, and there is still flexibility to completely change how "publish" and "unpublish" work in the domain.
You could put the constants on the trait with the implementation, if you wanted to; but try to keep the internals of the class that use them guarded, or it won't be possible to refactor the implementation without worrying about BC breaks.
Comment #26
pfrenssenI quite like the suggestion of removing
setPublished()
and the constants from the interface.publish()
andunpublish()
are sufficient.I wouldn't split it in separate interfaces though, that kind of granularity seems overkill to me.
Comment #27
timmillwood#2009958: Introduce EntityStatusInterface to standardize how entities can be disabled is a very different issue and relates to the status field on config entities. Here we're looking at the publishing status on content entities.
I don't think we need two interfaces for publish and unpublish.
setPublished()
is heavily in use, and is often good if we want to set the publishing status based on the result of something.$entity->setPublished($published);
is nicer than$published ? $entity->publish() : $entity->unpublish();
in my opinion.I'm very happy with the code in #24.
Comment #28
Anonymous (not verified) CreditAttribution: Anonymous commentedOkay consider this; what is stopping you from doing any of the following?
You can argue common sense, but you're making an assumption that the end user has that, and at the end of the day the API is still letting you make mistakes like this. Good domain design maintains complete control of the internal workings. Unfortunately, only PHP 7 will let you type-hint primitive types, if you wanted to expose the method as
setPublished(bool $published);
; you could implement a type check in that method, but what happens later on when it is decided that core is going to have a few more published states, as implied by the other tickets? Down the line, when someone decides to add an 'archived' state, the API needs to undergo changes in order to make this possible.The interface I had suggested above however, is flexible enough to accommodate changes. All you need to do is add the method
archive()
to the interface, and you could still completely replace the underlying implementation without deprecating, or changing any methods. Similar forneedsReview()
andneedsRevising()
, as other examples.Regarding interface segregation, it's fair to say that not a lot of OO programmers do this, but it does have it's advantages. It really helps highlight design flaws, and makes unit testing a walk in the park. Take the rough example code above, looking at the EntityPublishableInterface, there's 1 method on it, I know that my EntityPublisher service's command method has a dependency on that; therefore I know, without looking at any implementation, that my unit test must assert that
publish()
is invoked on the dependency. I'm trying to encourage this a bit more, as I've seen interfaces with some 40 methods on them. This is a problem because there's no way that any client using them as a dependency would need to (or should) invoke 40 methods on it. We need to remember that the interface isn't for the implementation, it's for the client itself, and should be appropriately named towards what the client expects to do with that dependency.To take your concern, it would probably be a lot clearer to the reader if you were to write that example as the following:
If this is happening a lot, then it is probably a design problem that should be addressed with a service to handle that responsibility. I'm assuming that everywhere that is using setPublished() at the moment is actually depending on NodeInterface, in which case you could always leave it on there, and deprecate it later?
Comment #29
timmillwoodRight, common sense will stop people putting non-boolean things into setPublished(). Also note, we are not planning to remove the method, just not add it to this interface.
Also, there are no plans for the publishing status to be anything more than boolean. We did discuss it and decided it's not viable. So EntityPublishedInterface only ever needs to support publishing and unpublishing.
Rather than...
as you suggested,
would be a lot clearer.
Comment #30
Anonymous (not verified) CreditAttribution: Anonymous commentedI think you might be mistaking cleaner for clearer, less lines of code is not necessarily clearer what the implementation is doing. Either way, if you are confident that this interface will need to do nothing else in future, and would rather use
setPublished(bool $state)
, then havingpublish()
andunpublish()
as well is just providing you with another way of achieving the same thing, on the same interface; which is just going to add confusion for developers, and probably cause more problems than it's worth down the line.Comment #31
timmillwoodI'm not saying it's either / or, lets have the traditional
setPublished()
and the newpublish()
/unpublish()
.Comment #32
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI looked around the code base a bit and found out that from 32 usages of
setPublished()
only 4 actually set it based on an external value, so I think it's ok to keepsetPublished()
out of the new interface.I agree with #30 that having three setters would be suboptimal DX, and we also have to keep in mind that the new interface has to make sense on its own and that we shouldn't architect it based on past decisions that maybe made sense at some point :)
Comment #33
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedBased on the comment above, just moving things along a bit :)
Comment #34
pfrenssensetPublished()
is no longer on the interface, so we should replace the{@inheritdoc}
with the full docblock again.I mentioned this before in my review of comment #23, but I think I did not properly communicate what troubles me about this.
Using the
status
entity key here, and defaulting to an entity property$entity->status
with hard couple the concept of an entity being "published" with its "status".This conflicts with #2009958: Introduce EntityStatusInterface to standardize how entities can be disabled where we want to introduce a
EntityStatusInterface
.My concern is that if we go ahead here and hard couple publication state to this entity key, then we will not be able to do #2009958: Introduce EntityStatusInterface to standardize how entities can be disabled without introducing a B/C break, effectively condemning that issue to be postponed to D9.
I suggest we instead introduce the entity key
published
for this, and in the Node and Comment entity definitions we declare:This will ensure that we still will be able to get an
EntityStatusInterface
in a reasonable timeframe.Comment #35
Anonymous (not verified) CreditAttribution: Anonymous commentedEntityInterface::preSave() should probably have the current user, or a token object passed as a dependency, rather than using that \Drupal global god thing? Separate issue perhaps. On the same topic however: Entity::getEntityType() should really have a reference to this dependency injected in the constructor, rather than using the same thing to go and locate it's dependency.
Fixed #34:1, I would have thought that re:2 that since the API has exposed
public function set($property, $value);
then it might be a problem that needs addressing in general, since external code can mutate the properties on the object anyway using this method - does look like validation should be applied in TypedDataInterface::setValue() however. Either way I've cleaned up duplicate code for when it's decided which key is tracking the published status.Comment #36
Anonymous (not verified) CreditAttribution: Anonymous commentedComment #37
timmillwoodApart from making use of
getPublishedEntityKey()
I'm not sure this is the issue to change this (if it needs changing at all).Comment #38
timmillwoodupdating patch based on #37.
Comment #39
catchI like the latest version of the interface, nice and clear.
I wanted to check to what extent 'unpublish' is used. It's something we already use a lot, but further reinforced here. However I did a google and found this question from kiamlaluno on a site, and the answers were in favour of unpublish vs. withdraw/retract. http://english.stackexchange.com/questions/2208/does-the-verb-unpublish-...
So +1 to the patch as it currently is.
Comment #40
claudiu.cristeaNice API change. Some comments...
We usually document this as
@return $this
. See https://www.drupal.org/docs/develop/coding-standards/api-documentation-a....I don't see what docs is this inheriting, it has no parent. Revert?
s/self::/static::/ ?
This is correct but why not calling it as static s/$this->/static::/ ?
We should deprecate also this, I think.
Missing new line between @return and @see.
Hm, I don't like this. Why 'status' for free? If an entity type is implementing this interface and misses a 'status' key then it's a malformed entity type, right? So, we need to throw something here.
Unused statements.
Comment #41
timmillwoodResolved everything apart from 7.
Not all entity types use, or should use a 'status' entity key. Take the current core examples of Node and Comment entities. Node has a 'status' entity key, and Comment doesn't. When working on #2810381: Add generic status field to ContentEntityBase Plach questioned the entity key, because I originally planned to add it to all entity types that want to use the 'status' field, but it seems there is no need. Therefore with this trait, if there is an entity key we use it, if not it's assumed the fields is 'status'. Hopefully the base field from the trait is used too, and not just the other methods in the trait.
Comment #42
claudiu.cristeaWhy? I see that entity types should be very carefully defined with all the keys in place. I don't understand this exception. If we miss the "id" key from the definition, does it have such a hard-coded fallback? (if it has, it's wrong too)
I see now that @pfrenssen raised the same concern. Let's wait for more input on this.
Comment #43
claudiu.cristeaDiscussing with @timmillwood on IRC. It's true that we should keep the hard-coded 'status' for assuring BC. If a custom entity type didn't add the key, relying on this hard-coded fallback, probably it will crash. For this reason we should keep that but add a @todo to be removed in D9?
Comment #44
timmillwoodI think comparing this to the entity_key for 'id' is a little different, as 'id' is set in ContentEntityBase, where as this is in a trait. This is more similar to \Drupal\Core\Entity\RevisionLogEntityTrait where all the field keys are hard-coded. Although there is an issue open to define these keys in the entity annotation, not using entity_keys, but another property.
Comment #45
Anonymous (not verified) CreditAttribution: Anonymous commented@#37 I just thought it made it a bit clearer that it was returning an array, but it doesn't need changing.
@#39 I had originally suggested retract, but agree that creating a difference in terminology between DX and UX will probably cause issues. Even though the word 'unpublish' isn't actually a word, it is used consistently by CMS users, and the DX should reflect that.
Regarding the status key, since the 'entity keys' are used by the field API, is the 'status' or publication state of an entity not misplaced here?
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21...
If the Drupal ORM will map the property directly to a column in the database, then surely it should use it's own column in order to actually be portable, in the form of a trait?
I'm not sure if the ORM is flexible enough to allow you to specify a property that maps to a different column in the database; at a glance, it doesn't seem to be. This would allow you to set $entity->published/$entity->set('published', ...) property in the trait, with that functionality contained properly; and then map that to the status column for nodes and comments in the ORM definitions (FieldableEntityInterface::baseFieldDefinitions()). The difficulty is that because the API isn't enforced, you need to check that there is no use of $node/$comment->set('status', ...) outside of the entity in order to replace them with correct use of the API here, this is probably a BC break as well for anyone using $node/$comment->get('status').
As I touched on before, I think that public get() and set() will become an irreversible architectural decision, if it isn't already. The problem is that having that API (public) is contradictory to using domain methods, as any external code can just go ahead and change the value anyway, without calling publish() or unpublish() in this example. The danger is that now you can do any of:
So you now have at least 2 different ways of achieving the same thing in the codebase, for getting and setting anything, on any entity. Even properties that aren't considered mutable can be manipulated like this (without overriding the API.) The logical solution to me is that set() and get() are protected/internal for the entity, to allow leveraging the field definitions and constraint validation, but without giving too much away. Without removing this, it's going to cause a maintenance nightmare when trying to refactor the API, and affects every entity.
Comment #46
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedDiscussed the 'status' entity key problem briefly in IRC today and both @catch and @FabianX were in favor of introducing a 'published' entity key.
Here's how it would look like.
@GroovyCarrot, the generic public
set()
andget()
methods are needed for configurable fields, which can not have dedicated methods on the entity interfaces.Comment #48
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedTo be honest, I have no idea why using the 'status' field as an entity key in Comment would impact the output of HAL+JSON vs. JSON in the rest module, but this fixes the test.
Comment #49
BerdirThis is a BC breaking change for someone whoal already used this trait. But it's against trait that is new in 8.3.x and was never part of a release.
I *suppose* that is OK, no idea?
I was following the discussion around this a but to be honest, I don't fully understand it. Yes, status can mean something other than published, especially for config entities. But having a published or not "status" and another status seems more than unlikely? And if you do have that, you can always not use this trait?
Here we deprecate this but I suppose what we actually deprecate is the existing method on Node and Comment?
naming nitpick: invalid is something that I'd expect we throw on discovery when the definition is.. invalid. the way it is used feels more like incomplete/unsupported entity type definition. but that's pretty minor.
wondering if we should deprecate those constants as well as it is now officially a boolean value? happy to discuss that in a follow-up issue.
this is strange, I think we can't just remove this without at least knowing what is happening?
Comment #50
catchYes that's OK, we could also have rolled back the trait entirely before 8.3.0.
File entities are a particularly strange one, that in theory at least could have a concept of being published vs. not. Users have a status key which isn't the same as published, but don't see us adding published.
I could see us renaming the schema for node etc. to use 'published' instead of 'status' in 9.x so it all lines up (i.e. we'd somehow deprecate direct calls to set('status'), not sure how to do a forward compatible change for entity queries but it could be mapped in an alter or something), in that case having published as the entity key helps, I see the ambivalence towards it now.
#2 yes with with the trait itself being new, we should remove that method rather than deprecate it, and just deprecate the node/comment methods instead.
Comment #51
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThanks for reviewing :)
Comment #52
BerdirAbout the status change:
First, the previous issue didn't add status to read only fields, it moved it. Because we now define it earlier up, the access check for it happens first. And the read only fields must be in that order, as it checks them one by one to figure out if you really don't have access. So it encountered the status first.
And here's what's happen with the status, this piece of cause is the "problem":
status is an entity key (btw, the above assumption is bogus, entity keys are *not* "what uniquely identify the entity") now, so it is checked as "did the value change or not", and if not, then we skip it and don't check access. Weird, but that's what's happening ;)
So because don't actually try to *change* the status, it is no longer access denied but silently ignored.
Comment #53
Wim Leers#46 + #48:
There is a very good reason for this. In
PATCH
request, one should send the fields one wants to change. But of course one cannot change entity keys: you cannot change the entity ID for example. However, there's an edge case: some entity keys must be sent for an entity to be denormalizable at all: the bundle field! So, entity keys are whitelisted since #2631774: Impossible to update Comment entity with REST (HTTP PATCH): bundle field not allowed to be updated, but EntityNormalizer::denormalize() requires it: they are allowed to be sent, but they must NOT be changed: they must match the currently stored value.So, if you're making a field that was not an entity key into an entity key, that means that if the field access check for that field did not allow changes before, then it won't complain anymore this time around. That's what is happening.
But why is this being made into an entity key?
#52: hah, only after having written all of the above do I see that you've already basically answered this. But, at least I was able to explain why it behaves this way.
Interesting! So what are entity keys for then? I'm not able to find relevant docs.
Oh, found it:
Wow, that is super confusing. Very bad name? Even more so because it is then followed by:
So then what is "key" about these keys?
Comment #54
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedYou can change entity keys as much as you want actually. What you should be looking at is the read-only property of a field definition (i.e.
$field_definition->isReadOnly()
) ;)Edit:
In order to bring consistency into how
EntityPublishedTrait
defines the name of the 'status' field.Comment #55
Wim LeersDiscussed this a bit with berdir in IRC:
… and because of this, this means that what
\Drupal\rest\Plugin\rest\resource\EntityResource::patch()
does is wrong, and so the changes in #2631774: Impossible to update Comment entity with REST (HTTP PATCH): bundle field not allowed to be updated, but EntityNormalizer::denormalize() requires it were wrong.This means it won't be possible anymore to modify the
status
field of aComment
entity via REST. This needs to be fixed before this issue can get in. Created a separate issue: #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior.Comment #56
Wim Leersd.o, you're stupid.
Comment #57
Wim LeersSo d.o ate my
title
change and decided to change thestatus
of this node. I definitely did not touch that. d.o has an appropriately sarcastic on-topic sense of humor!Republished.
Comment #58
Wim LeersBerdir is right.
But then I'd like to see this add test coverage to prove that it's still possible to change a comment's status.
Comment #59
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedDon't we have a dedicated issue for that? #2737719: EntityResource: Provide comprehensive test coverage: for every entity type, every format, every method
Comment #60
Wim LeersThat issue is about a massive testing overhaul. This would indeed eventually end up in there, as a special case to test for Comment entities. But that patch is not even RTBC yet. For now, this patch would need to expand the test coverage in
Drupal\rest\Tests\UpdateTest::testUpdateComment()
.Comment #61
timmillwoodThis issue is currently not blocked so we are looking for reviews of #51.
We have been through a number of iterations, therefore it should be in a pretty good place.
Comment #62
Wim LeersWell, this change is wrong.
If this change were correct, then we'd also see this change in #2826101: Add a ReadOnly constraint validation and use it automatically for read-only entity fields.
So AFAICT it is blocked. :(
Comment #63
BerdirIt is the correct change as that is what rest.module currently does. We could add a @todo to that issue but I agree that we don't need to block on that.
Comment #64
Wim LeersNo, it is wrong.
The test change proves that this is a BC-breaking regression: with this patch committed, a different (incorrect) response is returned for
PATCH /comment/CID
requests whose request body includes thestatus
field set to the same value as the current value when the user does not have theadminister comments
permission. This will then later change again, once #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior lands: that will restore currentHEAD
's behavior.If this lands now, there is no guarantee that #2824851 will also land before 8.3. Hence we might ship with the behavior introduced in this patch in 8.3, and then change it again in 8.4. That's why this affects BC. That's why this needs to be blocked.
Committing this means Drupal 8 is API-second, not API-first.
Comment #65
claudiu.cristeaNice. This will be helpful also in other places.
Both updates need testing. As they are separate modules, I'm afraid that we need two tests.
Comment #66
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedWhat stops #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior from being committed to 8.3.x? It is marked as a major bug as far as I see.
Thanks for the drama, I now have 0 (zero) intentions on working on that issue anymore.
Comment #67
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented@claudiu.cristea, thanks for reviewing and good point, added two update tests :)
Comment #68
claudiu.cristeaNit
I think it should be drupal-8-rc1.bare.standard.php.gz. That is newer.
Comment #69
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThat's true, but it doesn't really matter for the purpose of our test..
Edit: I'll update it if there's any more feedback to address, I don't like patch spamming :)
Comment #70
Wim Leers#66:
Comment #71
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedOk, no other feedback for a week, so let's move forward here :)
Here's the change requested in #68.
Comment #72
claudiu.cristeaFrom my point of view this is RTBC. I will let core committers to make the final decision. RTBC if green!
Comment #76
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedBack to RTBC after a round of random fails.
Comment #77
twistor CreditAttribution: twistor as a volunteer commentedShould this be \LogicException or at least something more specific?
Comment #79
claudiu.cristeaUnrelated failures.
Comment #81
timmillwoodComment #82
catchWhy is_subclass_of instead of instanceof here?
Looking at this, the publish()/unpublish() methods look to me like you don't need to also call save(). I think we should do setPublished()/setUnpublished() so it's more clear that these are setters/getters. Sorry for not catching that earlier. Just a rename, it's still nice to not have to use the bool constants.
Comment #83
Berdirinstanceof needs an object, $entity_type is the annotation object that returns the entity class name. That's why we added isSubclassOf() but that is equally confusing: #2194783: Rename EntityTypeInterface::isSubclassOf() to ::entityClassImplements().
Comment #84
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThe problem with
setPublished()
is that it conflicts with the existingsetPublished($published)
from NodeInterface and CommentInterface :/Comment #85
catchOK I was stumped for a while, but have two ideas which might be viable:
1. The bc policy allows us to change the API for interfaces for implementers not callers.
We could therefore potentially remove the argument from NodeInterface, but have Node itself do a func_get_args() and handle it. That would be no meaningful break for anyone, unless someone has directly implemented NodeInterface without subclassing Node (and it'd be a very small change even if they did).
2. To have no bc break at all, we could keep the bool argument to setPublished() in the new interface but immediately deprecate it.
So EntityPublishedInterface::setPublished($published = NULL). The trait can then handle the boolean if it's set, but also do @trigger_error('...', E_USER_DEPRECATED) etc. then we remove the argument in 9.x.
Comment #86
catchThought more about the 8.x-9.x bc break with #85.2. Because of the trait, as long as implementers of the interface use the trait, then we can remove the param from the interface + trait in 9.0.0, and any module using the trait neither has to update a method signature nor any use statements - so it should be a completely forward-compatible fix. Option #1 would be better for direct implementors of EntityPublishedInterface but even if you did that, you could still update to use the trait when 9.x is out and have cross-compatibility.
Comment #87
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI have to say that after coding #85.2, it actually looks pretty good and reasonable :)
Comment #88
claudiu.cristeaNice. Much more better.
Nits
Then
@param bool|null $published
(optional and deprecated)
hm, I have no better idea :)"...removed in 9.0.x" or "...removed before 9.0.0".
+1. Love it! We should do trigger_error() with E_USER_DEPRECATED everywhere we are deprecating.
Comment #89
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedThanks for reviewing :)
#88.1: Your comment is correct, but I'm not sure if we want to advertise the usage of NULL, that's why I didn't write it like that in the first place.
.2: Yeah.. me neither :)
.3: Both "...removed in 9.0.x" or "...removed before 9.0.0" are not actually true, this parameter will be removed exactly in 9.0.0 and it's also consistent with other 9.0.0 deprecation messages.
.4: Yup, we already do it a lot in other places.
Comment #92
claudiu.cristeaWell, using NULL is better than a boolean :)
I wonder how we'll do that? I still think it's not correct. You cannot deprecate exactly in 9.0.0, it will be deprecated during the dev cycle and that is before 9.0.0. "...removed in 9.0.x" or "...removed before 9.0.0" are the most used for the same reasons.
The failure seems unrelated.
Comment #93
catchYes I think removed before 9.0.0 is most accurate - there'll be beta/rc releases before 9.0.0 is tagged etc.
Comment #94
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedOk then :)
Comment #95
claudiu.cristeaHope we will not be visited anymore by random failures :)
Comment #96
catchPatch looks great, and doing it this way actually looks a lot less bad than I thought it would when suggesting it :)
One question - should we explicitly point people to using setUnpublished() for unpublishing in the deprecation message or is it obvious enough?
Comment #97
claudiu.cristeaVoilà!
Comment #99
claudiu.cristeaComment #101
claudiu.cristeaComment #102
xjmI chatted with @catch about this yesterday and I also think this approach is a good one. Let's add a change record.
One question, how do we ensure that we actually do the removal for 9.0.0 when there is no @deprecated tag?
Comment #103
claudiu.cristeaWe cannot add a @deprectaed tag but we can add, at least, a @todo.
Draft change notice: https://www.drupal.org/node/2830201
Comment #104
catch@xjm on #102 we can grep for E_USER_DEPRECATED and remove all of those as well, also #2488860: Bring phpunit bridge into drupal and use it for unit tests and simpletest to handle Deprecation should help with this.
Comment #105
timmillwoodDo we wanna open an issue against 9.x-dev for removing that, and add the url to the @todo?
Comment #106
catch#2716163: [META] Remove deprecated classes, methods, procedural functions and code paths outside of deprecated modules on the Drupal 9 branch is open already - to be honest I think that's plenty and we don't need the @todo. If we open 9.x and we haven't sorted out how to remove all deprecated method parameters by then, we shouldn't be opening it anyway.
Comment #107
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedAgreed with @catch that we can remove the @todo, we will be grepping the entire codebase for "deprecated" anyway when we start the clean-up process.
The change record looks good, thanks @claudiu.cristea!
Comment #108
timmillwoodIf we're removing the @todo then the patch from #97 is what's needed. Here's a re-upload.
Comment #109
claudiu.cristeaComment #110
timmillwoodUpdated issue summary ready for core committer review.
Comment #111
alexpottCommitted d2af116 and pushed to 8.3.x. Thanks!
When I first saw this change I pondered why we weren't still supporting the status key in EntityPublishedTrait since it was before. But EntityPublishedTrait was added on 20 nov 2016 in #2810381: Add generic status field to ContentEntityBase so has never been official API because it is yet to be released. Therefore I think these changes are fine.
Love it
Yay for upgrade path testing.
index 33b99c6..d52f04d 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -8,7 +8,6 @@
use Drupal\comment\CommentInterface;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityPublishedTrait;
-use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
Fixed unused use on commit.
Comment #114
alexpottI prematurely committed this. I missed the discussion about the rest change and @Wim Leers feedback. I agree with him that we should be addressing #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior first to prevent behaviour changes.
Setting to RTBC to mirror previous status but really I think it should be postponed.
Comment #116
BerdirSo, here's an idea. I'm now sure how quickly #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior will happen and we have very long nested issue dependencies, anything we can get in and put behind us helps.
What if we special case the status key with a @todo to that issue above in rest.module? Then we do not cause a change here for comment* and we can do it properly without yet another change for comment*.
* Yes, for comment. Because here is the problem: The very argument that Wim made here to not commit this issue yet (it will cause a very-minor change for REST clients) is something that will happen with #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior, on any not-really-read-only entity field.
And since node explicitly doesn't check for status being protected right now, adding a special case for the status key in general will in turn make the new node tests fail (we have no existing test for that for node I think). And if we make it comment specific, just to keep the tests happy, then any other entity that starts to use this will be hit that change twice like comment would.
Comment #118
amateescu CreditAttribution: amateescu for Pfizer, Inc. commented@Berdir, that's a very good suggestion, it allows us to move forward here and leave #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior to handle the mess introduced in #2631774: Impossible to update Comment entity with REST (HTTP PATCH): bundle field not allowed to be updated, but EntityNormalizer::denormalize() requires it.
This new patch does not modify any REST test case, so the argument from #64 that were blocking a commit is not valid anymore.
Comment #119
Wim LeersThe code in
EntityResource::patch()
in HEAD is terrible, due to oversights/flaws in the Entity API.The above hunk in this patch is making the terrible code in
EntityResource::patch()
more terrible.That's IMHO a fine strategy to keep this unblocked!
Comment #120
Wim LeersNo.
There's a very important difference.
This patch would introduce a behavior change. Then #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior would change it again. It's becoming very clear that fixing #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior will be very, very difficult. Because Entity API's semantics are not well-defined in those areas. So it's quite likely 8.3 would ship without #2824851. At which point 8.3 would have the behavior introduced by this patch, and then 8.4 could get the fixed behavior of #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior.
What is repeatedly being forgotten here is that
status
is not and had never been a read-only field in the sense of "set and forget" (i.e.\Drupal\Core\TypedData\DataDefinitionInterface::isReadOnly() === TRUE
. It's only read-only in the sense that only users with theadminister comments
permission are allowed to modify it. That's the first thing that\Drupal\comment\CommentAccessControlHandler::checkFieldAccess()
does. So #2824851: EntityResource::patch() makes an incorrect assumption about entity keys, hence results in incorrect behavior will not cause the same REST test change as the one that was being made until #118 worked around it in a different way.Comment #122
Wim LeersRandom fail in
OutsideInBlockFormTest
, which we thought was fixed earlier today in #2830485: \Drupal\Tests\outside_in\FunctionalJavascript\OutsideInBlockFormTest fails randomly. Reopened that.Somebody already queued this patch for re-testing, hopefully green again.
Comment #123
alexpottLet's trying committing again now that we have no unexpected behaviour changes.
Committed 67c7387 and pushed to 8.3.x. Thanks!
Comment #125
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedSmall performance improvement followup: #2834291: Add a SQL index for entity types that are using EntityPublishedInterface
Comment #127
timmillwood#2846830: Add changelog for Drupal 8.3.0
Comment #128
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedWe forgot to publish the change record for this issue. Just did that now :)