From cb5774fe4b6f873c6e4d6f2bdddb844e051e0922 Mon Sep 17 00:00:00 2001 From: Lee Rowlands Date: Fri, 24 Aug 2018 14:32:10 +1000 Subject: [PATCH] Issue #2985297 by amateescu, hchonov: Generalize the concept of entity synchronization --- .../Core/Config/Entity/ConfigEntityBase.php | 26 +--------- .../Config/Entity/ConfigEntityInterface.php | 43 +--------------- .../Drupal/Core/Entity/ContentEntityBase.php | 5 ++ .../Core/Entity/ContentEntityInterface.php | 2 +- .../Core/Entity/SynchronizableEntityTrait.php | 36 ++++++++++++++ .../Core/Entity/SynchronizableInterface.php | 49 +++++++++++++++++++ .../workspaces/src/EntityOperations.php | 4 +- .../workspaces/src/WorkspacePublisher.php | 7 +-- 8 files changed, 99 insertions(+), 73 deletions(-) create mode 100644 core/lib/Drupal/Core/Entity/SynchronizableEntityTrait.php create mode 100644 core/lib/Drupal/Core/Entity/SynchronizableInterface.php diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index 4efcade7a16..ef4423857ee 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -10,6 +10,7 @@ use Drupal\Core\Config\ConfigDuplicateUUIDException; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityWithPluginCollectionInterface; +use Drupal\Core\Entity\SynchronizableEntityTrait; use Drupal\Core\Plugin\PluginDependencyTrait; /** @@ -22,6 +23,7 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface use PluginDependencyTrait { addDependency as addDependencyTrait; } + use SynchronizableEntityTrait; /** * The original ID of the configuration entity. @@ -48,14 +50,6 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface */ protected $uuid; - /** - * Whether the config is being created, updated or deleted through the - * import process. - * - * @var bool - */ - private $isSyncing = FALSE; - /** * Whether the config is being deleted by the uninstall process. * @@ -204,22 +198,6 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface return !empty($this->status); } - /** - * {@inheritdoc} - */ - public function setSyncing($syncing) { - $this->isSyncing = $syncing; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function isSyncing() { - return $this->isSyncing; - } - /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php index 248701e7f32..3b549a1285d 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityInterface.php @@ -3,6 +3,7 @@ namespace Drupal\Core\Config\Entity; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\SynchronizableInterface; /** * Defines a common interface for configuration entities. @@ -10,7 +11,7 @@ use Drupal\Core\Entity\EntityInterface; * @ingroup config_api * @ingroup entity_api */ -interface ConfigEntityInterface extends EntityInterface, ThirdPartySettingsInterface { +interface ConfigEntityInterface extends EntityInterface, ThirdPartySettingsInterface, SynchronizableInterface { /** * Enables the configuration entity. @@ -36,16 +37,6 @@ interface ConfigEntityInterface extends EntityInterface, ThirdPartySettingsInter */ public function setStatus($status); - /** - * Sets the status of the isSyncing flag. - * - * @param bool $status - * The status of the sync flag. - * - * @return $this - */ - public function setSyncing($status); - /** * Returns whether the configuration entity is enabled. * @@ -63,36 +54,6 @@ interface ConfigEntityInterface extends EntityInterface, ThirdPartySettingsInter */ public function status(); - /** - * Returns whether this entity is being changed as part of an import process. - * - * If you are writing code that responds to a change in this entity (insert, - * update, delete, presave, etc.), and your code would result in a - * configuration change (whether related to this configuration entity, another - * configuration entity, or non-entity configuration) or your code would - * result in a change to this entity itself, you need to check and see if this - * entity change is part of an import process, and skip executing your code if - * that is the case. - * - * For example, \Drupal\node\Entity\NodeType::postSave() adds the default body - * field to newly created node type configuration entities, which is a - * configuration change. You would not want this code to run during an import, - * because imported entities were already given the body field when they were - * originally created, and the imported configuration includes all of their - * currently-configured fields. On the other hand, - * \Drupal\field\Entity\FieldStorageConfig::preSave() and the methods it calls - * make sure that the storage tables are created or updated for the field - * storage configuration entity, which is not a configuration change, and it - * must be done whether due to an import or not. So, the first method should - * check $entity->isSyncing() and skip executing if it returns TRUE, and the - * second should not perform this check. - * - * @return bool - * TRUE if the configuration entity is being created, updated, or deleted - * through the import process. - */ - public function isSyncing(); - /** * Returns whether this entity is being changed during the uninstall process. * diff --git a/core/lib/Drupal/Core/Entity/ContentEntityBase.php b/core/lib/Drupal/Core/Entity/ContentEntityBase.php index 2bf1372b619..b01d608c31a 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php @@ -22,6 +22,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C use EntityChangesDetectionTrait { getFieldsToSkipFromTranslationChangesCheck as traitGetFieldsToSkipFromTranslationChangesCheck; } + use SynchronizableEntityTrait; /** * The plain data values of the contained fields. @@ -921,6 +922,7 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C $translation->loadedRevisionId = &$this->loadedRevisionId; $translation->isDefaultRevision = &$this->isDefaultRevision; $translation->enforceRevisionTranslationAffected = &$this->enforceRevisionTranslationAffected; + $translation->isSyncing = &$this->isSyncing; return $translation; } @@ -1219,6 +1221,9 @@ abstract class ContentEntityBase extends Entity implements \IteratorAggregate, C $is_revision_translation_affected_enforced = $this->enforceRevisionTranslationAffected; $this->enforceRevisionTranslationAffected = &$is_revision_translation_affected_enforced; + $is_syncing = $this->isSyncing; + $this->isSyncing = &$is_syncing; + foreach ($this->fields as $name => $fields_by_langcode) { $this->fields[$name] = []; // Untranslatable fields may have multiple references for the same field diff --git a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php index 56cf1397e2b..d01f7ed59ff 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityInterface.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityInterface.php @@ -21,6 +21,6 @@ namespace Drupal\Core\Entity; * * @ingroup entity_api */ -interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, TranslatableRevisionableInterface { +interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, TranslatableRevisionableInterface, SynchronizableInterface { } diff --git a/core/lib/Drupal/Core/Entity/SynchronizableEntityTrait.php b/core/lib/Drupal/Core/Entity/SynchronizableEntityTrait.php new file mode 100644 index 00000000000..4b7f793efd4 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/SynchronizableEntityTrait.php @@ -0,0 +1,36 @@ +isSyncing = $syncing; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function isSyncing() { + return $this->isSyncing; + } + +} diff --git a/core/lib/Drupal/Core/Entity/SynchronizableInterface.php b/core/lib/Drupal/Core/Entity/SynchronizableInterface.php new file mode 100644 index 00000000000..6c9da87a3ad --- /dev/null +++ b/core/lib/Drupal/Core/Entity/SynchronizableInterface.php @@ -0,0 +1,49 @@ +isSyncing() and skip executing if it returns TRUE, and the + * second should not perform this check. + * + * @return bool + * TRUE if the configuration entity is being created, updated, or deleted + * through a synchronization process. + */ + public function isSyncing(); + +} diff --git a/core/modules/workspaces/src/EntityOperations.php b/core/modules/workspaces/src/EntityOperations.php index 857934f07d3..9309fde3d74 100644 --- a/core/modules/workspaces/src/EntityOperations.php +++ b/core/modules/workspaces/src/EntityOperations.php @@ -133,8 +133,8 @@ class EntityOperations implements ContainerInjectionInterface { throw new \RuntimeException('This entity can only be saved in the default workspace.'); } - /** @var \Drupal\Core\Entity\RevisionableInterface|\Drupal\Core\Entity\EntityPublishedInterface $entity */ - if (!$entity->isNew() && !isset($entity->_isReplicating)) { + /** @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\Core\Entity\EntityPublishedInterface $entity */ + if (!$entity->isNew() && !$entity->isSyncing()) { // Force a new revision if the entity is not replicating. $entity->setNewRevision(TRUE); diff --git a/core/modules/workspaces/src/WorkspacePublisher.php b/core/modules/workspaces/src/WorkspacePublisher.php index c6eb993912e..302b1a8e453 100644 --- a/core/modules/workspaces/src/WorkspacePublisher.php +++ b/core/modules/workspaces/src/WorkspacePublisher.php @@ -78,14 +78,11 @@ class WorkspacePublisher implements WorkspacePublisherInterface { foreach ($this->getDifferringRevisionIdsOnSource() as $entity_type_id => $revision_difference) { $entity_revisions = $this->entityTypeManager->getStorage($entity_type_id) ->loadMultipleRevisions(array_keys($revision_difference)); - /** @var \Drupal\Core\Entity\EntityInterface|\Drupal\Core\Entity\RevisionableInterface $entity */ + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ foreach ($entity_revisions as $entity) { // When pushing workspace-specific revisions to the default workspace // (Live), we simply need to mark them as default revisions. - // @todo Remove this dynamic property once we have an API for - // associating temporary data with an entity: - // https://www.drupal.org/node/2896474. - $entity->_isReplicating = TRUE; + $entity->setSyncing(TRUE); $entity->isDefaultRevision(TRUE); $entity->save(); }