Issue #2985297 by amateescu, hchonov: Generalize the concept of entity synchronization
parent
36ecfc620a
commit
cb5774fe4b
|
@ -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}
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -21,6 +21,6 @@ namespace Drupal\Core\Entity;
|
|||
*
|
||||
* @ingroup entity_api
|
||||
*/
|
||||
interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, TranslatableRevisionableInterface {
|
||||
interface ContentEntityInterface extends \Traversable, FieldableEntityInterface, TranslatableRevisionableInterface, SynchronizableInterface {
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Entity;
|
||||
|
||||
/**
|
||||
* Provides a trait for accessing synchronization information.
|
||||
*
|
||||
* @ingroup entity_api
|
||||
*/
|
||||
trait SynchronizableEntityTrait {
|
||||
|
||||
/**
|
||||
* Whether this entity is being created, updated or deleted through a
|
||||
* synchronization process.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isSyncing = FALSE;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setSyncing($syncing) {
|
||||
$this->isSyncing = $syncing;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isSyncing() {
|
||||
return $this->isSyncing;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Drupal\Core\Entity;
|
||||
|
||||
/**
|
||||
* Defines methods for an entity that supports synchronization.
|
||||
*/
|
||||
interface SynchronizableInterface {
|
||||
|
||||
/**
|
||||
* Sets the status of the synchronization flag.
|
||||
*
|
||||
* @param bool $status
|
||||
* The status of the synchronization flag.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSyncing($status);
|
||||
|
||||
/**
|
||||
* Returns whether this entity is being changed as part of a synchronization.
|
||||
*
|
||||
* 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 change to
|
||||
* this entity itself, a configuration change (whether related to this entity,
|
||||
* another entity, or non-entity configuration), you need to check and see if
|
||||
* this entity change is part of a synchronization 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 a synchronization process.
|
||||
*/
|
||||
public function isSyncing();
|
||||
|
||||
}
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue