You are here

public function PushIntent::execute in CMS Content Sync 8

Same name and namespace in other branches
  1. 2.1.x src/PushIntent.php \Drupal\cms_content_sync\PushIntent::execute()
  2. 2.0.x src/PushIntent.php \Drupal\cms_content_sync\PushIntent::execute()

Push the given entity.

Parameters

bool $return_only:

Return value

bool|PushIntent TRUE|FALSE if the entity is pushed via REST. NULL|PushIntent if $return_only is set to TRUE.

Throws

\Drupal\Core\Entity\EntityStorageException

\Drupal\cms_content_sync\Exception\SyncException

Overrides SyncIntent::execute

File

src/PushIntent.php, line 267

Class

PushIntent
Class PushIntent.

Namespace

Drupal\cms_content_sync

Code

public function execute($return_only = false) {
  $action = $this
    ->getAction();
  $reason = $this
    ->getReason();
  $entity = $this
    ->getEntity();

  /**
   * @var array $deletedTranslations
   *            The translations that have been deleted. Important to notice when
   *            updates must be performed (see ::ACTION_DELETE_TRANSLATION).
   */
  static $deletedTranslations = [];
  if (SyncIntent::ACTION_DELETE_TRANSLATION == $action) {
    $deletedTranslations[$entity
      ->getEntityTypeId()][$entity
      ->uuid()] = true;
    return false;
  }
  if ($entity instanceof TranslatableInterface) {
    $entity = $entity
      ->getUntranslated();
    $this->entity = $entity;
  }

  // If this very request was sent to delete/create this entity, ignore the
  // push as the result of this request will already tell Sync Core it has
  // been deleted. Otherwise Sync Core will return a reasonable 404 for
  // deletions.
  if (PullIntent::entityHasBeenPulledFromRemoteSite($entity
    ->getEntityTypeId(), $entity
    ->uuid())) {
    self::$noPushReasons[$entity
      ->getEntityTypeId()][$entity
      ->uuid()] = self::NO_PUSH_REASON__JUST_PULLED;
    return false;
  }
  $entity_type = $entity
    ->getEntityTypeId();
  $entity_bundle = $entity
    ->bundle();
  $entity_uuid = $entity
    ->uuid();
  $pushed = $this->entity_status
    ->getLastPush();
  if ($pushed) {
    if (SyncIntent::ACTION_CREATE == $action) {
      $action = SyncIntent::ACTION_UPDATE;
    }
  }
  else {
    if (SyncIntent::ACTION_UPDATE == $action) {
      $action = SyncIntent::ACTION_CREATE;
    }
    elseif (SyncIntent::ACTION_DELETE == $action) {
      self::$noPushReasons[$entity
        ->getEntityTypeId()][$entity
        ->uuid()] = self::NO_PUSH_REASON__NEVER_PUSHED;
      return false;
    }
  }
  $cms_content_sync_disable_optimization = boolval(\Drupal::config('cms_content_sync.debug')
    ->get('cms_content_sync_disable_optimization'));
  if (isset(self::$pushed[$action][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id]) && !$return_only) {
    return self::$pushed[$action][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id];
  }
  if (SyncIntent::ACTION_CREATE == $action) {
    if (isset(self::$pushed[SyncIntent::ACTION_UPDATE][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id]) && !$return_only) {
      return self::$pushed[SyncIntent::ACTION_UPDATE][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id];
    }
  }
  elseif (SyncIntent::ACTION_UPDATE == $action) {
    if (isset(self::$pushed[SyncIntent::ACTION_CREATE][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id]) && !$return_only) {
      return self::$pushed[SyncIntent::ACTION_CREATE][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id];
    }
  }

  // No need to retry from this point onward.
  self::$pushed[$action][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id] = true;
  $proceed = true;
  $operation = $this->operation;
  if (SyncIntent::ACTION_DELETE === $action) {
    $operation
      ->delete(SyncIntent::ACTION_DELETE === $action);
  }
  else {
    try {
      $proceed = $this
        ->serialize();
    } catch (\Exception $e) {
      $this
        ->saveFailedPush(PushIntent::PUSH_FAILED_INTERNAL_ERROR, $e
        ->getMessage());
      throw new SyncException(SyncException::CODE_ENTITY_API_FAILURE, $e);
    }
  }

  // If the entity didn't change, it doesn't have to be pushed again.
  // Note that we still serialize the entity above. This is required for the hash
  // of all referenced entities to be created (see PushSingle implementation).
  if (!$cms_content_sync_disable_optimization && !$this
    ->entityChanged() && self::PUSH_FORCED != $reason && SyncIntent::ACTION_DELETE != $action && empty($deletedTranslations[$entity
    ->getEntityTypeId()][$entity
    ->uuid()]) && !$return_only) {
    self::$noPushReasons[$entity
      ->getEntityTypeId()][$entity
      ->uuid()] = self::NO_PUSH_REASON__UNCHANGED;
    return false;
  }
  \Drupal::logger('cms_content_sync')
    ->info('@not @embed PUSH @action @entity_type:@bundle @uuid (hash: @hash) @reason: @message<br>Flow: @flow_id | Pool: @pool_id', [
    '@reason' => $reason,
    '@action' => $action,
    '@entity_type' => $entity_type,
    '@bundle' => $entity_bundle,
    '@uuid' => $entity_uuid,
    '@not' => $proceed ? '' : 'NO',
    '@embed' => $return_only ? 'EMBEDDING' : '',
    '@hash' => $this->operation
      ->getEntityHash(),
    '@message' => $proceed ? t('The entity has been pushed.') : t('The entity handler denied to push this entity.'),
    '@flow_id' => $this
      ->getFlow()
      ->id(),
    '@pool_id' => $this
      ->getPool()
      ->id(),
  ]);

  // Handler chose to deliberately ignore this entity,
  // e.g. a node that wasn't published yet and is not pushed unpublished.
  if (!$proceed) {
    self::$noPushReasons[$entity
      ->getEntityTypeId()][$entity
      ->uuid()] = self::NO_PUSH_REASON__HANDLER_IGNORES;
    $this
      ->saveFailedPush(PushIntent::PUSH_FAILED_HANDLER_DENIED);
    unset(self::$pushed[$action][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id]);
    $this
      ->extendedEntityExportLogMessage($entity);
    return $return_only ? null : false;
  }

  // We need to update the revision timestamp as otherwise the change won't be propagated by the Sync Core.
  if ($this->isQuickEdited) {
    $revision_timestamp = $operation
      ->getProperty('revision_timestamp');
    if (!empty($revision_timestamp[0]['value']) && $revision_timestamp[0]['value'] < $this
      ->getRequestTime()) {
      $revision_timestamp[0]['value'] = $this
        ->getRequestTime();
      $this->operation
        ->setProperty('revision_timestamp', $revision_timestamp);
    }
  }
  $this->entityVersionHash = $this->flow
    ->getEntityTypeConfig($entity
    ->getEntityTypeId(), $entity
    ->bundle())['version'];

  // If the version changed, UPDATE becomes CREATE instead and DELETE requests must be performed against the old
  // version, as otherwise they would result in a 404 Not Found response.
  if ($this->entityVersionHash != $this->entity_status
    ->getEntityTypeVersion()) {
    if (SyncIntent::ACTION_UPDATE == $action) {
      $action = SyncIntent::ACTION_CREATE;
    }
    elseif (SyncIntent::ACTION_DELETE == $action) {
      $this->entityVersionHash = $this->entity_status
        ->getEntityTypeVersion();
    }
  }
  if ($return_only) {
    self::$pushed[$action][$entity_type][$entity_bundle][$entity_uuid][$this->pool->id] = $this;
    $this
      ->extendedEntityExportLogMessage($entity);
    return $this;
  }
  try {
    $operation
      ->execute();
  } catch (SyncCoreException $e) {
    \Drupal::logger('cms_content_sync')
      ->error('Failed to @action entity @entity_type-@entity_bundle @entity_uuid' . PHP_EOL . '@message' . PHP_EOL . 'Got status code @status_code @reason_phrase with body:' . PHP_EOL . '@body<br>Flow: @flow_id | Pool: @pool_id', [
      '@action' => $action,
      '@entity_type' => $entity_type,
      '@entity_bundle' => $entity_bundle,
      '@entity_uuid' => $entity_uuid,
      '@message' => $e
        ->getMessage(),
      '@status_code' => $e
        ->getStatusCode(),
      '@reason_phrase' => $e
        ->getReasonPhrase(),
      '@body' => $e
        ->getResponseBody() . '',
      '@flow_id' => $this
        ->getFlow()
        ->id(),
      '@pool_id' => $this
        ->getPool()
        ->id(),
    ]);
    $this
      ->saveFailedPush(PushIntent::PUSH_FAILED_REQUEST_FAILED, $e
      ->getMessage());
    throw new SyncException(SyncException::CODE_PUSH_REQUEST_FAILED, $e);
  }
  $this
    ->updateEntityStatusAfterSuccessfulPush($action);

  // Dispatch entity push event to give other modules the possibility to react on it.
  \Drupal::service('event_dispatcher')
    ->dispatch(AfterEntityPush::EVENT_NAME, new AfterEntityPush($entity, $this->pool, $this->flow, $this->reason, $this->action));
  $this
    ->extendedEntityExportLogMessage($entity);
  return true;
}