cms_content_sync-3.0.x-dev/src/Plugin/rest/resource/SyncCoreEntityListResource.php

src/Plugin/rest/resource/SyncCoreEntityListResource.php
<?php

namespace Drupal\cms_content_sync\Plugin\rest\resource;

use Drupal\cms_content_sync\Controller\LoggerProxy;
use Drupal\cms_content_sync\Entity\EntityStatus;
use Drupal\cms_content_sync\Entity\Flow;
use Drupal\cms_content_sync\Exception\SyncException;
use Drupal\Core\Entity\EntityRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfo;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Render\Renderer;
use Drupal\rest\Plugin\ResourceBase;
use EdgeBox\SyncCore\Interfaces\IApplicationInterface;
use EdgeBox\SyncCore\V2\Raw\Model\RemoteEntityListRequestMode;
use EdgeBox\SyncCore\V2\Raw\Model\RemoteEntityListResponse;
use EdgeBox\SyncCore\V2\Raw\Model\RemoteRequestQueryParamsEntityList;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides entity interfaces for Content Sync, allowing Sync Core v2 to
 * list entities.
 *
 * @RestResource(
 *   id = "cms_content_sync_sync_core_entity_list",
 *   label = @Translation("Content Sync: Sync Core: Entity list"),
 *   uri_paths = {
 *     "canonical" = "/rest/cms-content-sync/v2/{flow_id}"
 *   }
 * )
 */
class SyncCoreEntityListResource extends ResourceBase implements ContentSyncRestInterface {
  use ContentSyncRestTrait;

  protected const NONE = 'null';

  /**
   * @var \Drupal\Core\Entity\EntityTypeBundleInfo
   */
  protected $entityTypeBundleInfo;

  /**
   * @var \Drupal\Core\Entity\EntityTypeManager
   */
  protected $entityTypeManager;

  /**
   * @var \Drupal\Core\Render\Renderer
   */
  protected $renderedManager;

  /**
   * @var \Drupal\Core\Entity\EntityRepositoryInterface
   */
  protected $entityRepository;

  /**
   * Constructs an object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param array $serializer_formats
   *   The available serialization formats.
   * @param \Psr\Log\LoggerInterface $logger
   *   A logger instance.
   * @param \Drupal\Core\Entity\EntityTypeBundleInfo $entity_type_bundle_info
   *   An entity type bundle info instance.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   An entity type manager instance.
   * @param \Drupal\Core\Render\Renderer $render_manager
   *   A rendered instance.
   * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository
   *   The entity repository interface.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    array $serializer_formats,
    LoggerInterface $logger,
    EntityTypeBundleInfo $entity_type_bundle_info,
    EntityTypeManagerInterface $entity_type_manager,
    Renderer $render_manager,
    EntityRepositoryInterface $entity_repository,
  ) {
    parent::__construct(
          $configuration,
          $plugin_id,
          $plugin_definition,
          $serializer_formats,
          $logger
      );

    $this->entityTypeBundleInfo = $entity_type_bundle_info;
    $this->entityTypeManager = $entity_type_manager;
    $this->renderedManager = $render_manager;
    $this->entityRepository = $entity_repository;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(
    ContainerInterface $container,
    array $configuration,
    $plugin_id,
    $plugin_definition,
  ) {
    return new static(
    $configuration,
    $plugin_id,
    $plugin_definition,
    $container->getParameter('serializer.formats'),
    LoggerProxy::get(),
    $container->get('entity_type.bundle.info'),
    $container->get('entity_type.manager'),
    $container->get('renderer'),
    $container->get('entity.repository')
    );
  }

  /**
   *
   */
  public function get($flow_id) {
    $query = \Drupal::request()->query->all();
    $queryObject = new RemoteRequestQueryParamsEntityList($query);

    $entity_type = $queryObject->getNamespaceMachineName();
    $bundle = $queryObject->getMachineName();

    $flow = Flow::getAll()[$flow_id] ?? NULL;
    if (empty($flow)) {
      if (IApplicationInterface::FLOW_NONE === $flow_id) {
        $flow = new Flow([
          'id' => $flow_id,
          'name' => 'Virtual',
          'type' => Flow::TYPE_PUSH,
          'variant' => Flow::VARIANT_SIMPLE,
          'simple_settings' => [
            'poolAssignment' => 'force',
            'mode' => 'automatically',
            'deletions' => FALSE,
            'updateBehavior' => 'ignore',
            'ignoreUnpublishedChanges' => FALSE,
            'allowExplicitUnpublishing' => FALSE,
            'pushMenuItems' => FALSE,
            'pushPreviews' => FALSE,
            'mergeLocalChanges' => FALSE,
            'resolveUserReferences' => 'name',
            'poolSelectionWidget' => 'checkboxes',
            'entityTypeSettings' => [
              $entity_type => [
                'perBundle' => [
                  $bundle => [
                    'mode' => 'default',
                  ],
                ],
              ],
            ],
          ],
        ], 'cms_content_sync_flow');
        $flow->getController()->isVirtual(TRUE);
      }
      else {
        $message = t("The flow @flow_id doesn't exist.", ['@flow_id' => $flow_id])->render();
        $this->logger->notice('@not LIST: @message', [
          '@not' => 'NO',
          '@message' => $message,
        ]);

        return $this->respondWith(
              ['message' => $message],
              self::
          CODE_NOT_FOUND,
          FALSE
                );
      }
    }

    $mode = $queryObject->getMode();
    if (!$mode) {
      return $this->returnError(t('The mode query parameter is required.')->render());
    }

    if (RemoteEntityListRequestMode::ALL === $mode) {
      if (!$entity_type || !$bundle) {
        return $this->returnError(t("The type and bundle query parameters are required for mode 'all'.")->render());
      }
    }

    $page = (int) $queryObject->getPage();
    if (!$page) {
      $page = 0;
    }

    $items_per_page = (int) $queryObject->getItemsPerPage();
    if (!$items_per_page) {
      $items_per_page = 0;
    }

    $mode = $queryObject->getMode();

    // Need to convert miliseconds to seconds.
    $changed_after = $queryObject->getChangedAfter() ? floor((int) $queryObject->getChangedAfter() / 1000) : NULL;

    $skip = $page * $items_per_page;

    $database = \Drupal::database();

    /**
     * @var \EdgeBox\SyncCore\V2\Raw\Model\RemoteEntitySummary[] $items
     */
    $items = [];

    try {
      // If ALL entities are requested, we can't rely on the status entity.
      // Instead, we query for these entities by their type's table directly.
      if (RemoteEntityListRequestMode::ALL === $mode) {
        $config = $flow->getController()->getEntityTypeConfig($entity_type, $bundle);
        $handler = $flow->getController()->getEntityTypeHandler($entity_type, $bundle, $config);
        $result = $handler->getSyncCoreList($flow, $queryObject);
      }
      else {
        $query = $database->select('cms_content_sync_entity_status', 'cses');

        $base_table = NULL;
        if ($entity_type && $bundle) {
          $entity_type_storage = \Drupal::entityTypeManager()->getStorage($entity_type);
          if ($entity_type_storage instanceof SqlContentEntityStorage) {
            $bundle_key = $entity_type_storage->getEntityType()->getKey('bundle');
            $base_table = $entity_type_storage->getBaseTable();
            if ($base_table) {
              $query->leftJoin($base_table, 'bt', 'bt.uuid = cses.entity_uuid');
            }
          }
        }

        $query
          ->condition('cses.flow', $flow_id);

        $changed_field = RemoteEntityListRequestMode::PULLED === $mode ? 'last_import' : 'last_export';
        if ($changed_after) {
          $query->condition('cses.' . $changed_field, $changed_after, '>');
        }
        elseif (RemoteEntityListRequestMode::PULLED === $mode || RemoteEntityListRequestMode::PUSHED === $mode) {
          $query->condition('cses.' . $changed_field, 0, '>');
        }

        if ($entity_type) {
          $query
            ->condition('cses.entity_type', $entity_type);
          if ($bundle && !empty($bundle_key) && $base_table) {
            $or = $query->orConditionGroup()
              ->where('flags&:deleted_flag=:deleted_flag', [':deleted_flag' => EntityStatus::FLAG_DELETED])
              ->condition('bt.' . $bundle_key, $bundle);
            $query->condition($or);
          }
        }

        if (RemoteEntityListRequestMode::PUSH_FAILED === $mode) {
          $query
            ->where('flags&:push_failed_flag=:push_failed_flag', [':push_failed_flag' => EntityStatus::FLAG_PUSH_FAILED]);
        }

        $query->addExpression('MIN(cses.id)', 'min_id');
        $query
          ->orderBy('min_id', 'ASC')
          ->fields('cses', ['entity_type', 'entity_uuid']);

        $query->groupBy('cses.entity_type');
        $query->groupBy('cses.entity_uuid');

        $total_number_of_items = (int) $query->countQuery()->execute()->fetchField();

        if ($total_number_of_items && $items_per_page) {
          $query->range($skip, $items_per_page);
          $ids = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
          foreach ($ids as $id) {
            $entity = \Drupal::service('entity.repository')->loadEntityByUuid(
                  $id['entity_type'],
                  $id['entity_uuid']
              );

            $handler_type = $entity_type ?? $id['entity_type'];
            $handler_bundle = $bundle ?? ($entity ? $entity->bundle() : '*');

            $config = $flow->getController()->getEntityTypeConfig($handler_type, $handler_bundle);
            if (empty($config)) {
              continue;
            }

            $handler = $flow->getController()->getEntityTypeHandler($handler_type, $handler_bundle, $config);
            if (empty($handler)) {
              continue;
            }

            $items[] = $handler->getSyncCoreListItem($flow, $entity, EntityStatus::getInfosForEntity($id['entity_type'], $id['entity_uuid'], ['flow' => $flow_id]));
          }
        }

        if (!$items_per_page) {
          $number_of_pages = $total_number_of_items;
        }
        else {
          $number_of_pages = ceil($total_number_of_items / $items_per_page);
        }

        $result = new RemoteEntityListResponse();
        $result->setPage($page);
        $result->setNumberOfPages($number_of_pages);
        $result->setItemsPerPage($items_per_page);
        $result->setTotalNumberOfItems($total_number_of_items);
        $result->setItems($items);
      }

      $body = $result->jsonSerialize();

      // Turn objects into arrays.
      return $this->respondWith(json_decode(json_encode($body), TRUE), self::CODE_OK, FALSE);
    }
    catch (SyncException $e) {
      $message = $e->getSyncExceptionMessage();
      $this->logger->notice('@not LIST: @message', [
        '@not' => 'NO',
        '@message' => $message,
      ]);

      return $this->respondWith(
            $e->serialize(),
            self::
        CODE_INTERNAL_SERVER_ERROR,
        FALSE
            );
    }
    catch (\Exception $e) {
      $this->logger->notice('@not LIST: @message', [
        '@not' => 'NO',
        '@message' => $e->getMessage(),
      ]);

      return $this->respondWith(
            [
              'message' => 'Unexpected error: ' . $e->getMessage(),
              'stack' => $e->getTraceAsString(),
            ],
            self::
            CODE_INTERNAL_SERVER_ERROR,
            FALSE
            );
    }
  }

  /**
   *
   */
  protected function returnError($message, $code = self::CODE_BAD_REQUEST) {
    $this->logger->notice('@not LIST: @message', [
      '@not' => 'NO',
      '@message' => $message,
    ]);

    return $this->respondWith(
          ['message' => $message],
      $code,
      FALSE
      );
  }

}

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

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