ispim-1.0.x-dev/src/PreviewImage/ListBuilder.php

src/PreviewImage/ListBuilder.php
<?php

declare(strict_types=1);

namespace Drupal\ispim\PreviewImage;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityListBuilder;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityViewBuilderInterface;
use Drupal\Core\Entity\EntityViewModeInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\ispim\Entity\IspimPreviewImageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * @method \Drupal\ispim\Entity\IspimPreviewImageInterface[] load()
 */
class ListBuilder extends EntityListBuilder implements FormInterface {

  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * The key to use for the form element containing the entities.
   */
  protected string $entitiesKey = 'entities';

  /**
   * @var array<\Drupal\ispim\Entity\IspimPreviewImageInterface>
   */
  protected array $entities = [];

  /**
   * Name of the entity's weight field or NULL if no field is provided.
   */
  protected ?string $weightKey = NULL;

  protected ?EntityViewModeInterface $viewMode = NULL;

  protected ?EntityViewDisplayInterface $viewDisplay = NULL;

  protected ?EntityViewBuilderInterface $viewBuilder = NULL;

  /**
   * @var null|array<string, array<string, mixed>>
   */
  protected ?array $components = NULL;

  /**
   * @var null|array<string, string|\Stringable>
   */
  protected ?array $header = NULL;

