image_to_media_swapper-2.x-dev/src/MediaSwapRecordTableService.php

src/MediaSwapRecordTableService.php
<?php

declare(strict_types=1);

namespace Drupal\image_to_media_swapper;

use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\Link;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\file\FileInterface;
use Drupal\image_to_media_swapper\Entity\MediaSwapRecordInterface;
use Drupal\media\MediaInterface;

/**
 * Service for building consistent MediaSwapRecord tables.
 */
final class MediaSwapRecordTableService {

  use StringTranslationTrait;

  /**
   * The logger service.
   *
   * @var \Drupal\Core\Logger\LoggerChannelInterface
   */
  private LoggerChannelInterface $logger;

  public function __construct(
    private readonly EntityTypeManagerInterface $entityTypeManager,
    private readonly Connection $connection,
    private readonly DateFormatterInterface $dateFormatter,
    private readonly ConfigFactoryInterface $configFactory,
    private readonly LoggerChannelFactoryInterface $loggerFactory,
    private readonly FileUrlGeneratorInterface $fileUrlGenerator,
  ) {
    // Ensure the logger is initialized.
    $this->logger = $this->loggerFactory->get('image_to_media_swapper');
  }

  /**
   * Builds a table of MediaSwapRecord entities.
   *
   * @param string|null $from_time
   *   Optional timestamp to filter records created after this time.
   * @param int $items_per_page
   *   Number of items to show per page. Defaults to 50.
   * @param array $processing_status
   *   Optional array of processing statuses to filter by.
   *
   * @return array
   *   Render array for the table of media swap records.
   *
   * @throws \Exception
   */
  public function buildTable(?string $from_time = NULL, int $items_per_page = 50, array $processing_status = []): array {
    if ($from_time !== NULL) {
      // Get the site's default timezone.
      $time_zone = $this->configFactory->get('system.date')
        ->get('timezone.default');
      // Get the date formatter
      // Convert the provided time to the site's timezone.
      $from_time_string = $this->dateFormatter
        ->format(strtotime($from_time), 'medium', 'short', $time_zone);
    }
    else {
      $from_time_string = $this->t('All time');
    }

    $header = $this->buildHeader();
    $output = [
      '#type' => 'details',
      '#title' => $this->t('Conversion Results @time', [
        '@time' => $this->t('since @time', [
          '@time' => $from_time_string,
        ]),
      ]),
      '#open' => TRUE,
      '#attributes' => ['class' => ['file-to-media-swapper-results']],
    ];
    // Get the unique field selectors in the media_swap_records table.
    $field_selectors_query = $this->connection
      ->select('media_swap_record', 'msr')
      ->fields('msr', ['field_selector'])
      ->groupBy('field_selector');
    if (!empty($processing_status)) {
      $field_selectors_query->condition('processing_status', $processing_status, 'IN');
    }
    $field_selectors = $field_selectors_query->execute()
      ->fetchAll(\PDO::FETCH_COLUMN);

    foreach ($field_selectors as $field_selector) {
      $swapRecordStorage = $this->entityTypeManager->getStorage('media_swap_record');
      $query = $swapRecordStorage->getQuery()
        ->accessCheck(TRUE)
        ->sort('created', 'DESC')
        ->condition('field_selector', $field_selector, '=');
      if (!empty($processing_status)) {
        $query->condition('processing_status', $processing_status, 'IN');
      }
      $element_id = count($output);
      $pager_id = 'pager_' . $field_selector;
      if ($items_per_page > 0) {
        $query->pager($items_per_page, $element_id, $pager_id);
      }
      if ($from_time !== NULL) {
        $query->condition('created', strtotime($from_time), '>=');
      }

      $swapRecordIds = $query->execute();

      if (!empty($swapRecordIds)) {
        /** @var \Drupal\image_to_media_swapper\Entity\MediaSwapRecordInterface[] $swapRecords */
        $records = $swapRecordStorage->loadMultiple($swapRecordIds);

        if (!empty($records)) {
          $rows = [];
          /** @var \Drupal\image_to_media_swapper\Entity\MediaSwapRecordInterface $record */
          foreach ($records as $record) {
            $rows[] = $this->buildRow($record);
          }
          $output[$field_selector] = [
            '#type' => 'details',
            '#attributes' => ['class' => ['image-to-media-swapper-result-wrapper']],
            '#title' => $this->t('Results for @field (@count records)', [
              '@field' => $field_selector,
              '@count' => count($records),
            ]),
          ];
          $output[$field_selector]['table'] = [
            '#type' => 'table',
            '#header' => $header,
            '#rows' => $rows,
          ];
        }
      }
      // Add pager to the output if there are any records.
      $output[$field_selector]['pager'] = [
        '#type' => 'pager',
        '#element' => $element_id,
        '#id' => $pager_id,
        '#weight' => 100,
      ];
    }

    return $output;
  }

