Right now entity_load_unchanged() simply wraps a cache clear + entity load. In OO code it's not recommended to call procedural functions which live in the global scope, hence we need to inject the entity manager and retrieve the proper storage controller. Since the load unchanged logic lives only in the function above, OO code needs to recode it every time. Although the implemented logic is quite simple this in not exactly DRY.

See #1807692-82: Introduce a column synchronization capability and use it to translate alt and titles through the image field widget and below.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

plach’s picture

Status: Active » Needs review
FileSize
2.92 KB

Here is a patch.

sun’s picture

Looks fine to me.

Just one thing: Can we make the method body in ConfigStorageController identical to DatabaseStorageController, please? The resetCache method exists, and is merely empty right now, but we want to change that in #1885830: Enable static caching for config entities

plach’s picture

Here it is.

Btw, would it make sense to have an abstract BaseStorageController as a common ancestry between database and config storage controllers to avoid repeated code?

sun’s picture

Status: Needs review » Reviewed & tested by the community

Thanks!

Yeah, I totally thought of a EntityStorageControllerBase a couple of times already, too. We might want to do that as part of #1893772: Move entity-type specific storage logic into entity classes.

plach’s picture

Yes, I was thinking about that one too :)

Berdir’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.phpundefined
@@ -41,6 +41,17 @@ public function resetCache(array $ids = NULL);
+   * @param $id
+   *   The ID of the entity to load.
+   *
+   * @return
+   *   The unchanged entity, or FALSE if the entity cannot be loaded.
+   */
+  public function loadUnchanged($id);

Nitpick, but the @param and @return should specify the type.

plach’s picture

Status: Needs work » Needs review
FileSize
3 KB

There is no fixed type for the entity ID, right?
(no interdiff I just edited the patch :)

Berdir’s picture

Status: Needs review » Reviewed & tested by the community

Thanks.

catch’s picture

Status: Reviewed & tested by the community » Needs review

Hmm while we're moving this could we rename it to loadUncached() or similar?

plach’s picture

Not sure about this: currently the method name describes what you'll get, if we rename it we are exposing how we provide the unchanged stuff, which may need to be updated if we change the underlying implementation.

catch’s picture

Hmm yeah I'm not sure either. This was originally added for hook_node_*() implementations that want to see the original entity before it was loaded rather than the one as it's being changed, but we now have $entity->original (which is messy in itself but for the moment that's what we have).

The only place this is used outside of storage controllers and tests is http://api.drupal.org/api/drupal/core%21modules%21translation_entity%21l...

But could that use $entity->original instead?

If it can, then we could probably refactor the tests a bit and make this a protected method.

plach’s picture

If we make $entity->original a proper and reliable entity property then the field synchronizer can totally leverage it. Then we can make the method protected.

plach’s picture

@catch:

Do you think we should do that in this issue ot it can be another one? I've the feeling it'll be easier to do once the NG migration is complete.

catch’s picture

Separate issue seems fine but could we create that now and add a @todo?

sun’s picture

I think that loadUnchanged() is actually much more appropriate than loadUncached().

The focus of this facility is not on the cache, because loading the [database-]cached copy of the entity would be totally OK. loadUnchanged() actually works around the static cache, because the static cache is compromised with edited, in-process, submitted form values.

plach’s picture

Here is the todo.

Actually I'm thinking that perhaps it would be better to add a method to EntityInterface to retrieve the unchanged entity as a replacement to $entiy->original instead of defining a property. This would be a more solid solution and wouldn't require us to wait for the NG conversion to be completed. If we decied to go this way we can hijack this issue and skip the follow-up.

sun’s picture

Status: Needs review » Reviewed & tested by the community

I'm not sure about #16, and likewise, also not sure about the added @todo.

I could foresee situations in which you want to populate $entity->original with a fake/dummy entity that isn't actually loaded from storage. But turning ->original into a method would enforce storage-loading on all possible scenarios.

In any case, this particular and focused change looks good to me. Let's handle any further/additional questions in separate issues instead.

plach’s picture

Status: Reviewed & tested by the community » Needs review

Makes sense, here is the follow-up: #1925914: Make $entity->original a defined entity field.

I introduced the Post NG clean-up tag, since I have lots of issues/todos pending on that.

plach’s picture

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

Status: Reviewed & tested by the community » Needs work

Good clean-up! I found a nit-pick:

+++ b/core/lib/Drupal/Core/Config/Entity/ConfigStorageController.php
@@ -143,6 +143,15 @@ public function load(array $ids = NULL) {
+  function loadUnchanged($id) {

"public function"

+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -236,6 +236,15 @@ public function load(array $ids = NULL) {
+  function loadUnchanged($id) {

should be "public function"

plach’s picture

Status: Needs work » Needs review
FileSize
3.14 KB

Here it is.

catch’s picture

I could foresee situations in which you want to populate $entity->original with a fake/dummy entity that isn't actually loaded from storage. But turning ->original into a method would enforce storage-loading on all possible scenarios.

Not necessarily, we could add a setter as well. If we had it as a method, we'd only need to load the entity when accessed, right now it's wasted effort when it's not needed.

fago’s picture

Status: Needs review » Reviewed & tested by the community

Thanks plach, back to RTBC then.

Not necessarily, we could add a setter as well. If we had it as a method, we'd only need to load the entity when accessed, right now it's wasted effort when it's not needed.

Ideally, we'd have the entity itself tracking what has changed imo - so we could save the load operation at whole. Howsoever that discussion belongs into its own issue.

catch’s picture

Yes that's an even better point. I thought we had an issue for this already, but it may be stuck in one of the very early entity API dicsussions on Drupal.org. At the risk of a duplicate issue opened #1926714: Track changes for entity property changes and make available to hooks.

catch’s picture

Title: entity_load_unchanged() should be part of the storage controller » Change notice: entity_load_unchanged() should be part of the storage controller
Priority: Normal » Critical
Status: Reviewed & tested by the community » Active

Committed/pushed to 8.x, thanks!

Will need a change notice.

plach’s picture

Status: Active » Needs review
plach’s picture

Small follow-up: #1935762: Remove entity_load_unchanged() call from FieldTranslationSynchronizer::synchronizeFields().

Nothing special in itself, but since it's probably setting a standard it could use some serious DX review. Quoting @yched from #1807692-82: Introduce a column synchronization capability and use it to translate alt and titles through the image field widget:

Interesting.
In the previous patch, this was translation_entity_sync() function calling entity_load_uncached().
So now that the code moved to a class, we avoid calling functional stuff like entity_load_uncached() within a method and go for the OO equivalent ?

Does that mean that entity_load() too is considered bad practice in OO code, and that any class willing to load an entity needs to get an injected EntityManager and do $this->entityManager->getStorageController($entity_type)->load(array($id)); instead ? Ew :-/
Definitely not a debate for this thread, but IMO that would deserve a spin off discussion to establish the practice.

irek02’s picture

Title: Change notice: entity_load_unchanged() should be part of the storage controller » entity_load_unchanged() should be part of the storage controller
Priority: Critical » Normal
Status: Needs review » Fixed

The change record looks good. I searched through d.o including examples module, where this change could be reflected but did not find anything. If no objections, I change the status to fixed.
Good job, plach!

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