Those have just been added by #1893772: Move entity-type specific storage logic into entity classes.

Entity::save() & Entity::delete() should be left as the parent implementations that just delegate to the storage controller. The storage controller then calls [pre|post]Save(), [pre|post]Delete(), etc...

Remaining tasks:
- get green tests
- moving our code to pre/postSave() means we can stop populating $this->original, ConfigStorageController::save() does it for us

Patch should be ready.

CommentFileSizeAuthor
#65 field-save-delete-2020895-64.patch24.12 KByched
#63 field-save-delete-2020895-63.patch24.12 KByched
#56 field-save-delete-2020895-56.patch24.12 KByched
#56 interdiff.txt3.11 KByched
#55 field-save-delete-2020895-55.patch24.03 KBswentel
#55 interdiff.txt2.38 KBswentel
#49 field-save-delete-2020895-49.patch24.05 KBswentel
#47 field-save-delete-2020895-47.patch24.62 KByched
#46 field-save-delete-2020895-46.patch25.53 KByched
#46 interdiff.txt1.94 KByched
#44 field-save-delete-2020895-44.patch24.53 KByched
#44 interdiff.txt1.99 KByched
#43 field-save-delete-2020895-43.patch24.58 KByched
#43 interdiff.txt787 bytesyched
#41 field-save-delete-2020895-41.patch24.58 KByched
#38 Screen Shot 2013-09-16 at 23.30.45.png50.45 KBswentel
#37 field-save-delete-2020895-37.patch24.09 KByched
#37 interdiff.txt3.9 KByched
#36 field-save-delete-2020895-36.patch22.65 KByched
#36 interdiff.txt4.72 KByched
#35 field-save-delete-2020895-35.patch22.61 KByched
#35 interdiff.txt2.5 KByched
#34 field-save-delete-2020895-34.patch22.69 KByched
#34 interdiff.txt4.14 KByched
#32 field-save-delete-2020895-32.patch21.2 KByched
#32 interdiff.txt7.2 KByched
#28 field-save-delete-2020895-28.patch19.21 KByched
#25 field-save-delete-2020895-25.patch20.3 KBpcambra
#25 interdiff.txt5.24 KBpcambra
#21 interdiff.txt7.61 KBpcambra
#21 field-save-delete-2020895-21.patch20.28 KBpcambra
#20 interdiff.txt1.82 KBpcambra
#20 field-save-delete-2020895-20.patch15.66 KBpcambra
#18 interdiff.txt742 bytespcambra
#18 field-save-delete-2020895-18.patch15.67 KBpcambra
#12 field-save-delete-2020895-12.patch15.73 KByched
#12 interdiff.txt1.9 KByched
#11 field-save-delete-2020895-11.patch16.45 KByched
#11 interdiff.txt2.15 KByched
#8 interdiff.txt3.13 KBpcambra
#8 field-save-delete-2020895-8.patch17.45 KBpcambra
#4 field-save-delete-2020895-4.patch16.56 KByched
#1 field-save-delete-2020895-1.patch17.14 KByched
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

yched’s picture

Status: Active » Needs review
FileSize
17.14 KB

Temptative patch.

This leaves out FieldInstance::delete() for now, because of the custom $field_cleanup param, this might be a bit tricky :-(.

amateescu’s picture

I had the same tricky situation in the patch that converted menu links to entities :) I chose to solve it by using a flag on controller that needs to be set prior to calling the delete method. See http://drupalcode.org/project/drupal.git/blob/refs/heads/8.x:/core/modul... and http://drupalcode.org/project/drupal.git/blob/refs/heads/8.x:/core/modul....

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-1.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
16.56 KB

Reroll after #2018731: Move field_has_data() to Field::hasData().

Fails were because $storage_controller->loadUnchanged($this->getOriginalID()); in FieldInstance::preSave() return FALSE when we are called from field_entity_bundle_rename().

I added a test / workaround for now, but field_entity_bundle_rename() should update the config records directly IMO, rather than re-saving the ConfigEntity. I know we went back and forth on this in the "Field / CMI" issue, but I can't remember why we stayed that way.

yched’s picture

re @amateescu #2 / menu links - setPreventReparenting() :
Hm, smart ;-) The tricky bit might be making sure that the flag that gets set on the controller for a specific delete() operation doesn't interfere for the rest of the request. Or I guess each delete operation explicitly sets the state of the flag it needs, so the flag always has the "right" value when performing a delete ?

I probably won't have the time to look into this before I go, though :-/

amateescu’s picture

Yep, in that case, it was always set by the function that called the storage controller. But the proper scenario would be to unset it at the end of the delete() method.

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-4.patch, failed testing.

pcambra’s picture

Status: Needs work » Needs review
FileSize
17.45 KB
3.13 KB

Here's a reroll of this, I've updated it to the new changes on the field but got several test failures as it seems that in some circumstances, the instance is not saved to config, had to "force it" in a test to demonstrate the problem (see the interdiff).

    // Save in config.
    \Drupal::config('field.instance.' . $this->instance_definition['field_name'])
      ->setData($this->instance_definition)
      ->save();

