Modules that want to generalize by entities are stuck on presave, because we gave up too early on adding a hook_entity_presave() because of silly user.module. Now those modules have to implement *every single* different presave hook rather than just hook_entity_presave(). Any time another contrib module adds another entity, those modules have to add support for those as well.

Can we just get this fixed before we reach RC?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Dave Reid’s picture

Status: Active » Needs review
FileSize
12.95 KB

Just for the testbot right now...

Dave Reid’s picture

Issue tags: +pathauto, +xmlsitemap

Tagging potential modules that this issue blocks...

Status: Needs review » Needs work

The last submitted patch, 968458-hook-entity-presave-D7.patch, failed testing.

Dave Reid’s picture

Status: Needs work » Needs review
FileSize
14.23 KB

And this one should be just fine since really the user forms should provide 'uid' as a FAPI value field anyway.

sun’s picture

Status: Needs review » Reviewed & tested by the community

I like.

fago’s picture

Status: Reviewed & tested by the community » Needs work

No, this certainly won't work as expected and the form hack demonstrates that.

+    $edit = (object) $edit;
+    module_invoke_all('entity_presave', $edit, 'user');
+    $edit = (array) $edit;

$edit won't contain a complete user object, e.g. it may contain a single value that's updated only. I guess cloning $account and merging in $edit would work though.

sun’s picture

Status: Needs work » Needs review
FileSize
15.01 KB

What a mess. First task for D8: Drop that $edit argument. Stupidly insane.

Status: Needs review » Needs work

The last submitted patch, drupal.hook-entity-presave.7.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
15.06 KB

One more try.

Status: Needs review » Needs work

The last submitted patch, drupal.entity-presave.9.patch, failed testing.

fago’s picture

Status: Needs work » Needs review
FileSize
13.19 KB

>What a mess. First task for D8: Drop that $edit argument. Stupidly insane.
Agreed.

Attached patch makes user's entity presave hook work like the others.

Status: Needs review » Needs work

The last submitted patch, drupal-entity-presave.patch, failed testing.

sun’s picture

Status: Needs work » Needs review
FileSize
14.7 KB

array_diff_assoc() fails if $account or $edit contain scalar values.

Status: Needs review » Needs work

The last submitted patch, drupal.entity-presave.13.patch, failed testing.

sun’s picture

Assigned: Dave Reid » sun
Status: Needs work » Needs review
FileSize
15.06 KB

Attached patch should fix the remaining 2 failures.

fago’s picture

Status: Needs work » Needs review

hm, array_diff_assoc() should work with scalar values, not? However, the loop is easier to read+understand anyway.

>+ // array_diff_assoc() fails if there are scalar values in $account or $edit.
I don't think we need that comment.

+    foreach ((array) $account as $key => $value) {

We can directly loop over the object the same way, no need to cast to an array.

>+        unset($account->$key);

So we are reverting the changes to $account back to $account_unchanged. Why not go with $account_unchanged instead then for the remaining code? Thus, as we have an updated object already, we could use it for calling field_attach_update() too - what would fix this hook and so help #985642: Remove file_attach_load() from file_field_update().

Similarly field_attach_insert() and entity_insert is broken, not sure whether we want to deal with that all in this issue though.

fago’s picture

Status: Needs review » Needs work
naught101’s picture

Status: Needs review » Needs work

subscribe

sun’s picture

Status: Needs work » Needs review
FileSize
14.96 KB

Within the scope of this issue, I don't want to fiddle with further changes in user_save(). Certain properties of $account are used further down in the function, and changing that usage is not within the scope of this issue. Let's move that suggestion into #985642: Remove file_attach_load() from file_field_update(), please.

sun’s picture

#19: drupal.entity-presave.19.patch queued for re-testing.

fago’s picture

FileSize
13.51 KB

>Within the scope of this issue, I don't want to fiddle with further changes in user_save().
ok.

@patch:
I think it's confusing to update $account first and then revert that changes. We have already two variables, so there is no need to go with reverting. Updated patch attached.

sun’s picture

Status: Needs review » Reviewed & tested by the community

ok, works for me.

In case it's not immediately clear for everyone: $account remains unchanged, only $account_updated is updated with $edit and sent to presave hook implementations, and if those hooks changed any properties, the corresponding keys in $edit are updated accordingly afterwards.

webchick’s picture

Status: Reviewed & tested by the community » Needs work

This seems like a fairly straight-forward oversight in the API.

However, seriously?

-    // Presave field allowing changing of $edit.
-    $edit = (object) $edit;
-    field_attach_presave('user', $edit);
-    $edit = (array) $edit;

Ugly, but makes sense.

+    // Invoke presave operations of Field Attach API and Entity API. As those
+    // APIs require an updated entity object, create that.
+    $account_updated = clone $account;
+    foreach ($edit as $key => $value) {
+      $account_updated->$key = $value;
+    }
+    field_attach_presave('user', $account_updated);
+    module_invoke_all('entity_presave', $account_updated, 'user');
+    // Update $edit with any changes modules might have applied to the account.
+    foreach ($account_updated as $key => $value) {
+      if (!property_exists($account, $key) || $value !== $account->$key) {
+        $edit[$key] = $value;
+      }
+    }

Ugly, and leaving me utterly mystified why the three lines above that were removed wouldn't be sufficient?

At the very least we need to comment why we are re-implementing (array) and (object) in PHP code. But I would prefer to just go back to the old grokkable thing.

sun’s picture

Status: Needs work » Needs review
FileSize
14.98 KB

I hope the extended inline comment explains why it needs to be done that way.

fago’s picture

Status: Needs review » Reviewed & tested by the community

Yep, just casting is not enough. I think the comment is fine now.

sun’s picture

#24: drupal.entity-presave.24.patch queued for re-testing.

the greenman’s picture

Small note in support of this patch - presave feels a bit useless in user.module at the moment. $edit does not contain the uid, so it is impossible to perform presave actions based on a user.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Thanks, that new comment helps.

Committed to HEAD!

yched’s picture

Great, thanks folks :-)

Should there be a followup to solve field_attach_update() not receiving the $entity->original property ? (cf #16, #19 and #985642: Remove file_attach_load() from file_field_update())

fago’s picture

Yep, I've rolled a follow-up patch. See #999004: user_save() relies on $edit instead of $account.

Status: Fixed » Closed (fixed)
Issue tags: -pathauto, -xmlsitemap

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