media_mpx-8.x-1.x-dev/src/DataObjectImporter.php

src/DataObjectImporter.php
<?php

namespace Drupal\media_mpx;

use GuzzleHttp\Psr7\Query;
use GuzzleHttp\Psr7\Uri;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\file\FileInterface;
use Drupal\guzzle_cache\DrupalGuzzleCache;
use Drupal\media\Entity\Media;
use Drupal\media\MediaInterface;
use Drupal\media\MediaTypeInterface;
use Drupal\media_mpx\Event\ImportEvent;
use Drupal\media_mpx\Plugin\media\Source\MpxMediaSourceInterface;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use Lullabot\Mpx\DataService\ObjectInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Import an mpx item into a media entity.
 */
class DataObjectImporter {

  /**
   * The request headers to use for our injected cache responses.
   */
  const REQUEST_HEADERS = [
    'Accept' =>
      [
        'application/json',
      ],
    'Content-Type' =>
      [
        'application/json',
      ],
    'Host' =>
      [
        'read.data.media.theplatform.com',
      ],
  ];

  /**
   * The entity type manager used to load existing media entities.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The mpx cache strategy for injecting cache items.
   *
   * @var \Drupal\media_mpx\MpxCacheStrategy
   */
  private $cache;

  /**
   * The system event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  private $eventDispatcher;

  /**
   * DataObjectImporter constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager used to load existing media entities.
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher
   *   The system event dispatcher.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
   *   The cache backend to store HTTP responses in.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager, EventDispatcherInterface $eventDispatcher, CacheBackendInterface $cacheBackend) {
    $this->entityTypeManager = $entityTypeManager;
    $this->eventDispatcher = $eventDispatcher;
    $this->cache = new MpxCacheStrategy(new DrupalGuzzleCache($cacheBackend), 3600 * 24 * 30);
  }

  /**
   * Unpublish a media entity for mpx object of the given id and type.
   *
   * @param \Lullabot\Mpx\DataService\ObjectInterface $mpx_object
   *   The mpx object.
   * @param \Drupal\media\MediaTypeInterface $media_type
   *   The media type to import to.
   *
   * @return \Drupal\media\MediaInterface[]
   *   The array of media entities that were imported.
   */
  public function unpublishItem(ObjectInterface $mpx_object, MediaTypeInterface $media_type): array {
    // Find any existing media items.
    $entities = $this->loadMediaEntitiesById($media_type, $mpx_object->getId());
    foreach ($entities as $entity) {
      // Set as unpublished and save this change.
      $entity->setUnpublished();
      $entity->save();
    }
    // Return unpublished entities.
    return $entities;
  }

  /**
   * Import an mpx object into a media entity of the given type.
   *
   * @param \Lullabot\Mpx\DataService\ObjectInterface $mpx_object
   *   The mpx object.
   * @param \Drupal\media\MediaTypeInterface $media_type
   *   The media type to import to.
   *
   * @return \Drupal\media\MediaInterface[]
   *   The array of media entities that were imported.
   */
  public function importItem(ObjectInterface $mpx_object, MediaTypeInterface $media_type): array {
    // Store an array of media items we touched, so we can clear out their
    // static cache.
    $reset_ids = [];
    // @todo start a transaction.
    // Find any existing media items, or return a new one.
    $results = $this->loadMediaEntities($media_type, $mpx_object);

    // Allow other modules to alter the media entities as they are imported.
    $event = new ImportEvent($mpx_object, $results);
    $this->eventDispatcher->dispatch($event, ImportEvent::IMPORT);
    $results = $event->getEntities();

    foreach ($results as $media) {
      // @todo This can be replaced by calling $media->updateMetadata() when
      // https://www.drupal.org/project/drupal/issues/2878119 is merged.
      $this->updateMetadata($media);
      $media->save();
      $reset_ids[] = $media->id();
    }

    return $results;
  }