Didn't push it to the sandbox because I get this git message on status & I don't know if I should push or not :)
Your branch is ahead of 'origin/field-save-delete-2020895' by 510 commits.

yched’s picture

Thanks ! Will look into this.

The 510 commits are probably the ones you merged from HEAD, it should be fine to push.

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-8.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
2.15 KB
16.45 KB

The instance 'id' property was not saved anymore. Caused all sorts of weirdness when loading more than two of them.
My bad :-(.

Reverted the changes in CrudTest.php

yched’s picture

Reroll effect - the patch reintroduced the CRUD hooks that got removed in #2013939: Remove hook_field_[CRUD]_() in favor of hook_ENTITY_TYPE_[CRUD]() calls.

pcambra’s picture

amateescu’s picture

This cleanup looks great to me. I could only find this small nitpick :)

+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
@@ -284,43 +285,45 @@ public function getExportProperties() {
+   * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
+   * @throws \Drupal\field\FieldException If the field definition is invalid.

Missing description for the @param argument and needs an empty line before @throws. Also happens for the next method.

pcambra’s picture

Status: Needs review » Needs work

FieldInstance::preDelete() is still TODO

yched’s picture

Status: Needs work » Needs review

We might be able to remove the need for the $field_cleanup bool, actually.
It's just there to avoid an infinite loop on:
"delete last instance -> delete field -> delete all instances"
or
"delete field -> delete all instances -> delete field"

But If we move "delete all instances" to Field::postDelete(),
and "if (last instance) { delete field }" to FieldInstance::postDelete(),
then by the time those run, the actual records that might risk "re-delete ad nauseum" have been deleted already, load() will return nothing, and there is nothing to delete, no loop :-)

yched’s picture

Oops, also, I didn't re-remove "Invoke hook_field_delete_field()" from Field::postDelete() - see #12

pcambra’s picture

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-18.patch, failed testing.

pcambra’s picture

Status: Needs work » Needs review
FileSize
15.66 KB
1.82 KB

Some load and load multiple fixes on the storage controller

pcambra’s picture

Here's a initial approach to #16 and #17, I'm getting some warnings on CRUD field tests as it seems to be reloading a instance twice.

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-21.patch, failed testing.

yched’s picture

Thanks @pcambra!

+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.phpundefined
@@ -380,108 +385,101 @@ public function preDelete() {
+    foreach ($fields as $field) {
+      if (!$field->deleted) {
+        $state = \Drupal::state();
+        $deleted_fields = $state->get('field.field.deleted') ?: array();
+        $config = $field->getExportProperties();
+        $config['deleted'] = TRUE;
+        $deleted_fields[$field->uuid] = $config;
+        $state->set('field.field.deleted', $deleted_fields);
+      }

This handles multiple field deletion, so for performance, it would be best to do one single $state->get() before looping, and one single $state->set() after looping ?

+ 80 chars comment wrapping needs to be adjusted after the code is moved around.

Also, this was previously done *after* the "delete all instances" part, and is now done before. Might have an impact on test fails, not sure.

+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.phpundefined
@@ -380,108 +385,101 @@ public function postDelete() {
+    foreach ($fields as $field) {
+      if (!$field->deleted) {
+        $module_handler = \Drupal::moduleHandler();
+        $instance_controller = \Drupal::entityManager()->getStorageController('field_instance');

Those two lines should move above the loop.

+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.phpundefined
@@ -380,108 +385,101 @@ protected function saveNew() {
+        foreach ($instance_controller->loadMultiple($instance_ids) as $instance) {
+          // By default, FieldInstance::delete() will automatically try to delete
+          // a field definition when it is deleting the last instance of the
+          // field. Since the whole field is being deleted here, pass FALSE as
+          // the $field_cleanup parameter to prevent a loop.
+          $instance->delete(FALSE);
         }

That's for a second line of optimizations once this is green, but this could probably be optimized by collecting the instance ids for all the fields, and do a single loadMultiples() on all the collected ids.

Also, FieldInstance::delete(), that defined the custom $field_cleanup set to FALSE here, is now gone, so the FALSE is useless now.
Meaning the code could just do:
$instances = $instance_controller->loadMultiple(...);
$instance_controller->delete($instances);
(which is the way entity_delete_multiple does() it)

+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.phpundefined
@@ -330,153 +328,105 @@ public function getExportProperties() {
+  public static function postDelete(EntityStorageControllerInterface $storage_controller, array $instances) {
+  foreach ($instances as $instance) {

indentation is broken :-)

yched’s picture

Also note: moving our code to pre/postSave() means we can stop populating $this->original, ConfigStorageController::save() does it for us.

pcambra’s picture

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

Here's some progress, rebased against latest HEAD and I've implemented most of #23 & #24 but probably still have some fails.

The only substantial change is that getBundles() wasn't being populated on postDelete of Field, probably because is too late, stored it in the state in preDelete for later use on postDelete.

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-25.patch, failed testing.

yched’s picture

#1969698: ConfigEntity::save() should disallow saving ID/UUID conflicts (Field UUID changes can badly corrupt field data) means we shouldn't need to check for "cannot create a *new* field/instance with the same id", ConfigEntity::save() checks that for us.
But the current code organization in HEAD forces us to have our own check, because it needs to happen before we call the EntityStorageController::on[Field|Instance]Create() - and our current call to parent::save() happens after that.
The new code organization in the patch should hopefully let us remove that.

Added that in a "remaining tasks" section in the OP, along with #24

yched’s picture

Status: Needs work » Needs review
FileSize
19.21 KB

Reroll for now
(I also updated the field-save-delete-2020895 branch in the sandbox)

Status: Needs review » Needs work
Issue tags: -Field API

The last submitted patch, field-save-delete-2020895-28.patch, failed testing.

yched’s picture

Status: Needs work » Needs review

#28: field-save-delete-2020895-28.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +Field API

The last submitted patch, field-save-delete-2020895-28.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
7.2 KB
21.2 KB

Let's see where this one brings us.

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-32.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
4.14 KB
22.69 KB

Some dirty parts to polish, but let's see.

yched’s picture

Reordering a couple things.

yched’s picture

Previous patch seems lost in testbot limbo, I'll cancel it...
Meanwhile, debugged & reordered a bit more.

yched’s picture

Cleaned up the newly added tests.
I think this is ready for reviews now.

swentel’s picture

Killed the test, it was running for 4 days now ...

Screen Shot 2013-09-16 at 23.30.45.png

Status: Needs review » Needs work
Issue tags: -Field API

The last submitted patch, field-save-delete-2020895-37.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
Issue tags: +Field API

#37: field-save-delete-2020895-37.patch queued for re-testing.

yched’s picture

Reroll

swentel’s picture

+++ b/core/modules/field/lib/Drupal/field/Entity/Field.php
@@ -355,100 +350,102 @@ protected function saveNew() {
+    // Delete instances first. Note: when deleteing a field through

Type in 'deleteing'.

+++ b/core/modules/field/lib/Drupal/field/Entity/Field.php
@@ -355,100 +350,102 @@ protected function saveNew() {
+    if ($this->type != $this->original->type) {

Should we already use getFieldType() ?

Same maybe for $field->name ?

yched’s picture

Should we already use getFieldType() ?
Same maybe for $field->name

I'd think not in this issue. The Field / FieldInstance classes currently have plenty of code that access their own properties directly, let's not change this here.

Fixed the typo.

yched’s picture

Attempt to do #27

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-44.patch, failed testing.

yched’s picture

Status: Needs work » Needs review
FileSize
1.94 KB
25.53 KB

Hm, #1969698: ConfigEntity::save() should disallow saving ID/UUID conflicts (Field UUID changes can badly corrupt field data) did not take care of that actually, so we need to keep those for now. Reverting the changes from #44.
We should be good here IMO.

yched’s picture

Reroll + bump. We need to do this, the sooner the better, and it's a bit painful to merge :-/

Status: Needs review » Needs work

The last submitted patch, field-save-delete-2020895-47.patch, failed testing.

swentel’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
24.05 KB

This is good to go indeed.

Status: Reviewed & tested by the community » Needs work
Issue tags: -Field API

The last submitted patch, field-save-delete-2020895-49.patch, failed testing.

swentel’s picture

Status: Needs work » Needs review

#49: field-save-delete-2020895-49.patch queued for re-testing.

Status: Needs review » Needs work
Issue tags: +Field API

The last submitted patch, field-save-delete-2020895-49.patch, failed testing.

swentel’s picture

pinged jthorson about this one - installation goes fine locally ..

Berdir’s picture

+++ b/core/lib/Drupal/Core/Entity/FieldableDatabaseStorageController.php
@@ -1115,9 +1114,10 @@ public function onFieldDelete(FieldInterface $field) {
-    $revision_new_table = static::_fieldRevisionTableName($field);

+++ b/core/modules/field/lib/Drupal/field/Entity/Field.php
@@ -353,100 +347,102 @@ protected function saveNew() {
+    // Make sure all settings are present, so that a complete field
+    // definition is passed to the various hooks and written to config.
+    $this->settings += \Drupal::service('plugin.manager.entity.field.field_type')->getDefaultSettings($this->type);
 

"The service definition "plugin.manager.entity.field.field_type" does not exist.".

Without "entity" now.

swentel’s picture

Status: Needs work » Needs review
FileSize
2.38 KB
24.03 KB

Ok, that was weird - must have been on the wrong branch.

yched’s picture

Doh, thanks for fixing this :-)
Patch just moves the dependencies on the field type manager to standalone vars, as the current code tends to do in Field / FieldInstance pending real injection abilities in entity classes.

swentel’s picture

Status: Needs review » Reviewed & tested by the community

Right, much nicer :)

swentel’s picture

Issue summary: View changes

remaining tasks

Xano’s picture

yched’s picture

Issue summary: View changes

removed the stale "remaining tasks"

Xano’s picture

yched’s picture

Can I haz commit ? :-p

yched’s picture

Reroll (just offsets)

yched’s picture

Re-reroll.

Pleaaaase ? :-)

catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 8.x, thanks!

Status: Fixed » Closed (fixed)

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