Closed (fixed)
Project:
Drupal core
Version:
8.7.x-dev
Component:
layout_builder.module
Priority:
Major
Category:
Task
Assigned:
Unassigned
Reporter:
Created:
16 Jan 2019 at 22:03 UTC
Updated:
14 Sep 2023 at 00:32 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
tedbowFirst try at it
Comment #4
tedbowI made this change so to agnostic about storage. So if a storage says it is applicable then it is compatible with Layout Builder.
But I was passing the entity's display as 'display' context without checking to see if it was a
LayoutBuilderEntityViewDisplayinstance and this caused a fatal error in\Drupal\layout_builder\Plugin\SectionStorage\DefaultsSectionStorage::isApplicable()not sure if we need extra checking inside that function because another storage type could also use the 'display' context but with different type of entity.but that would be a different issue
Comment #6
tedbowI didn't mean to already call
EntityContext::fromEntity()before theinstanceoftest.Comment #8
plachThis blocks a major so let's make it major as well :)
Comment #9
tedbowChanging the title slightly because the inline block logic is not totally storage agnostic.
We want to preserve the existing logic that when a new override is created from the defaults that we duplicate the inline blocks.
You can find the reasons #2957425-72: Allow the inline creation of non-reusable Custom Blocks in the layout builder(and in other comments in that issue)
Updating some comments and using dependency injection to get section storage manager. I also removed a unused var from the constructor in \Drupal\layout_builder\InlineBlockEntityOperations. This an internal class, not because it is experimental but because it should never be used outside of the module.
Comment #10
tim.plunkettCan we keep this helper method around and change it to make this exact comparison?
Instead of this @see, can this be an @todo linking to #3008943: Clean up todo in InlineBlockEntityOperations::handleEntityDelete()?
Not sure about this @see, but if you keep it, add the () to the end please
In theory I understand this check, but none of the code after it really cares if it is fieldable or not...
Let's use the interface here, not the class
Added twice
This should have an @todo to #3008924: Callers of LayoutEntityHelperTrait::getEntitySections() do not account for the view mode
Comment #11
tedbow$section_storage instanceof OverridesSectionStorageInterfacebecause that is what we really care about.Comment #13
tedbowTests failed because the constructor for this class was updated but the service definition didn't add the argument so it wasn't loaded.
The service is declared dynamically in
\Drupal\layout_builder\LayoutBuilderServiceProvider::registerfixed.
Comment #16
tedbowCanceled test on #13 to fix standards in #15
Diff from #11
Comment #17
tim.plunkettThanks for the changes!
A couple nits and one question
Nit: second line should be indented two spaces and end with a trailing space.
Nit: could be combined into one to cut down on nesting
Do we need to provide this method still? If so, could be an instanceof check like the one in
entityHasNewOverride()Comment #18
tedbowalso it gives the impression to a caller that they can rely on how the layout override is stored which I don't think we should imply.
This actually won't always give the same result as the current method.
the existing
$entity instanceof FieldableEntityInterface && $entity->hasField(OverridesSectionStorage::FIELD_NAME);won't always be the same as
$this->getSectionStorageForEntity($entity) instanceof OverridesSectionStorageInterfaceBecause they may have the field still the storage could be
DefaultsSectionStorageInterfaceif it has no sections.Comment #19
tim.plunkettMissed one of the @todos from #17.1 to fix
#18.3, good point.
Does this mean #2986403: Create an easy way to get layout sections for an entity. should be closed as a duplicate?
Comment #20
tedbowFixed the @todo
I think we can close #2986403: Create an easy way to get layout sections for an entity.
Unless the intention of that was provide help to custom or contrib code. But since that issue was not removing the @internal I think it is covered by the changes here.
Comment #22
tedbowI chatted with @tim.plunkett again about #17 3,
isEntityUsingFieldOverride(). While we are not using it is conceivable that custom code could be calling it even though it is internal.Adding it back as deprecated.
Comment #23
kristen polThanks for the update. Confirmed that isEntityUsingFieldOverride was added back in #22 which matches the interdiff. Is there any way to manually test this?
Comment #24
tedbow@Kristen Pol no this can't be tested because it the methods is not actually used anywhere. We just have to keep around because contrib might be calling it
Added a test for the method and the deprecation.
Comment #25
tim.plunkettGreat! Thanks for adding the deprecation and test.
One additional thing: deprecations are supposed to have CRs associated with them in the trigger_error message. I know this was only recently added but it was in 8.6.0 and it's on a trait.
Comment #26
tedbowAdded the change record https://www.drupal.org/node/3030609
Comment #27
tedbowAdded the change record to the deprecation
Comment #28
tim.plunkettThanks!
Comment #29
plachPatch looks good to me, just a few questions:
Shouldn't we remove the
$databaseconnection property and the related constructor param, while we are at this?Was the
$databaseparam unused?Why are we doing this here?
Would it make sense to move this into
LayoutEntityHelperTrait?Comment #30
tedbow\Drupal\layout_builder\InlineBlockEntityOperations::removeUnused().We wait till cron because an entity that has hundreds of revisions could have multiple unique inline blocks in each revision meaning thousands of inline blocks. So if deleting when the parent is deleted could run into a timeout.
Also because custom blocks also can have layouts and layout overrides and hence even though own inline block the deleting inline blocks when say a node is delete could cascade down many levels.
LayoutEntityHelperTraitbecause I don't want us to get into a situation like we are now with\Drupal\layout_builder\LayoutEntityHelperTrait::isEntityUsingFieldOverride()where we don't actually need it but we can't delete because of BC(even though since it is marked internal it seems like we should be able to.)In fact looking at this again I would prefer to make
entityHasNewOverrideprivate. If/when we need it outside of this class in the module we could move it to the trait.Comment #31
plachOk, thanks
Comment #32
tedbowre #30.4 making
\Drupal\layout_builder\InlineBlockEntityOperations::entityHasNewOverride()private this is internal logic that we should be to change later.Comment #33
tim.plunkettOh good point, let's not burn ourselves again
Comment #34
xjmSo:
We should preferably use
privatefor things that, architecturally, should be private, not to avoid providing BC. Increasing the visibility of something later is a disruptive change because it can cause method name collisions. So it's not a total freebie in terms of BC/disruption. (I discussed this with @plach and he was hesitant for policy reasons as well, i.e. our official core policy is still "we never use private, only protected" and we definitely don't have any sort of policy for usingprivateas a substitution for BC or deprecation.)Furthermore, regardless of that, we should have an
@todolinking an issue for what needs to be changed later if we think it needs to be changed later.Comment #35
tedbowIn #32.4 I was giving one reason for not moving this to the
LayoutEntityHelperTrait. This was probably colored by the fact that I just dealt writing the depreciation code for the other method on the class.There are other reasons why this is internal logic to the class and why other classes should not use this as an indicator of if an override is "new".
For this classes logic we want to:
So our logic really cares if
$entity->original(if available) use the default storage. Since Layout Builder is extensible system where modules could swap out parts I don't think we can always say this logic will mean it is "new override". That is another reason I didn't want to move it to the trait. I think it would have been confusing as a general helper method and the caller might have assumptions we are not thinking of.So maybe the method is badly named. I have renamed the method
originalEntityUsesDefaultStorage(). Since this a new method in this issue I think it is scope. I believe this is more clear as to what is actually being tested.As to @plach's original question in #29.4
I think moving the newly renamed function to the trait is fine because it very clear what is tested and we are not guarenting this a "new override" to the caller just about the storage off the original.
So here is patch that renames it, moves to the trait and makes it protected again. interdiff from 27 before it was made private.
Comment #36
tim.plunkettOh great, that makes much more sense, and no need for a follow-up now. +1 on the new name too!
Comment #37
xjmThis issue has two CRs that look to be about the same thing; which is preferred?
https://www.drupal.org/node/3030609
https://www.drupal.org/node/3028211
Comment #38
xjmI don't think removing the DB parameter is in scope, is it?
At least the typehints are different so it will cleanly fatal vs. having weird bugs, but we are moving away from constructor breaks in general even for "internalish" code. See #3030640: [policy, documentation] Clarify Constructor BC policy and document best practices for subclassing other classes.
Comment #39
xjmA related followup woulod be to explain the "why" in the
@internal. Is it because LB is experimental? Because subclassing this is a totally bad idea? Etc.Comment #40
xjm...Clarifying #38, the "more BC" way to change the constructor is to keep the DB service param, and make the new section storage param optional with a fallback to
\Drupal::service()that@trigger_error()s.Comment #41
tedbowI chatted with @xjm about this and since we also have an used $database parameter to the constructor(existing problem) there is not clear answer for what to do. For that reason I am not throwing @trigger_error because there is not a clear recommendation.
added follow up. #3031492: Remove @todo in InlineBlockEntityOperations constructor and also referenced #3030640: [policy, documentation] Clarify Constructor BC policy and document best practices for subclassing other classes
Comment #42
xjm"Optoinal" sounds like a non-Western musical scale. ;)
Also, nit: we should spell out the word "one" in a sentence. (I think the rule they taught me in grade school was to spell out numbers less than or equal to ten.)
Comment #44
tedbowI can neither confirm or deny 😜
fixed.
fixed
Comment #45
tedbowpointed out by @tim.plunkett
extra space "one totally"
Comment #46
tim.plunkettInterdiff looks good, all of the feedback since the last RTBC seems to have been addressed.
Comment #47
tim.plunkettComment #49
tim.plunkettDrupal\Tests\media\FunctionalJavascript\MediaStandardProfileTest random fail
Comment #50
xjmThe
@trigger_error()is missing here in theif.Also, we could get rid of the
elsefor easier future deprecation cleanup, by setting$section_storage_managerinstead in theif, and then just setting$this->sectionStorageManagerthereafter outside of any conditional.Comment #51
tedbowsee #41.2
I was hoping we could address this in the follow up #3031492: Remove @todo in InlineBlockEntityOperations constructor
otherwise would have to
trigger_errorwould have to give a recommendation now and but then the follow up might change the constructor signature again so we would have new recommendation.Was hoping we could just ask developers to update there code once. But I am not sure here
Fixed the other point in #50
Comment #52
tedbowI talked with @xjm and now understand what was asked for in #50.
Here is it is.
Comment #54
tedbowFixed standard nits
Comment #56
phenaproximaNit: I think this parameter description needs to be prefixed with
(optional).It looks like the call to $this->originalEntityUsesDefaultStorage() could be merged into the preceding if statement.
Should this type hint to LayoutEntityDisplayInterface instead?
Discussed in Slack with @tedbow and @xjm; Given the way Layout Builder works (it takes over the entity_view_display entity type) I'm not sure this instanceof check is actually needed. Although this also isn't really hurting anything, I don't see why we bother to check.
To remove yet another level of indentation, this can just be
return $original_section_storage instanceof DefaultsSectionStorageInterface.Any reason not to use assertSame() here?
Comment #57
tedbowComment #59
tedbowre #56.4
Now I remember why we need this. This migrate test fails without it. I don't think it is bad check to have.
A could swap this our for one entity type. Who knows why. Modules do strange things
Comment #60
phenaproximaBriefly discussed with @tedbow and @xjm and I think we are going to need explicit test coverage of the new trait methods too.
Comment #61
kristen polSince this needs work anyway, I noticed a couple tiny nitpicks.
Nitpick: One extra space before and, constructor, general, https. (3 spaces instead of 2)
Nitpick: Add comma before otherwise.
Comment #62
tim.plunkettHere's a WIP test
Comment #63
tedbow@tim.plunkett thanks for the WIP test.
I finished the test coverage for the trait. I used unit tests when I could and kernel tests where I had to.
Comment #64
tedbowwhoops name the patch wrong
Comment #66
tedbowOk this cause what seemed to be an unrelated test failure in
\Drupal\KernelTests\Core\Asset\ResolvedLibraryDefinitionsFilesMatchTest::setUp()but it turns out is related but just a problem with that kernel test. Let me explain....demo_umami_contentbecause that module makes demo content and requires tables that are not present in the kernel test.workspaces_install()creates a new workspace with the default uid of 1 if no other user is present.\Drupal\layout_builder\SectionStorage\SectionStorageManager::findByContext().There are a few ways we can solve this problem in the test
workspaceslikedemo_umami_contentbecause it also requires a table which is not present.Ensure thatdoesn't fix itlayout_builderis installed afterworkspacesso the hook_entity_presave is not present.$this->installEntitySchema('user');in the test before all modules are installed.This patch does 3) which I think is the best.
Comment #67
tim.plunkett#3035825: Make it easier to set up the current_user service in kernel tests will be installing the user entity schema in many many more tests, so I think that change is more than reasonable here.
Tests look good!
Comment #69
xjmOK, I didn't get through the whole patch (this is hard to grok!) but here are questions and feedback I had while reviewing it down to the tests:
@todos go at the bottom of the dockblock. Also, it's a little hard to read. How about:
I added a line at the end of the CR for this.
"Additionally
InlineBlockEntityOperationshas been updated to use the section storage manager, but this is unlikely to affect any extending code."What if the default storage only uses some of the inline blocks, or totally different ones? How do these get cleaned up? Is there a mechanism to clean them up manually?
How is this change a part of the patch? Why do we now need #3008943: Clean up todo in InlineBlockEntityOperations::handleEntityDelete() when we didn't before?
At first it looked like this line was exactly the same in both codepaths, but one is setting
$contexts['display]and the other is setting$contexts['entity'](and then$contexts['display']gets set later to something else). So this is correct, but an inline comment in each of theifand theelsemight be helpful to describe what they're doing.Typos here. It should say:
And then the "See" should be moved into an
@see.This could give a little more explanation.
So this method is essentially checking whether the updated entity:
In a nutshell, whether a new override layout is being created for the entity in this update operation.
The method name kind of confused me when I read it in calling code. I think there's already been discussion about what this could be called and I don't want to put too much Naming Things in here, but just flagging for now that it tripped me up reading the code.
So I would have expected this fallback in the constructor itself for BC, but it's here in the trait. Seems odd/wrong for a trait to have dependencies on services or be falling back to
\Drupal. Why is it on the trait?Apparently this hard-blocks a stable blocker, so tagging accordingly.
Thanks!
Comment #70
tedbow@xjm thanks for the review!
in that case we don't want to remove any inline blocks. They will still be used in the default.
This logic is effectively the same as what is in 8.7.x but was using the previous methods
When saving a LayoutBuilderEntityViewDisplay entity which is where the defaults are stored originalEntityUsesDefaultStorage() will not return TRUE so the inline blocks that are no longer referenced will be removed just after this by
isLayoutCompatibleEntity()was simply doing thisSo it only worked for by knowing about how both override and default section storages stored the sections which should be internal to the plugin. Also this method would never work for any other section storage plugins.
So it was changed to this
return $this->getSectionStorageForEntity($entity) !== NULL;which should work for any entity that has layout section storage and doesn't need to know any details about how any section storage stores their sections.The problem is that in
getSectionStorageForEntity()we pass the entity that was just deleted toreturn $this->sectionStorageManager()->findByContext($contexts, new CacheableMetadata());Hence the error from #3008943: Clean up todo in InlineBlockEntityOperations::handleEntityDelete(). After that is fixed we can call the new version of
isLayoutCompatibleEntity()without an error.Not calling
isLayoutCompatibleEntity()just means that we are calling\Drupal\layout_builder\InlineBlockUsage::removeByLayoutEntity()sometimes when we don't need to if we knew the entity was not layout compatible.But calling
removeByLayoutEntity()is not the end of the world since it would just update the any row in theinline_block_usagetable that match the entity type and entity id for the deleted entity. But since there should never be blocks that reference the non-compatible entities it will have not effect. Just a update we can save after #3008943: Clean up todo in InlineBlockEntityOperations::handleEntityDelete() is fixed.for instance
My comment would be
// If the entity is an instance of LayoutEntityDisplayInterface set it is has the 'display' context.I could extra information about how this is used by defaults section storage but from the section manager perspective this could used by other section storages we don't know about. So I don't want to then add back assumptions about certain section storage plugins if we don't have to.
I agree the diff is confusing but I am not sure the new code is confusing if you didn't know about the previous version.
I can add them if you still think it is needed.
I really really really don't want to rename this function
entityHasNewOverride()We need this function to determine if it is the original entity was using default as far as the inline blocks is concerned. This may not always be what is considered a "new override" especially after #2946333: Allow synced Layout override Translations: translating labels and inline blocks.
I explained my reasoning in #34 further.
I removed the internal comment that was left over and mentioned "new override".
I would also be happy to move this back to InlineBlockEntityOperations and off of LayoutEntityHelperTrait if we want to keep this solely for inline blocks logic.
Actually I remember..... It is on the trait because we can't be sure someone isn't already using the trait in another class and we can't update their constructor(even though our trait is @internal)
Comment #72
tedbow#71 was random fail in MediaStandardProfileTest 😢unrelated but would probably be fixed by #2901792: Disable all animations in Javascript testing
Comment #74
tedbowComment #75
tim.plunkettI believe #70 addresses all of the concerns from #69, and the changes look good. Setting back to RTBC.
Comment #77
tedbowI am not sure where the test failure is for #76. When I clicked the link https://www.drupal.org/pift-ci-job/1218142
It says all tests passed. the patch still applies and it looks like someone queued for retesting
Comment #79
alexpottThis looks really close - only a few minor nits.
My head hurts less. Nice.
We're slightly changing the return type here. I think an empty array makes more sense but then we need to update the docs to remove
|null. Also the docs have an @todo - is that done now?Should the NULL case be deprecated? I always find traits and services tricky.
That's funky. That's probably worth a follow to fix workspaces to not create a workspace when user does not exist.
I think we should document that we should document that classes using this trait are expected to set this.
Comment #80
tim.plunkett#79
2)
Fixed. Also, that todo points to an issue that this one duplicates. I'll close it.
3)
I have no problem leaving this as it is. I think deprecating is too prescriptive here, there's nothing actually wrong with the service call IMO.
4)
Opened #3039217: ResolvedLibraryDefinitionsFilesMatchTest needs to install the user entity schema
5)
Given my take on 3, I don't think this is needed. The only time this would matter is during unit testing.
Comment #81
phenaproximaNit: This could be a single (albeit very long) line.
Feedback looks pretty solidly addressed to me, and I agree that the logic surrounding originalEntityUsesDefaultStorage() is a lot cleaner. I only skimmed the tests, but the coverage looks (at a glance) comprehensive. Seeing as how this already seems to have @alexpott's blessing, I'm not sure there's much more for me to do here but RTBC this.
Comment #82
alexpottCommitted and pushed 09a6d81ec9 to 8.8.x and 70b46d5554 to 8.7.x. Thanks!
I'm not 100% convinced on the usage of \Drupal::service() in traits but this trait is far from alone and service location is a massive issue and fixing the way we do it far beyond the scope here so /shrug.
Comment #83
tim.plunkettThis was not successfully pushed
Comment #85
alexpotthint: Updates were rejected because the tip of your current branch is behindsorry :(Pushing to 8.8.x now and will backport to 8.7.x once we're out of the freeze. Committed 30654bd41d and pushed to 8.8.x. Thanks!
Comment #86
alexpottSetting the correct status.
Comment #88
alexpottComment #90
xjmWe're still in commit freeze, so reverted from 8.7.x for now.
Comment #91
alexpottComment #94
quietone commentedpublish change record