  /**
   * Load all media entities for a given mpx object, or return a new stub.
   *
   * @param \Drupal\media\MediaTypeInterface $media_type
   *   The media type to load all entities for.
   * @param \Lullabot\Mpx\DataService\ObjectInterface $mpx_object
   *   The mpx object to load the associated entities for.
   *
   * @return \Drupal\media\Entity\Media[]
   *   An array of existing media entities or a new media entity.
   */
  protected function loadMediaEntities(MediaTypeInterface $media_type, ObjectInterface $mpx_object): array {
    // Find any existing media items.
    $results = $this->loadMediaEntitiesById($media_type, $mpx_object->getId());

    // Create a new entity owned by the admin user.
    if (empty($results)) {
      ['source_field' => $source_field, 'media_storage' => $media_storage] = $this->getSourceFieldAndStorage($media_type);
      /** @var \Drupal\media\Entity\Media $new_media_entity */
      $new_media_entity = $media_storage->create([
        $this->entityTypeManager->getDefinition('media')
          ->getKey('bundle') => $media_type->id(),
        'uid' => 1,
      ]);
      $new_media_entity->set($source_field->getName(), $mpx_object->getId());
      $results = [$new_media_entity];
    }

    return $results;
  }

  /**
   * Load all media entities for a given mpx object.
   *
   * @param \Drupal\media\MediaTypeInterface $media_type
   *   The media type to load all entities for.
   * @param GuzzleHttp\Psr7\Uri $mpx_object_id
   *   The mpx object id to load the associated
   *    entities for represente by UriInterface.
   *
   * @return \Drupal\media\Entity\Media[]
   *   An array of existing media entities or a new media entity.
   */
  protected function loadMediaEntitiesById(MediaTypeInterface $media_type, Uri $mpx_object_id): array {
    ['source_field' => $source_field, 'media_storage' => $media_storage] = $this->getSourceFieldAndStorage($media_type);
    $results = $media_storage->loadByProperties([$source_field->getName() => (string) $mpx_object_id]);
    return $results;
  }

  /**
   * Get source field and storage for certain media type.
   *
   * @param \Drupal\media\MediaTypeInterface $media_type
   *   The media type to load all entities for.
   *
   * @return \Drupal\media\Entity\Media[]
   *   An array of existing media entities or a new media entity.
   */
  protected function getSourceFieldAndStorage(MediaTypeInterface $media_type): array {
    $media_source = $this->loadMediaSource($media_type);
    $source_field = $media_source->getSourceFieldDefinition($media_type);
    $media_storage = $this->entityTypeManager->getStorage('media');
    return ['source_field' => $source_field, 'media_storage' => $media_storage];
  }

  /**
   * Return the media source plugin for a given media type.
   *
   * @param \Drupal\media\MediaTypeInterface $media_type
   *   The media type object to load the source plugin for.
   *
   * @return \Drupal\media_mpx\Plugin\media\Source\Media
   *   The source plugin.
   */
  public static function loadMediaSource(MediaTypeInterface $media_type): MpxMediaSourceInterface {
    /** @var \Drupal\media_mpx\Plugin\media\Source\Media $media_source */
    $media_source = $media_type->getSource();
    if (!($media_source instanceof MpxMediaSourceInterface)) {
      throw new \RuntimeException(dt('@type is not configured as a mpx Media source.', ['@type' => $media_type->id()]));
    }
    return $media_source;
  }

  /**
   * Update the media entity overwriting fields with remote data.
   *
   * This code and all code called from it is adapted from an upstream patch.
   * Since the upstream patch marks updateMetadata as internal, we copy the code
   * here.
   *
   * @param \Drupal\media\MediaInterface $media
   *   The media entity to update.
   *
   * @see https://www.drupal.org/project/drupal/issues/2878119
   */
  protected function updateMetadata(MediaInterface $media): void {
    foreach (array_keys($media->getTranslationLanguages()) as $langcode) {
      $translation = $media->getTranslation($langcode);
      $field_map = $media->bundle->entity->getFieldMap();
      $source = $media->getSource();
      foreach ($field_map as $attribute_name => $field_name) {
        $media->set($field_name, $source->getMetadata($media, $attribute_name));
      }
      $this->updateThumbnail($media);

      // If the media item does not have a title yet, get a default name from
      // the metadata.
      if ($translation->get('name')->isEmpty()) {
        $media_source = $media->getSource();
        $translation->setName($media_source->getMetadata($media, $media_source->getPluginDefinition()['default_name_metadata_attribute']));
      }
    }
  }

  /**
   * Update the thumbnail for a media entity.
   *
   * @param \Drupal\media\MediaInterface $media
   *   The media entity to update.
   *
   * @see https://www.drupal.org/project/drupal/issues/2878119
   */
  protected function updateThumbnail(MediaInterface $media): void {
    $source = $media->getSource();
    $plugin_definition = $source->getPluginDefinition();

    $thumbnail_uri = $source->getMetadata($media, $plugin_definition['thumbnail_uri_metadata_attribute']);

    if (!$thumbnail_uri) {
      return;
    }

    $file = $this->createFileForThumbnail($media, $thumbnail_uri);

    // Always reference the thumbnail file via the default thumbnail field on
    // media. This way anything depending on that relationship can continue to
    // do so.
    $this->referenceThumbnailAsFile($media, $file);

    // If the media source is configured to save the thumbnail as a media
    // entity, create the media image entity and reference the file from it.
    if ($source->getPluginId() === 'media_mpx_media' && $source->doSaveThumbnailAsMedia()) {
      $this->referenceThumbnailAsMedia($media, $file);
    }
  }

  /**
   * Return a file entity for the given URI.
   *
   * URI is assumed to be a public URI for an image already on disk. If there is
   * already a file entity for the URI it's returned, otherwise one is created
   * and returned.
   *
   * @param \Drupal\media\MediaInterface $media
   *   The media entity being updated.
   * @param string $thumbnail_uri
   *   URI to the thumbnail. Must be a public URI.
   *
   * @return \Drupal\file\FileInterface
   *   File entity that was either created or found for the given thumbnail URI.
   */
  protected function createFileForThumbnail(MediaInterface $media, $thumbnail_uri) {
    $values = [
      'uri' => $thumbnail_uri,
    ];

    $file_storage = $this->entityTypeManager->getStorage('file');

    /** @var \Drupal\file\FileInterface[] $existing */
    $existing = $file_storage->loadByProperties($values);
    if ($existing) {
      $file = reset($existing);
    }
    else {
      /** @var \Drupal\file\FileInterface $file */
      $file = $file_storage->create($values);
      if ($owner = $media->getOwner()) {
        $file->setOwner($owner);
      }
      $file->setPermanent();
      $file->save();
    }

    return $file;
  }

  /**
   * Reference the thumbnail using as an image media entity.
   *
   * As defined by the source plugin for the given media.
   *
   * @param \Drupal\media\MediaInterface $media
   *   Mpx video media entity.
   * @param \Drupal\file\FileInterface $file
   *   Thumbnail file entity.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  protected function referenceThumbnailAsMedia(MediaInterface $media, FileInterface $file) {
    $source = $media->getSource();
    $source_configuration = $source->getConfiguration();

    $media_image = $this->getExistingMediaImageEntityForFile($media, $file);
    if (!$media_image) {
      // Save a media entity for the thumbnail image.
      $media_image = Media::create([
        'bundle' => $source_configuration['media_image_bundle'],
        'name' => $file->label(),
        $source_configuration['media_image_field'] => [
          [
            'target_id' => $file->id(),
            'alt' => $this->getThumbnailAltForMedia($media),
            'title' => $this->getThumbnailTitleForMedia($media),
          ],
        ],
      ]);
      if ($owner = $media->getOwner()) {
        $media_image->setOwner($owner);
      }
      $media_image->save();
    }
    // Set a reference to the newly saved thumbnail media entity on the video
    // media entity.
    $media->{$source_configuration['media_image_entity_reference_field']} = [
      ['target_id' => $media_image->id()],
    ];
  }

  /**
   * Look up an existing media image file for the given mpx media and thumbnail.
   *
   * @param \Drupal\media\MediaInterface $media
   *   Mpx video media entity.
   * @param \Drupal\file\FileInterface $file
   *   Thumbnail file entity.
   *
   * @return \Drupal\media\MediaInterface|null
   *   The media entity for the file that already exists according to the
   *   source configuration for the given mpx media.
   */
  protected function getExistingMediaImageEntityForFile(MediaInterface $media, FileInterface $file) {
    $source = $media->getSource();
    $source_configuration = $source->getConfiguration();

    // Look up whether we already have a media entity corresponding to the given
    // file.
    $media_storage = $this->entityTypeManager->getStorage('media');
    $media_query = $media_storage->getQuery();
    $existing = $media_query->condition('bundle', $source_configuration['media_image_bundle'])
      ->condition("{$source_configuration['media_image_field']}.target_id", $file->id())
      ->accessCheck(TRUE)
      ->execute();
    if ($existing) {
      /** @var \Drupal\media\MediaInterface $media_image */
      $media_image = $media_storage->load(reset($existing));
      return $media_image;
    }
    else {
      return NULL;
    }
  }