  /**
   * {@inheritdoc}
   *
   * @noinspection PhpMissingParentCallCommonInspection
   */
  public static function createInstance(
    ContainerInterface $container,
    EntityTypeInterface $entity_type,
  ): static {
    // @phpstan-ignore-next-line
    return new static(
      $entity_type,
      $container->get('entity_type.manager'),
      $container->get('config.factory'),
      $container->get('form_builder'),
      $container->get('entity_type.bundle.info'),
      $container->get('entity_field.manager'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function __construct(
    EntityTypeInterface $entity_type,
    EntityTypeManagerInterface $entityTypeManager,
    protected ConfigFactoryInterface $configFactory,
    protected FormBuilderInterface $formBuilder,
    protected EntityTypeBundleInfoInterface $entityTypeBundleInfo,
    protected EntityFieldManagerInterface $entityFieldManager,
  ) {
    $this->entityTypeManager = $entityTypeManager;

    /* @noinspection PhpUnhandledExceptionInspection */
    parent::__construct(
      $entity_type,
      $this->entityTypeManager->getStorage($entity_type->id()),
    );

    if ($this->entityType->hasKey('weight')) {
      $this->weightKey = (string) $this->entityType->getKey('weight');
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return 'ispim_preview_images_form';
  }

  /**
   * {@inheritdoc}
   *
   * @phpstan-param array<string, mixed> $form
   *
   * @return array<string, mixed>
   *
   * @throws \Drupal\Core\Entity\EntityMalformedException
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form[$this->entitiesKey] = [
      '#type' => 'table',
      '#header' => $this->buildHeader(),
      '#empty' => $this->t(
        'There are no @entityType.pluralLabel yet.',
        [
          '@entityType.pluralLabel' => $this->entityType->getPluralLabel(),
        ],
      ),
      '#tabledrag' => [
        [
          'action' => 'order',
          'relationship' => 'sibling',
          'group' => 'weight',
        ],
      ],
      '#sorted' => FALSE,
    ];

    $this->entities = $this->load();
    $delta = 10;
    // Change the delta of the weight field if have more than 20 entities.
    if ($this->weightKey) {
      $count = count($this->entities);
      if ($count > 20) {
        $delta = ceil($count / 2);
      }
    }

    foreach ($this->entities as $entity) {
      $row = $this->buildRow($entity);

      if (isset($row[$this->weightKey])) {
        $row[$this->weightKey]['#delta'] = $delta;
      }

      $form[$this->entitiesKey][$entity->id()] = $row;
    }

    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Save'),
      '#button_type' => 'primary',
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   *
   * @phpstan-param array<string, mixed> $form
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // No validation.
  }

  /**
   * {@inheritdoc}
   *
   * @phpstan-param array<string, mixed> $form
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $this
      ->submitFormUpdateEntities($form, $form_state)
      ->submitFormUpdateImageSettings($form, $form_state);
  }

  /**
   * @phpstan-param array<string, mixed> $form
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   */
  protected function submitFormUpdateEntities(array &$form, FormStateInterface $formState): static {
    $numOfUpdated = 0;
    foreach ($formState->getValue($this->entitiesKey, []) ?: [] as $id => $value) {
      if (!isset($this->entities[$id])) {
        continue;
      }

      $newWeight = (int) $value[$this->weightKey];
      if ($this->entities[$id]->getWeight() !== $newWeight) {
        // Save entity only when its weight was changed.
        $this->entities[$id]->set($this->weightKey, $newWeight);
        $this->entities[$id]->save();
        $numOfUpdated++;
      }
    }

    if ($numOfUpdated) {
      $this->messenger()->addStatus($this->t(
        'Order of the preview images have been updated.',
      ));
    }

    return $this;
  }

  /**
   * @phpstan-param array<string, mixed> $form
   */
  protected function submitFormUpdateImageSettings(array &$form, FormStateInterface $formState): static {
    $previewImage = $formState->getValue(['image_settings', 'preview_image']);
    if (!$previewImage
      || $previewImage === $this->configFactory->get('image.settings')->get('preview_image')
    ) {
      return $this;
    }

    $this
      ->configFactory
      ->getEditable('image.settings')
      ->set('preview_image', $previewImage)
      ->save();

    $this->messenger()->addStatus($this->t(
      'Default preview image has been updated.',
    ));

    return $this;
  }

  /**
   * {@inheritdoc}
   *
   * @noinspection PhpMissingParentCallCommonInspection
   */
  protected function getEntityListQuery(): QueryInterface {
    // No parent::getEntityListQuery() call, because of "sort by ID".
    $keys = $this->entityType->getKeys();
    $query = $this
      ->getStorage()
      ->getQuery()
      ->accessCheck();

    if (!empty($keys['weight'])) {
      $query->sort($keys['weight']);
    }

    if (!empty($keys['label'])) {
      $query->sort($keys['label']);
    }

    // Only add the pager if a limit is specified.
    if ($this->limit) {
      $query->pager($this->limit);
    }

    return $query;
  }

  /**
   * {@inheritdoc}
   *
   * @return array<string, mixed>
   */
  public function render(): array {
    $this->initViewDisplay();

    return $this->weightKey
      ? $this->formBuilder->getForm($this)
      : parent::render();
  }

  protected function initViewDisplay(): static {
    $this->viewMode = NULL;
    $this->viewDisplay = NULL;
    $this->viewBuilder = NULL;
    $this->header = NULL;
    $this->components = NULL;

    $settings = $this->configFactory->get("ispim.{$this->entityTypeId}.settings");
    $renderer = $settings->get('listBuilder.renderer');
    if ($renderer !== 'viewMode') {
      return $this;
    }

    $viewModeSortId = $settings->get('listBuilder.config.viewMode.id');
    if (!$viewModeSortId) {
      return $this;
    }

    /* @noinspection PhpUnhandledExceptionInspection */
    $this->viewMode = $this
      ->entityTypeManager
      ->getStorage('entity_view_mode')
      ->load("{$this->entityTypeId}.$viewModeSortId");
    if (!$this->viewMode) {
      return $this;
    }

    /* @noinspection PhpUnhandledExceptionInspection */
    $this->viewDisplay = $this
      ->entityTypeManager
      ->getStorage('entity_view_display')
      ->load("{$this->entityTypeId}.{$this->entityTypeId}.$viewModeSortId");

    if (!$this->viewDisplay) {
      return $this;
    }

    $this->viewBuilder = $this->entityTypeManager->getViewBuilder($this->entityTypeId);

    // If for some reason ::buildRow() were called before the ::buildHeader()
    // then make it sure ::$header is already initialized.
    $this->initViewDisplayHeader();

    return $this;
  }

  protected function initViewDisplayHeader(): static {
    $this->header = [];
    $this->components = $this->viewDisplay->getComponents();
    uasort(
      $this->components,
      function (array $a, array $b): int {
        return $a['weight'] <=> $b['weight'];
      },
    );

    $fields = $this->entityFieldManager->getFieldDefinitions($this->entityTypeId, $this->entityTypeId);
    foreach ($this->components as $name => $component) {
      $this->header[$name] = $fields[$name]->getLabel();
      if ($name === 'image') {
        $this->header['default'] = $this->t('Default');
      }
    }

    return $this;
  }

  /**
   * {@inheritdoc}
   *
   * @return array<string, string|\Stringable>
   *
   * @noinspection PhpMissingParentCallCommonInspection
   */
  public function buildHeader(): array {
    return $this->viewDisplay
      ? $this->buildHeaderViewMode()
      : $this->buildHeaderDefault();
  }

  /**
   * @return array<string, string|\Stringable>
   */
  protected function buildHeaderViewMode(): array {
    return $this->header + parent::buildHeader();
  }

  /**
   * @return array<string, string|\Stringable>
   */
  protected function buildHeaderDefault(): array {
    $header = [];

    $header['id'] = $this->t('Id');

    if ($this->weightKey) {
      $header['weight'] = $this->t('Weight');
    }

    if ($this->entityType->getBundleEntityType()) {
      $header['bundle'] = $this->t('Type');
    }

    $header['image'] = $this->t('Image');
    $header['default'] = $this->t('Default');
    $header['label'] = $this->t('Label');

    return $header + parent::buildHeader();
  }

  /**
   * {@inheritdoc}
   *
   * @return array<string, mixed>
   *
   * @throws \Drupal\Core\Entity\EntityMalformedException
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   *
   * @noinspection PhpMissingParentCallCommonInspection
   */
  public function buildRow(EntityInterface $entity): array {
    /** @var \Drupal\ispim\Entity\IspimPreviewImageInterface $entity */
    return $this->viewDisplay
      ? $this->buildRowViewMode($entity)
      : $this->buildRowDefault($entity);
  }

  /**
   * @return array<string, mixed>
   */
  protected function buildRowViewMode(IspimPreviewImageInterface $entity): array {
    $row = [
      '#attributes' => [
        'class' => ['draggable'],
      ],
      '#weight' => $entity->getWeight(),
    ];

    $imageSettings = $this->configFactory->get('image.settings');
    foreach ($this->components as $componentName => $component) {
      if ($componentName !== $this->weightKey) {
        // @todo I have no idea what's the problem here.
        // @phpstan-ignore-next-line
        $row[$componentName]['data'] = $this->viewBuilder->viewField($entity->get($componentName), $component);

        if ($componentName === 'image') {
          $row['default'] = [
            'data' => [
              '#type' => 'radio',
              '#title' => $this->t('Default'),
              '#title_display' => 'invisible',
              '#required' => TRUE,
              '#return_value' => $entity->getImageFile()->getFileUri(),
              '#default_value' => $imageSettings->get('preview_image') === $entity->getImageFile()->getFileUri()
                ? $entity->getImageFile()->getFileUri()
                : NULL,
              '#name' => 'image_settings[preview_image]',
              '#parents' => ['image_settings', 'preview_image'],
              '#access' => $entity->isPublished(),
            ],
          ];
        }

        continue;
      }

      $row[$componentName] = [
        '#type' => 'weight',
        '#title' => $this->t(
          'Weight for @entity.label',
          [
            '@title' => $entity->label(),
          ],
        ),
        '#title_display' => 'invisible',
        '#default_value' => $entity->getWeight(),
        '#attributes' => [
          'class' => [
            'weight',
          ],
        ],
      ];
    }

    return $row + parent::buildRow($entity);
  }

  /**
   * @return array<string, mixed>
   *
   * @throws \Drupal\Core\Entity\EntityMalformedException
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function buildRowDefault(IspimPreviewImageInterface $entity): array {
    $bundleEntityTypeId = $entity
      ->getEntityType()
      ->getBundleEntityType();

    $bundle = NULL;
    if ($bundleEntityTypeId) {
      /* @noinspection PhpUnhandledExceptionInspection */
      $bundle = $this
        ->entityTypeManager
        ->getStorage($entity->getEntityType()->getBundleEntityType())
        ->load($entity->bundle());
    }

    $row = [];

    $row['id'] = [
      'data' => [
        '#markup' => $entity->id(),
      ],
    ];

    if ($this->weightKey) {
      // Override default values to markup elements.
      $row['#attributes']['class'][] = 'draggable';
      $row['#weight'] = $entity->getWeight();

      $row[$this->weightKey] = [
        '#type' => 'weight',
        '#title' => $this->t(
          'Weight for @entity.label',
          [
            '@title' => $entity->label(),
          ],
        ),
        '#title_display' => 'invisible',
        '#default_value' => $entity->getWeight(),
        '#attributes' => [
          'class' => [
            'weight',
          ],
        ],
      ];
    }

    if ($bundle) {
      $row['bundle'] = [
        'data' => ($bundle->access('edit') ? $bundle->toLink(NULL, 'edit-form') : $bundle->label()),
      ];
    }

    $row['image'] = [
      'data' => $entity->get('image')->view([
        'type' => 'image',
        'label' => 'hidden',
        'settings' => [
          'image_link' => '',
          'image_style' => $this->getImageStyleId(),
        ],
      ]),
    ];

    $row['default'] = [
      'data' => [
        '#type' => 'markup',
        '#markup' => $this->t('@todo Implement'),
      ],
    ];

    $row['label'] = [
      'data' => [
        '#type' => 'link',
        '#title' => $entity->label(),
        '#url' => $entity->toUrl('canonical'),
      ],
    ];

    return $row + parent::buildRow($entity);
  }

  protected function getImageStyleId(): string {
    $settings = $this->configFactory->get("ispim.{$this->entityTypeId}.settings");

    return $settings->get('listBuilder.config.default.imageStyle');
  }

}

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

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