flow-1.0.0-beta8/src/Entity/EntitySaveHandler.php

src/Entity/EntitySaveHandler.php
<?php

namespace Drupal\flow\Entity;

use Drupal\Core\Entity\RevisionableInterface;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\flow\Flow;
use Drupal\flow\Helpers\EntityRepositoryTrait;

/**
 * Handles the saving of multiple entities.
 */
class EntitySaveHandler {

  use EntityRepositoryTrait;

  /**
   * The saving list threshold.
   *
   * When the number of entities in a saving list surpasses this value, then
   * saving will be performed.
   *
   * @var int
   */
  protected static int $threshold = 20;

  /**
   * Constructs a new entity save handler instance.
   *
   * @param int $threshold
   *   The saving list threshold.
   */
  public function __construct(int $threshold = 20) {
    static::$threshold = $threshold;
  }

  /**
   * Get the entity save handler service.
   *
   * @return \Drupal\flow\Entity\EntitySaveHandler
   *   The entity save handler.
   */
  public static function service(): EntitySaveHandler {
    return \Drupal::service('flow.entity_save_handler');
  }

  /**
   * Saves the given list of entities, if it makes sense to do so.
   *
   * Any entity that got saved will be removed from the given list.
   * This method should only be called when it's guaranteed that somewhere later
   * within the same PHP process ::ensureSave() will be finally called.
   *
   * @param \Drupal\Core\Entity\EntityInterface[] &$entities
   *   The list of entities to save.
   */
  public function saveIfRequired(array &$entities): void {
    if (count($entities) > static::$threshold) {
      $this->ensureSave($entities);
      return;
    }
    if (count($entities) > 1) {
      foreach ($entities as $entity) {
        foreach ($entity->referencedEntities() as $referenced) {
          if ($referenced->isNew() && in_array($referenced, $entities, TRUE) && ($referenced !== $entity)) {
            $this->ensureSave($entities);
            return;
          }
        }
      }
    }
  }

  /**
   * Ensures that all entities are being saved that are in the given list.
   *
   * Any entity that is ensured to be saved (or got already saved) will be
   * removed from the given list. Entities may remain in the list, when they
   * are not safe to be saved now, but instead to be saved later. No further
   * call of ::ensureSave() is needed, as this handler guarantees the save.
   *
   * @param \Drupal\Core\Entity\EntityInterface[] &$entities
   *   The list of entities to save.
   */
  public function ensureSave(array &$entities): void {
    /** @var \Drupal\Core\Entity\ContentEntityInterface[] $ensured */
    $ensured = [];
    // Treat entities laying in the Flow stack as already ensured for saving.
    foreach (Flow::$stack as &$stacked_entities) {
      foreach ($stacked_entities as $stacked_entity) {
        if (!in_array($stacked_entity, $ensured, TRUE)) {
          if ($uuid = $stacked_entity->uuid()) {
            $ensured[$uuid] = $stacked_entity;
          }
          else {
            $ensured[] = $stacked_entity;
          }
        }
      }
    }
    foreach ($entities as $i => $entity) {
      if (in_array($entity, $ensured, TRUE) || ($entity->uuid() && isset($ensured[$entity->uuid()]))) {
        unset($entities[$i]);
        continue;
      }
      if ($entity->isNew() && $entity->uuid() && ($loaded = $this->getEntityRepository()->loadEntityByUuid($entity->getEntityTypeId(), $entity->uuid()))) {
        if (!$loaded->isNew()) {
          // Entity got already saved.
          unset($entities[$i]);
          continue;
        }
      }
      foreach ($entity->referencedEntities() as $referenced) {
        if ($referenced->isNew() && (in_array($referenced, $ensured, TRUE) || ($referenced->uuid() && isset($ensured[$referenced->uuid()])))) {
          // When the contained reference is in the list of ensured entities,
          // and the reference is new, then it is within its own saving process.
          // For that case, this entity needs to be saved afterwards. Otherwise
          // it would cause an infinite loop of save attempts, because new
          // referenced entities are being automatically saved by
          // EntityReferenceItem. Saving the entity afterwards will happen, if
          // not otherwise done, handled by flow_entity_insert() and
          // flow_entity_update() that both call _flow_process_after_task().
          if ($entity->isNew()) {
            // When both entities are new, we need to take care of it below.
            $ensured_new[] = $referenced;
            continue;
          }
          continue 2;
        }
      }
      if ($entity->isNew()) {
        // Take a look whether this new entity is being referenced from one
        // of the ensured entities. If so, we need to take care by properly
        // saving it, before it can be referenced from an ensured entity.
        $entity_saved = FALSE;
        /** @var \Drupal\Core\Entity\ContentEntityInterface $ensured_entity */
        foreach ($ensured as $ensured_entity) {
          $is_referenced = in_array($entity, $ensured_entity->referencedEntities(), TRUE);
          if ($is_referenced && !$entity_saved) {
            if (!empty($ensured_new)) {
              // For the case both entities are new, it is a problem of what
              // came first. The entity needs to be saved for being referenced,
              // but this would cause an infinite loop as EntityReferenceItem
              // would automatically try to save the other one too. Therefore
              // the new ones need to be filtered out, save the entity, re-add
              // the new ones and let this one to be saved again afterwards.
              $unfiltered_lists = [];
              $filtered_lists = [];
              foreach ($entity as $field_name => $item_list) {
                if ($item_list instanceof EntityReferenceFieldItemListInterface) {
                  $referenced_entities = $item_list->referencedEntities();
                  foreach ($ensured_new as $ensured_new_entity) {
                    if (in_array($ensured_new_entity, $referenced_entities, TRUE)) {
                      $unfiltered_lists[$field_name] = $unfiltered_lists[$field_name] ?? $referenced_entities;
                      $filtered_lists[$field_name] = array_filter($filtered_lists[$field_name] ?? $referenced_entities, function ($referenced_entity) use ($ensured_new_entity) {
                        return $referenced_entity !== $ensured_new_entity;
                      });
                    }
                  }
                }
              }
              foreach ($filtered_lists as $field_name => $references) {
                /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
                $entity->get($field_name)->setValue($references);
              }
            }
            $flow_is_active = Flow::isActive();
            Flow::setActive(FALSE);
            try {
              $entity->save();
              $entity_saved = TRUE;
              if (!empty($unfiltered_lists)) {
                foreach ($unfiltered_lists as $field_name => $references) {
                  /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
                  $entity->get($field_name)->setValue($references);
                }
              }
              if ($entity instanceof RevisionableInterface) {
                // Another save will follow.
                $entity->updateLoadedRevisionId();
                $entity->setNewRevision(FALSE);
              }
            }
            finally {
              Flow::setActive($flow_is_active);
            }
          }
          if ($is_referenced) {
            foreach ($ensured_entity as $field_name => $item_list) {
              if ($item_list instanceof EntityReferenceFieldItemListInterface) {
                foreach ($item_list as $item) {
                  if ($item->entity === $entity) {
                    $item->setValue($entity);
                  }
                }
              }
            }
          }
        }
        if (!empty($ensured_new)) {
          // Save the entity again afterwards.
          $after_save[] = $entity;
          continue;
        }
      }
      unset($entities[$i]);
      $entity->save();
      if ($uuid = $entity->uuid()) {
        $ensured[$uuid] = $entity;
      }
      else {
        $ensured[] = $entity;
      }
    }
    if (!empty($after_save)) {
      foreach ($after_save as $entity) {
        array_push(Flow::$save, $entity);
      }
    }
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc