commercetools-8.x-1.2-alpha1/modules/commercetools_content/src/Form/CatalogFiltersForm.php

modules/commercetools_content/src/Form/CatalogFiltersForm.php
<?php

namespace Drupal\commercetools_content\Form;

use Drupal\commercetools\CommercetoolsService;
use Drupal\commercetools\CommercetoolsLocalization;
use Drupal\commercetools_content\Service\CommercetoolsContentComponents;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides catalog filters.
 *
 * @internal
 */
class CatalogFiltersForm extends FormBase implements BaseFormIdInterface {

  /**
   * {@inheritdoc}
   */
  public function getBaseFormId() {
    return 'commercetools_content_catalog_filters_form';
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    // Try to fix an error with several identical forms on the same page.
    // @todo Remove after https://www.drupal.org/project/drupal/issues/2821852
    static $index = 0;
    return $this->getBaseFormId() . $index++;
  }

  /**
   * The Commercetools service.
   *
   * @var \Drupal\commercetools\CommercetoolsService
   */
  protected CommercetoolsService $ct;

  /**
   * The renderer service.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected RendererInterface $renderer;

  /**
   * The Commercetools service.
   *
   * @var \Drupal\commercetools\CommercetoolsLocalization
   */
  protected CommercetoolsLocalization $ctLocalization;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $instance = parent::create($container);
    $instance->ct = $container->get('commercetools');
    $instance->ctLocalization = $container->get('commercetools.localization');
    $instance->renderer = $container->get('renderer');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state, $install_state = NULL) {
    [$searchResult, $enabledFilters, $activeFilters, $appliedSorts, $productListIndex] = $form_state->getBuildInfo()['args'];
    $fractionDigits = $this->ctLocalization->getFractionDigits();
    $paramName = CommercetoolsContentComponents::getParamNameByIndex('filters', $productListIndex);

    $form['#attributes']['data-product-list-index'] = $productListIndex;
    $form['#attributes']['class'][] = 'commercetools-content-auto-submit';
    $form['#attributes']['class'][] = 'commercetools-filters-form';

    $form[$paramName] = [
      '#type' => 'container',
      '#tree' => TRUE,
    ];

    // Build filters.
    foreach ($enabledFilters as $filter) {
      $path = $filter['path'];
      switch ($filter['widget_type']) {
        case 'facet':
        case 'facet_count':
          $form[$paramName][$path] = $this->buildFacetFilter($filter, $searchResult['facets'] ?? [], $activeFilters);
          break;

        case 'textfield':
        case 'checkbox':
          $form[$paramName][$path] = $this->buildSimpleFilter($filter, $activeFilters);
          break;

        case 'separator':
          $form[$paramName][$path] = $this->buildSeparator($filter);
          break;

        case 'custom':
          // @todo Add handling for other custom filters or rename the group
          // from 'custom' to 'range' if only range filters are expected.
          $form[$paramName][$path] = $this->buildRangeFilter($filter, $activeFilters, $searchResult['facets'] ?? [], $fractionDigits);
          break;
      }
    }

    // Add sort by select.
    $language_fallback = $this->ctLocalization->getLanguageFallbacks();
    $locale = !empty($language_fallback) ? reset($language_fallback) : NULL;
    $form['sort_by'] = [
      '#type' => 'select',
      '#title' => $this->t('Sort by'),
      '#options' => [
        'name.' . $locale . ' asc' => $this->t('Name ↑'),
        'name.' . $locale . ' desc' => $this->t('Name ↓'),
        'price asc' => $this->t('Price ↑'),
        'price desc' => $this->t('Price ↓'),
      ],
      '#name' => 'sorts',
      '#default_value' => !empty($appliedSorts) ? $appliedSorts : NULL,
      '#empty_option' => $this->t('Default'),
    ];

    $form['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Apply'),
    ];

    $form['#attached']['library'][] = 'commercetools_content/ajaxify_blocks';
    return $form;
  }

  /**
   * Build faced filter.
   */
  protected function buildFacetFilter(array $filter, array $facetResults = [], array $activeFilters = []) {
    $label = [
      '#theme' => 'commercetools_filter_label',
      '#label' => $filter['label'],
    ];
    $element = [
      '#title' => $this->renderer->render($label),
      '#type' => 'fieldset',
    ];

    foreach ($facetResults as $facetResult) {
      if ($facetResult['facet'] !== $filter['path']) {
        continue;
      }
      foreach ($facetResult['value']['terms'] as $term) {
        $labelRender = [
          '#theme' => 'commercetools_filter_option',
          '#label' => $term['label'] ?? $term['term'],
          '#count' => $filter['widget_type'] === 'facet_count' ? $term['productCount'] : NULL,
          '#key' => $filter['attributeKey'],
        ];
        $element[] = [
          '#type' => 'checkbox',
          '#title' => $this->renderer->render($labelRender),
          '#return_value' => $term['term'],
          '#default_value' => in_array($term['term'], $activeFilters[$facetResult['facet']] ?? []),
          '#attributes' => [
            'class' => ['facet-filter'],
            'data-product-attribute-key' => $filter['type'] === 'attribute' ? $filter['attributeKey'] : NULL,
          ],
        ];
      }
    }

    return $element;
  }

  /**
   * Build simple filter.
   */
  protected function buildSimpleFilter($filter, array $activeFilters = []) {
    $group_label = [
      '#theme' => 'commercetools_filter_label',
      '#label' => $filter['label'],
    ];

    return [
      '#title' => $this->renderer->render($group_label),
      '#type' => $filter['widget_type'],
      '#default_value' => $activeFilters[$filter['path']] ?? NULL,
    ];
  }

  /**
   * Build custom filter.
   */
  protected function buildRangeFilter($filter, array $activeFilters = [], array $facetResults = [], $fractionDigits = NULL): array {
    $label = [
      '#theme' => 'commercetools_filter_label',
      '#label' => $filter['label'],
    ];
    $element = [
      '#title' => $this->renderer->render($label),
      '#type' => 'fieldset',
    ];

    foreach ($facetResults as $facet) {
      if ($facet['facet'] === CommercetoolsService::CT_VARIANTS_PRICE_AMOUNT) {
        $min = $facet['value']['ranges'][0]['min'] ?? NULL;
        $max = $facet['value']['ranges'][0]['max'] ?? NULL;
      }
    }

    // Convert stored cents back to display format.
    $fromValue = isset($activeFilters[$filter['path']]['from'])
      ? number_format($activeFilters[$filter['path']]['from'], $fractionDigits, '.', '')
      : NULL;
    $toValue = isset($activeFilters[$filter['path']]['to'])
      ? number_format($activeFilters[$filter['path']]['to'], $fractionDigits, '.', '')
      : NULL;

    // Calculate step based on fractionDigits: 1/10^fractionDigits.
    $step = $fractionDigits > 0 ? 1 / pow(10, $fractionDigits) : 1;
    $element['from'] = [
      '#type' => 'number',
      '#title' => $this->t('From:'),
      '#step' => $step,
      '#min' => 0,
      '#default_value' => $fromValue,
      '#placeholder' => isset($min) ? $this->convertPrice($min, $fractionDigits, 'divide') : '',
    ];
    $element['to'] = [
      '#type' => 'number',
      '#title' => $this->t('To:'),
      '#step' => $step,
      '#min' => 0,
      '#default_value' => $toValue,
      '#placeholder' => isset($max) ? $this->convertPrice($max, $fractionDigits, 'divide') : '',
    ];

    return $element;
  }

  /**
   * Build simple filter.
   */
  protected function buildSeparator($separator) {
    return [
      '#type' => 'html_tag',
      '#tag' => 'h3',
      '#value' => $separator['label'],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $query = $this->getRequest()->query->all();
    // Reset page after applying new facets.
    unset($query['page']);

    [$searchResult, $enabledFilters, , , $productListIndex, $targetPage] = $form_state->getBuildInfo()['args'];
    $fractionDigits = $this->ctLocalization->getFractionDigits();
    $paramName = CommercetoolsContentComponents::getParamNameByIndex('filters', $productListIndex);

    $filtersValues = $form_state->getValue($paramName);

    // Convert price values to cents.
    foreach ($filtersValues as $path => $value) {
      if ($path === CommercetoolsService::CT_VARIANTS_PRICE_AMOUNT) {
        $filtersValues[$path]['from'] = $this->convertPrice($value['from'], $fractionDigits);
        $filtersValues[$path]['to'] = $this->convertPrice($value['to'], $fractionDigits);
      }
    }

    foreach ($enabledFilters as $filter) {
      $filterValue = $filtersValues[$filter['path']] ?? NULL;
      $filterValue = is_array($filterValue) ? array_filter($filterValue) : $filterValue;

      if (empty($filterValue)) {
        unset($query[$paramName][$filter['path']]);
      }
      else {
        $query[$paramName][$filter['path']] = $filterValue;
      }
    }

    $query['sorts'] = $form_state->getValue('sort_by');
    $targetPage ??= Url::fromRoute('<current>');
    $targetPage->setOption('query', array_filter($query));
    $form_state->setRedirectUrl($targetPage);
  }

  /**
   * Convert camel case to kebab case.
   */
  protected function camelToKebab(string $string): string {
    return strtolower(preg_replace('/[A-Z]/', '-$0', $string));
  }

  /**
   * Converts price values between display format and API format.
   *
   * @param mixed $priceValue
   *   The price value (can be string, float, int, or null).
   * @param int $fractionDigits
   *   The number of fraction digits.
   * @param string $operation
   *   Either 'multiply' (to cents) or 'divide' (from cents).
   *
   * @return string|null
   *   The converted price or '*' for null values.
   */
  private function convertPrice($priceValue, int $fractionDigits = 0, string $operation = 'multiply') {
    // Handle null, empty, or '*' values.
    if ($priceValue === NULL || $priceValue === '' || $priceValue === '*') {
      return '';
    }

    $priceValue = trim((string) $priceValue);
    if ($priceValue === '') {
      return '';
    }

    // Calculate the factor: 10^fractionDigits.
    $factor = pow(10, $fractionDigits);

    if ($operation === 'multiply') {
      // Convert to cents/mills (for API).
      $priceFloat = (float) $priceValue;
      $result = $priceFloat * $factor;

      return (string) (int) round($result);
    }
    elseif ($operation === 'divide') {
      // Convert from cents/mills (for display).
      $priceInt = (int) $priceValue;

      if ($factor === 1) {
        return (string) $priceInt;
      }

      $result = $priceInt / $factor;
      return number_format($result, $fractionDigits, '.', '');
    }

    throw new \InvalidArgumentException("Operation must be 'multiply' or 'divide'");
  }

  /**
   * Initialize the form state and the entity before the first form build.
   */
  protected function init(FormStateInterface $form_state) {

  }

}

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

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