  /**
   * Builds a table header for MediaSwapRecord displays.
   */
  public function buildHeader(): array {
    return [
      $this->t('Entity Type'),
      $this->t('Bundle'),
      $this->t('Target Entity'),
      $this->t('Source File'),
      $this->t('Target Media'),
      $this->t('Status'),
      $this->t('Category'),
      $this->t('Processed'),
      $this->t('Error Message'),
      $this->t('Actions'),
    ];
  }

  /**
   * Builds a table row for a MediaSwapRecord entity.
   *
   * @throws \Drupal\Core\Entity\EntityMalformedException
   */
  public function buildRow(MediaSwapRecordInterface $record): array {
    $entity_type = $record->getTargetEntityType();
    $entity_id = $record->getTargetEntityId();
    $link = $this->getEntityWithRoute($entity_id, $entity_type);

    $processing_status = $record->getProcessingStatus();
    $processing_status_class = match ($processing_status) {
      'completed' => 'color-success',
      'failed' => 'color-error',
      'processing' => 'color-warning',
      default => 'color-status',
    };

    $processed_time = $record->getProcessedTime();
    $processed_display = $processed_time ?
      $this->dateFormatter->format($processed_time, 'short') :
      $this->t('Not processed');

    $sourceFileLink = $this->t('N/A');
    if (!empty($record->getSourceFileId())) {
      // Get the file entity path.
      try {
        $entity = $this->entityTypeManager->getStorage('file')
          ->load($record->getSourceFileId());
        if ($entity instanceof FileInterface) {
          $fileUri = $entity->getFileUri();
          // Create a URL to the file from the URI.
          $fileUrl = $this->fileUrlGenerator->generate($fileUri);
          // Create a link to the file download URL.
          $sourceFileLink = Link::fromTextAndUrl($this->t('File'), $fileUrl);
        }
      }
      catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
        $this->logger->error($e->getMessage());
      }
    }
    $mediaLink = $this->t('N/A');
    if (!empty($record->getCreatedMediaId())) {
      try {
        $entity = $this->entityTypeManager->getStorage('media')
          ->load($record->getCreatedMediaId());
        if ($entity instanceof MediaInterface) {
          $mediaLink = $entity->toLink();
        }
      }
      catch (InvalidPluginDefinitionException | PluginNotFoundException $e) {
        $this->logger->error($e->getMessage());
      }
    }

    if ($processing_status === 'pending') {
      // Add a button to re-check the record.
      $status_check_button = Link::createFromRoute($this->t('Recheck'), 'image_to_media_swapper.recheck_record', ['media_swap_record' => $record->id()], [
        'attributes' => [
          'class' => ['button', 'button--small'],
          'style' => 'margin-left: 10px;',
        ],
      ]);
    }

    return [
      $entity_type,
      $record->getTargetBundle(),
      $link ?: $this->t('N/A'),
      $sourceFileLink,
      $mediaLink,
      [
        'data' => [
          '#type' => 'html_tag',
          '#tag' => 'span',
          '#attributes' => ['class' => [$processing_status_class]],
          '#value' => ucfirst($processing_status),
        ],
      ],
      $record->getBatchCategory(),
      $processed_display,
      $record->getErrorMessage(),
      $status_check_button ?? '',
    ];
  }

  /**
   * Gets an entity link with appropriate route.
   *
   * @throws \Drupal\Core\Entity\EntityMalformedException
   */
  private function getEntityWithRoute(int $entity_id, string $type): ?Link {
    try {
      $entity = $this->entityTypeManager->getStorage($type)->load($entity_id);
    }
    catch (\Exception $e) {
      return NULL;
    }

    if (!$entity instanceof EntityInterface) {
      return NULL;
    }

    // Try to get canonical URL.
    if ($entity->hasLinkTemplate('canonical')) {
      return $entity->toLink();
    }

    // Try edit form.
    if ($entity->hasLinkTemplate('edit-form')) {
      return $entity->toLink($this->t('Edit'), 'edit-form');
    }

    // For paragraphs, try to link to parent entity.
    if ($type === 'paragraph' && method_exists($entity, 'getParentEntity')) {
      $parent = $entity->getParentEntity();
      if ($parent instanceof EntityInterface) {
        return $this->getEntityWithRoute($parent->id(), $parent->getEntityTypeId());
      }
    }

    return NULL;
  }

}

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

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