Skip to content
Snippets Groups Projects

Draft: #3502902 pull logic from ApiAutoSaveController to update entity from auto-save data

Closed Draft: #3502902 pull logic from ApiAutoSaveController to update entity from auto-save data
6 unresolved threads
Closed Ted Bowman requested to merge issue/experience_builder-3502902:3502902-doomed-idea into 0.x
6 unresolved threads
2 files
+ 85
17
Compare changes
  • Side-by-side
  • Inline
Files
2
@@ -3,9 +3,16 @@
namespace Drupal\experience_builder\AutoSave;
use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\TranslatableInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\experience_builder\AutoSaveData;
use Drupal\experience_builder\ClientDataToEntityConverter;
use Drupal\experience_builder\Entity\PageRegion;
use Drupal\experience_builder\Entity\XbHttpApiEligibleConfigEntityInterface;
use Drupal\experience_builder\Plugin\DisplayVariant\XbPageVariant;
/**
* Defines a class for storing and retrieving auto-save data.
@@ -17,6 +24,7 @@ class AutoSaveManager {
public function __construct(
private readonly AutoSaveTempStoreFactory $tempStoreFactory,
private readonly CacheTagsInvalidatorInterface $cacheTagsInvalidator,
private readonly ClientDataToEntityConverter $clientDataToEntityConverter,
) {
}
@@ -28,8 +36,15 @@ class AutoSaveManager {
return $this->tempStoreFactory->get('experience_builder.auto_save', expire: $expire);
}
public function save(EntityInterface $entity, array $data): void {
public function save(EntityInterface $entity, array $data): bool {
$key = $this->getAutoSaveKey($entity);
if ($this->entityMatchesAutoSaveData($entity, $data)) {
if ($this->getTempStore()->get($key)) {
$this->getTempStore()->delete($key);
$this->cacheTagsInvalidator->invalidateTags([self::CACHE_TAG]);
    • Comment on lines +43 to +44
      Author Maintainer

      We may have had an previous state in auto-save that didn't match the saved entity. but if the client now matches we should delete auto-saved version.

      This would happen if say code component briefly changed the JS to something other the saved version but then reverted that change. there would be no longer a need for an auto-saved state and it should not show up in lists.

Please register or sign in to reply
}
return FALSE;
}
$auto_save_data = [
'entity_type' => $entity->getEntityTypeId(),
'entity_id' => $entity->id(),
@@ -40,6 +55,7 @@ class AutoSaveManager {
];
$this->getTempStore()->set($key, $auto_save_data);
$this->cacheTagsInvalidator->invalidateTags([self::CACHE_TAG]);
return TRUE;
}
public static function getLabelToSave(EntityInterface $entity, array $data): string {
@@ -96,4 +112,66 @@ class AutoSaveManager {
$this->getTempStore()->deleteAll();
}
public function entityMatchesAutoSaveData(EntityInterface $entity, array $data): bool {
$original_entity = clone $entity;
$entity = $this->updateEntityWithAutoSaveData($entity, $data);
    • Author Maintainer

      in this idea to update the saved entity with the auto-saved data and then compare. If it is equal we no longer need the auto-saved

Please register or sign in to reply
if (!$entity) {
return FALSE;
}
// Determine if the entity has changed.
Please register or sign in to reply
if ($entity instanceof ContentEntityInterface) {
foreach ($entity as $field_name => $field) {
if ($field_name === 'revision_log') {
continue;
}
assert($field instanceof FieldItemListInterface);
$original_field = $original_entity->get($field_name);
assert($original_field instanceof FieldItemListInterface);
if (!$field->equals($original_field)) {
return FALSE;
}
}
return TRUE;
}
else {
return $entity->toArray() === $original_entity->toArray();
Please register or sign in to reply
}
}
/**
* This was originally in \Drupal\experience_builder\Controller\ApiAutoSaveController::post().
*/
public function updateEntityWithAutoSaveData(EntityInterface $entity, array $data, bool $validate = FALSE): ?EntityInterface {
    • Comment on lines +142 to +144
      Author Maintainer

      In the original version of the code in ApiAutoSaveController::post() it updated the entities and validated them. Here I tried to remove the validate logic and leave it in ApiAutoSaveController::post(), but \Drupal\experience_builder\ClientDataToEntityConverter::convert has built-in logic for validation so I added the argument which would be true when called from ApiAutoSaveController::post()

      Edited by Ted Bowman
Please register or sign in to reply
if ($entity instanceof PageRegion) {
$entity = $entity->forAutoSaveData($data);
return $entity;
}
elseif ($entity instanceof XbHttpApiEligibleConfigEntityInterface) {
$denormalized = $entity::denormalizeFromClientSide($data);
foreach ($denormalized as $property_name => $property_value) {
$entity->set($property_name, $property_value);
}
}
else {
assert($entity instanceof FieldableEntityInterface);
// 🥺 This method probably won't work because using `convert()` assumes the data
// is some valid, for instance must have real components.
// Pluck out only the content region.
$content_region = \array_values(\array_filter($data['layout'], static fn(array $region) => $region['id'] === XbPageVariant::MAIN_CONTENT_REGION));
try {
$this->clientDataToEntityConverter->convert([
'layout' => reset($content_region),
'model' => $data['model'],
'entity_form_fields' => $data['entity_form_fields'],
], $entity, $validate);
}
catch (\Throwable) {
return NULL;
}
}
return $entity;
}
}
Loading