6

I've added a field via hook_entity_base_field_info(). It has data stored in it. I then go to uninstall my module and I get the following error:

-> drush8 pm-uninstall massing -y
The following extensions will be uninstalled: massing
Do you really want to continue? (y/n): y
exception 'Drupal\Core\Extension\ModuleUninstallValidatorException' with message 'The following reasons prevents the modules from being uninstalled:    [error]
There is data for the field massing_memberships on entity type User' in
/Users/les.peabody/Sites/drupal8/core/lib/Drupal/Core/Extension/ModuleInstaller.php:347
Stack trace:
#0 /Users/les.peabody/Sites/drupal8/core/lib/Drupal/Core/ProxyClass/Extension/ModuleInstaller.php(95):
Drupal\Core\Extension\ModuleInstaller->uninstall(Array, true)
#1 /Users/les.peabody/opt/drush8/commands/core/drupal/environment.inc(227): Drupal\Core\ProxyClass\Extension\ModuleInstaller->uninstall(Array)
#2 /Users/les.peabody/opt/drush8/commands/core/drupal/pm_8.inc(80): drush_module_uninstall(Array)
#3 /Users/les.peabody/opt/drush8/commands/pm/pm.drush.inc(1190): _drush_pm_uninstall(Array)
#4 [internal function]: drush_pm_uninstall('massing')
#5 /Users/les.peabody/opt/drush8/includes/command.inc(364): call_user_func_array('drush_pm_uninst...', Array)
#6 /Users/les.peabody/opt/drush8/includes/command.inc(215): _drush_invoke_hooks(Array, Array)
#7 [internal function]: drush_command('massing')
#8 /Users/les.peabody/opt/drush8/includes/command.inc(183): call_user_func_array('drush_command', Array)
#9 /Users/les.peabody/opt/drush8/lib/Drush/Boot/BaseBoot.php(62): drush_dispatch(Array)
#10 /Users/les.peabody/opt/drush8/drush.php(70): Drush\Boot\BaseBoot->bootstrap_and_dispatch()
#11 /Users/les.peabody/opt/drush8/drush.php(11): drush_main()
#12 {main}

So what I'm trying to figure out how to do is how to drop the data in this table after I've called for an uninstall on the module, but before the check happens for whether or not there is data in the table. I've tried implementing hook_uninstall() and hook_module_preuninstall() but neither of these hooks are invoked before the check for data presence. Thoughts?

EDIT: Looking at ModuleInstaller.php at line 343 (D8 RC1), it runs uninstall validators prior to triggering hook_module_preuninstall and hook_uninstall. This is where it is failing. So, maybe there is a way to add an uninstall validator for my module and run it first? There doesn't look to be any other way to do it besides adding some kind of mechanism to the module which makes it "purge" itself of any data it's added to the system, which then lets you uninstall it. Seems terribly convoluted and not side builder friendly in the slightest, very frustrating.

  • Are you on the latest version of drush? – mpdonadio Oct 9 '15 at 12:08
  • @MPD: Yes I am. – Lester Peabody Oct 9 '15 at 14:14
1

I was able to solve this problem by implementing hook_uninstall() in my module's install file and explicitly deleting my custom entities.

For example:

function mymodule_uninstall() {
  //remove_fields_user();

  $results = \Drupal\mymodule\Entity\MeetEntity::loadMultiple();
  foreach ($results as $entity) {
    $entity->delete();
  }
  $results = \Drupal\mymodule\Entity\Team::loadMultiple();
  foreach ($results as $entity) {
    $entity->delete();
  }
}

See Drupal 8 hook_uninstall() docs for more details

1

I encountered this same issue, and unfortunately found no satisfactory answer here so I am posting my solution. It is a solution which I find unappealing but it is the most graceful solution that I could come up with after hours of searching and finding no answer.

First the reason that the solution from Eric C does not work for me is because, as with the original request, my entity is User, and I have added fields to it. I cannot simply delete the User entity, so I must empty the fields before uninstalling.

This can be handled a few ways, one is to create a custom page to visit and delete all of the data, then uninstall the module. I considered this but because I am not the one that will be handling those operations I created a solution which I think is more elegant, though still unfortunate.

Three basic steps:

  1. When the user is loaded, fill the fields.
  2. When the user logs out empty the fields.
  3. Make sure that the fields are still initialized.

Step one:


function myModule_user_load(array $entities) {
    $fields=['field_1','field_2',...];

    foreach ($entities as $index => $user) {

        foreach ($field as $field) {
                $entities[$index]->set($field, 'value');

        }
    }
}

This basically sets the data on user load because we are going to be destroying the data when they logout.

Step 2:



function myModule_user_logout($account) {

    $user = User::load($account->id());
    $fields=['field_1','field_2',...];

        foreach ($field as $field) {
            $user->set($field, NULL);
        }
    $user->save();

    initilizeFieldsFunction();
}

Now assuming that every user has logged out before you uninstall this module, the data should be empty, freeing the fields. However drupal seems to still require that the fields be initialized and I have had the uninstaller fail due to the fields not being found. So at the bottom of the logout hook we have to make sure that the fields are initialized. This is basically the same as what you, probably did at the installation of your module, if you created these fields through the BaseFieldDefinition::create() method.

Step 3:


function initilizeFieldsFunction() {
       $fields=['field_1','field_2', ...];

        //  Check that the fields are empty
        // by querying all uids where the fields
        // still exist.
        $query = \Drupal::entityQuery('user');
        $group = $query->orConditionGroup()
                ->exists('field_1')
                ->exists('field_2')
                ->exists('...');
        $uids = $query->condition($group)
                ->execute();

        if (count($uids) === 0) {
            foreach ($fields as $field) {
                $definition = \Drupal::service('entity_field.manager')->getFieldStorageDefinitions('user')[$field];
                \Drupal::service('field_storage_definition.listener')->onFieldStorageDefinitionCreate($definition);
            }
        }
    }

Now assuming that every user with data has logged out, you ought to be able to uninstall the module.

I hope this helps someone, though more I'd like a better solution to this issue. I never found the validator method that the original requester was pondering to work, and I don't actually know when the validator runs, so the issue that I have is that I can't destroy this data for every user if the validator runs before uninstall is triggered because they may be using that data up until uninstall. So basically as far as I can tell this needs to be able to happen in hook_uninstall and drupal 8 just doesn't permit that.

If any one has a more appropriate solution please do contribute because this seems to be a fairly unaddressed issue in D8.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.