a12s-1.0.0-beta7/modules/page_context/src/RecordSet.php

modules/page_context/src/RecordSet.php
<?php

namespace Drupal\a12s_page_context;

use Drupal\a12s_page_context\Entity\PageContextFormInterface;
use Drupal\Core\Entity\EntityInterface;

/**
 * Represents a set of records.
 */
class RecordSet implements \IteratorAggregate, \ArrayAccess, \Countable {

  /**
   * The list of records.
   *
   * @var \Drupal\a12s_page_context\Record[]
   */
  protected array $records;

  /**
   * The compiled data.
   *
   * @var array
   */
  protected array $compiledData;

  /**
   * Class constructor.
   *
   * @param \Drupal\a12s_page_context\Record[] $records
   *   The list of records.
   * @param \Drupal\a12s_page_context\Entity\PageContextFormInterface $pageContextForm
   *   The related "page context" form.
   * @param array $context
   *   The current context.
   */
  public function __construct(array $records, protected PageContextFormInterface $pageContextForm, protected array $context = []) {
    $settings = array_values($this->pageContextForm->getDisplaySettings());
    usort($settings, fn(array $a, array $b) => $a['weight'] <=> $b['weight']);
    $order = array_map(fn(array $setting) => $setting['plugin_id'], $settings);

    // Order results by plugin weight, so the data compilation takes care of
    // plugin priority.
    usort($records, function (Record $a, Record $b) use ($order) {
      $aPluginId = $a->getPluginId();
      $bPluginId = $b->getPluginId();

      if ($aPluginId !== $bPluginId) {
        $aWeight = array_search($aPluginId, $order);
        $bWeight = array_search($bPluginId, $order);
        return $aWeight === FALSE ? 1 : ($bWeight === FALSE ? -1 : ($aWeight <=> $bWeight));
      }

      return 0;
    });

    $this->records = $records;
  }

  /**
   * Load the stored records for the given context form and page/context.
   *
   * @param \Drupal\a12s_page_context\Entity\PageContextFormInterface $pageContextForm
   *   The Page Context Form instance.
   * @param array $context
   *   The plugin context. Default values are calculated by the in-charge
   *   plugins, however it is possible to force some values if desired.
   *
   * @return \Drupal\a12s_page_context\RecordSet
   *   The record set.
   */
  public static function load(PageContextFormInterface $pageContextForm, array $context = []): RecordSet {
    static $sets = [];

    $context['a12s_page_context_form'] = $pageContextForm;
    ksort($context);
    $contextClone = [];

    foreach ($context as $key => $item) {
      if ($item instanceof EntityInterface) {
        $contextClone[$key] = $item->id();
      }
    }

    $hash = md5(serialize($contextClone));

    if (!isset($sets[$hash])) {
      $connection = \Drupal::database();
      $query = $connection->select('a12s_page_context_record')
        ->fields('a12s_page_context_record')
        ->condition('config_id', $pageContextForm->id());

      $conditions = $connection->condition('OR')
        // Ensure we have at least a condition which returns no results, just in
        // case there are no enabled display plugins or nothing matching with
        // the current route.
        ->where('1 = 0');

      foreach ($pageContextForm->getDisplayPlugins() as $plugin) {
          if ($condition = $plugin->addRecordQueryConditions($context)) {
          $conditions->condition($condition);
        }
      }

      $results = $query->condition($conditions)->execute()->fetchAll(\PDO::FETCH_CLASS, Record::class);
      $sets[$hash] = new static($results, $pageContextForm, $context);
    }

    return $sets[$hash];
  }

  /**
   * Get the data for the current Page Context Form and context.
   *
   * @return array
   *   The compiled data.
   */
  public function getData(): array {
    if (!isset($this->compiledData)) {
      $this->compiledData = $this->compile();
    }

    return $this->compiledData;
  }

  /**
   * Get the data as tree.
   *
   * @return array
   *   The nested data.
   */
  public function getDataTree(): array {
    return array_reduce($this->records, fn(array $tree, Record $record) => $this->addRecordToDataTree($record, $record->getData(), $tree), []);
  }

  /**
   * Build recursively the data tree, with their related records.
   *
   * @param \Drupal\a12s_page_context\Record $record
   *   The record to add.
   * @param array $data
   *   The current data.
   * @param array $tree
   *   The current tree.
   *
   * @return array
   *   The data tree.
   */
  public function addRecordToDataTree(Record $record, array $data, array $tree = []): array {
    foreach ($data as $key => $item) {
      if (str_ends_with($key, '_override')) {
        continue;
      }

      if (is_array($item)) {
        $tree[$key] = $this->addRecordToDataTree($record, $item, $tree[$key] ?? []);
      }
      else {
        $overrideKey = $key . '_override';

        if (!array_key_exists($key, $tree) || !isset($data[$overrideKey]) || !empty($data[$overrideKey])) {
          $tree[$key][] = [
            'record' => $record,
            'value' => $item,
          ];
        }
      }
    }

    return $tree;
  }

  /**
   * Compile the provided data with the current values.
   *
   * This is a recursive function, which takes care of calculating the final
   * context values with the optional overrides.
   *
   * @param array $recordData
   *   The data from a database record.
   * @param array $values
   *   The already calculated values.
   *
   * @return array
   *   The compiled data.
   */
  public function compileRecordData(array $recordData, array $values = []): array {
    foreach ($recordData as $key => $data) {
      if (str_ends_with($key, '_override')) {
        continue;
      }

      if (is_array($data)) {
        $values[$key] = $this->compileRecordData($data, $values[$key] ?? []);
      }
      else {
        $overrideKey = $key . '_override';

        // Having an override key not set can mean that the parent record
        // has been created after the children one. So this should lead
        // to an override.
        if (!array_key_exists($key, $values) || !isset($recordData[$overrideKey]) || !empty($recordData[$overrideKey])) {
          $values[$key] = $data;
        }
      }
    }

    return $values;
  }

  /**
   * Compile the raw data from all records recursively.
   *
   * @return array
   *   The compiled data.
   */
  protected function compile(): array {
    $records = $this->filter();
    $recordsData = array_map(fn(Record $record) => $record->getData() ?? [], $records);
    return array_reduce($recordsData, fn(array $values, array $data) => $this->compileRecordData($data, $values), []);
  }

  /**
   * Filter the records using enabled plugins and current context.
   *
   * @return \Drupal\a12s_page_context\Record[]
   *   The filtered records.
   */
  protected function filter(): array {
    $records = $this->records;

    foreach ($this->pageContextForm->getDisplayPlugins() as $plugin) {
      $records = $plugin->filterRecords($records, $this->context);
    }

    return $records;
  }

  /**
   * {@inheritdoc}
   */
  public function offsetExists(mixed $offset): bool {
    return array_key_exists($offset, $this->records);
  }

  /**
   * {@inheritdoc}
   */
  public function offsetGet(mixed $offset): ?Record {
    return $this->records[$offset] ?? NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function offsetSet(mixed $offset, mixed $value): void {
    throw new \LogicException('Attempting to write to an immutable array');
  }

  /**
   * {@inheritdoc}
   */
  public function offsetUnset(mixed $offset): void {
    throw new \LogicException('Attempting to write to an immutable array');
  }

  /**
   * {@inheritdoc}
   */
  public function count(): int {
    return count($this->records);
  }

  /**
   * {@inheritdoc}
   *
   * @return \Drupal\a12s_page_context\Record[]
   *   The list of records.
   */
  public function getIterator(): \Traversable {
    return new \ArrayIterator($this->records);
  }

}

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

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