Re-saving a new bundle entity throws an exception

Created on 6 January 2016, over 8 years ago
Updated 7 February 2023, over 1 year ago

When a configuration entity that acts as a bundle for another entity is first saved, it does not have an "original ID" yet. If that configuration entity is then re-saved during Entity::postSave() or hook_entity_insert(), the site shows a blank screen (WSOD).

This is because ConfigEntityBundleBase::preSave() throws an exception if the entity ID differs from the original ID. Of course this is always the case for new bundle entities, as they have NULL for their original ID. See below for the relevant code:

  public function preSave(EntityStorageInterface $storage) {
    parent::preSave($storage);

    // Only handle renames, not creations.
    if (!$this->isNew() && $this->getOriginalId() !== $this->id()) {
      $bundle_type = $this->getEntityType();
      $bundle_of = $bundle_type->getBundleOf();
      if (!empty($bundle_of)) {
        throw new ConfigNameException("The machine name of the '{$bundle_type->getLabel()}' bundle cannot be changed.");
      }
    }
  }

However, EntityStorageBase::doPostSave has a fix for this, but it's being called too late:

  protected function doPostSave(EntityInterface $entity, $update) {
    $this->resetCache(array($entity->id()));

    // The entity is no longer new.
    $entity->enforceIsNew(FALSE);

    // Allow code to run after saving.
    $entity->postSave($this, $update);
    $this->invokeHook($update ? 'update' : 'insert', $entity);

    // After saving, this is now the "original entity", and subsequent saves
    // will be updates instead of inserts, and updates must always be able to
    // correctly identify the original entity.
    $entity->setOriginalId($entity->id());

    unset($entity->original);
  }

Proposed resolution:

  • Move the relevant piece of code above the postSave and invokeHook for new entities and leave it below the hooks for updated entities.
  • Alternatively, do the above in a brand new ConfigEntityStorageBase class override of ::postSave().

Example use case:
Config entities can have plugin collections by implementing EntityWithPluginCollectionInterface. After saving, one could let other modules try to install "default" plugins on the config entity. Doing so would require a re-save of the config entity, leading to the exception thrown.

🐛 Bug report
Status

Needs work

Version

10.1

Component
Entity 

Last updated about 5 hours ago

Created by

🇧🇪Belgium kristiaanvandeneynde Antwerp, Belgium

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

Production build 0.69.0 2024