  /**
   * Reference the thumbnail using the default thumbnail file entity reference.
   *
   * Using the thumbnail field defined for all media types by core. This just
   * sets the thumbnail field on the given mpx media and assumes that the caller
   * will save the changes.
   *
   * @param \Drupal\media\MediaInterface $media
   *   Mpx video media entity.
   * @param \Drupal\file\FileInterface $file
   *   Thumbnail file entity.
   */
  protected function referenceThumbnailAsFile(MediaInterface $media, FileInterface $file) {
    $media->thumbnail = [
      [
        'target_id' => $file->id(),
        'alt' => $this->getThumbnailAltForMedia($media),
        'title' => $this->getThumbnailTitleForMedia($media),
      ],
    ];
  }

  /**
   * Get the alt text for the given mpx video's thumbnail.
   *
   * @param \Drupal\media\MediaInterface $media
   *   Mpx video media entity.
   *
   * @return string
   *   Thumbnail alt text.
   */
  protected function getThumbnailAltForMedia(MediaInterface $media) {
    $source = $media->getSource();
    $plugin_definition = $source->getPluginDefinition();

    if (!empty($plugin_definition['thumbnail_alt_metadata_attribute'])) {
      return $source->getMetadata($media, $plugin_definition['thumbnail_alt_metadata_attribute']);
    }
    else {
      return $media->t('Thumbnail', [], ['langcode' => $media->langcode->value]);
    }
  }

  /**
   * Get the title text for the given mpx video's thumbnail.
   *
   * @param \Drupal\media\MediaInterface $media
   *   Mpx video media entity.
   *
   * @return string
   *   Thumbnail title text.
   */
  protected function getThumbnailTitleForMedia(MediaInterface $media) {
    $source = $media->getSource();
    $plugin_definition = $source->getPluginDefinition();

    if (!empty($plugin_definition['thumbnail_title_metadata_attribute'])) {
      return $source->getMetadata($media, $plugin_definition['thumbnail_title_metadata_attribute']);
    }
    else {
      return $media->label();
    }
  }

  /**
   * Inject a single mpx item into the response cache.
   *
   * @param \Lullabot\Mpx\DataService\ObjectInterface $item
   *   The object being injected.
   * @param array $service_info
   *   The service definition from the media source plugin, containing a
   *   'schema_version' key.
   */
  public function cacheItem(ObjectInterface $item, array $service_info) {
    $query = [
      'form' => 'cjson',
      'schema' => $service_info['schema_version'],
    ];
    $encoded = \GuzzleHttp\json_encode($item->getJson());

    $uri = $item->getId()->withScheme('https')->withQuery(Query::build($query));
    $request = new Request('GET', $uri, static::REQUEST_HEADERS);
    $response_headers = $this->getResponseHeaders($encoded);
    $response = new Response(200, $response_headers, $encoded);
    $this->cache->cache($request, $response);
  }

  /**
   * Return response headers for a single encoded entry item.
   *
   * @param string $encoded
   *   The encoded item.
   *
   * @return array
   *   An array of response headers.
   */
  private function getResponseHeaders($encoded): array {
    $response_headers = [
      'Access-Control-Allow-Origin' =>
        [
          '*',
        ],
      'Cache-Control' =>
        [
          'max-age=0',
        ],
      'Date' =>
        [
          gmdate('D, d M Y H:i:s \G\M\T', time()),
        ],
      'Content-Type' =>
        [
          'application/json; charset=UTF-8',
        ],
      'Content-Length' =>
        [
          strlen($encoded),
        ],
    ];
    return $response_headers;
  }

}

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

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