cc-1.0.x-dev/modules/cc_cex/src/Controller/MarketPairsController.php

modules/cc_cex/src/Controller/MarketPairsController.php
<?php

namespace Drupal\cc_cex\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\cc_cex\Service\CcxtService;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;

/**
 * Controller for displaying market pairs.
 */
class MarketPairsController extends ControllerBase implements ContainerInjectionInterface {

  /**
   * The CCXT service.
   *
   * @var \Drupal\cc_cex\Service\CcxtService
   */
  protected $ccxtService;

  /**
   * The date formatter.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  protected $dateFormatter;

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * The messenger.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface
   */
  protected $messenger;

  /**
   * Constructs a new MarketPairsController object.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   The config factory.
   * @param \Drupal\cc_cex\Service\CcxtService $ccxt_service
   *   The CCXT service.
   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
   *   The date formatter.
   * @param \Drupal\Core\Messenger\MessengerInterface $messenger
   *   The messenger.
   */
  public function __construct(
    ConfigFactoryInterface $config_factory,
    CcxtService $ccxt_service,
    DateFormatterInterface $date_formatter,
    MessengerInterface $messenger
  ) {
    $this->configFactory = $config_factory;
    $this->ccxtService = $ccxt_service;
    $this->dateFormatter = $date_formatter;
    $this->messenger = $messenger;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('config.factory'),
      $container->get('cc_cex.ccxt_service'),
      $container->get('date.formatter'),
      $container->get('messenger')
    );
  }

  /**
   * Displays the market pairs page.
   *
   * @return array
   *   A render array for the market pairs page.
   */
  public function content() {
    $config = $this->configFactory->get('cc.settings');
    $enabled_exchanges = $config->get('enabled_exchanges') ?: '';
    $enabled_exchanges = !empty($enabled_exchanges) ? array_map('trim', explode(',', $enabled_exchanges)) : [];
    $currencies = $config->get('currencies') ?: [];
    $accepted_currencies = $currencies['accepted'] ?? [];
    $needed_currencies = $currencies['needed'] ?? [];

    $build = [];

    // Add cache contexts and tags
    $build['#cache'] = [
      'contexts' => ['user'],
      'tags' => ['cc:markets', 'cc:symbols', 'cc:prices', 'config:cc.settings'],
      'max-age' => 31536000, // 1 year
    ];

    // Add refresh buttons
    $build['actions'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => ['cc-market-pairs-actions'],
      ],
      'refresh_prices' => [
        '#type' => 'link',
        '#title' => $this->t('Refresh Prices'),
        '#url' => Url::fromRoute('cc_cex.market_pairs_refresh', ['type' => 'prices']),
        '#attributes' => [
          'class' => ['button', 'button--primary'],
        ],
      ],
      'refresh_markets' => [
        '#type' => 'link',
        '#title' => $this->t('Refresh Available Markets'),
        '#url' => Url::fromRoute('cc_cex.market_pairs_refresh', ['type' => 'markets']),
        '#attributes' => [
          'class' => ['button'],
        ],
      ],
    ];

    $build['description'] = [
      '#markup' => '<p>' . $this->t('Available market pairs based on your <a href="@settings">exchange settings</a>.', [
        '@settings' => Url::fromRoute('cc_cex.exchange_settings')->toString(),
      ]) . '</p>',
    ];

    if (empty($enabled_exchanges) || empty($needed_currencies) || empty($accepted_currencies)) {
      $build['empty'] = [
        '#markup' => '<p>' . $this->t('Please <a href="@settings">configure your exchange settings</a> first.', [
          '@settings' => '/admin/config/services/cc-cex-exchange',
        ]) . '</p>',
      ];
      return $build;
    }

    // Validate currency arrays
    $needed_currencies = array_filter($needed_currencies);
    $accepted_currencies = array_filter($accepted_currencies);

    // Get valid symbols from cache
    $exchange_symbols = $this->ccxtService->getAvailableSymbols(
      $enabled_exchanges,
      $needed_currencies,
      $accepted_currencies,
    );

    $all_symbols = [];
    if (!empty($exchange_symbols)) {
      foreach ($exchange_symbols as $symbols) {
        $all_symbols = array_merge($all_symbols, $symbols);
      }
      $all_symbols = array_unique($all_symbols);
    }

    // Only proceed if we have symbols
    if (empty($all_symbols)) {
      $build['empty'] = [
        '#markup' => '<p>' . $this->t('No valid market pairs found for the selected currencies.') . '</p>',
      ];
      return $build;
    }

    // Only fetch prices for active symbols
    $active_symbols = array_filter($all_symbols, function($symbol) use ($needed_currencies, $accepted_currencies) {
      if (!str_contains($symbol, '/')) {
        \Drupal::logger('cc')->warning('Invalid symbol format: @symbol', ['@symbol' => $symbol]);
        return false;
      }
      list($base, $quote) = explode('/', $symbol);
      if (!$base || !$quote) {
        \Drupal::logger('cc')->warning('Invalid symbol format: @symbol', ['@symbol' => $symbol]);
        return false;
      }
      return in_array($base, $accepted_currencies) && in_array($quote, $needed_currencies);
    });

    // Use the cached symbols to fetch only the prices we need
    $market_pairs = $this->ccxtService->getPrices(
      $enabled_exchanges,
      $active_symbols,
      false // Don't force refresh prices on normal page load
    );
    if (empty($market_pairs)) {
      $build['no_pairs'] = [
        '#markup' => '<p>' . $this->t('No market pairs found for your current configuration.') . '</p>',
      ];
      return $build;
    }

    // Create table
    $header = [
      $this->t('Exchange'),
      $this->t('Symbol'),
      $this->t('Price'),
      $this->t('Last update'),
    ];

    $rows = [];
    foreach ($market_pairs as $pair) {
      // Convert millisecond timestamp to seconds if needed
      $timestamp = $pair['timestamp'];
      if ($timestamp > 1000000000000) { // If timestamp is in milliseconds
        $timestamp = (int)($timestamp / 1000);
      }

      $rows[] = [
        $pair['exchange'],
        $pair['symbol'],
        $pair['price'],
        $this->t('@time ago', ['@time' => $this->dateFormatter->formatTimeDiffSince($timestamp)]),
      ];
    }

    $build['market_pairs'] = [
      '#type' => 'table',
      '#header' => $header,
      '#rows' => $rows,
      '#empty' => $this->t('No market pairs available.'),
      '#attributes' => [
        'class' => ['cc-market-pairs-table'],
      ],
    ];

    return $build;
  }

  /**
   * Refreshes market pair prices.
   *
   * @param string $type
   *   The type of refresh. Either 'prices' or 'markets'.
   *
   * @return \Symfony\Component\HttpFoundation\RedirectResponse
   *   A redirect response to the market pairs page.
   */
  public function refresh($type) {
    // Get current config
    $config = $this->configFactory->get('cc.settings');
    $enabled_exchanges = $config->get('enabled_exchanges') ?: '';
    $enabled_exchanges = !empty($enabled_exchanges) ? array_map('trim', explode(',', $enabled_exchanges)) : [];
    $currencies = $config->get('currencies') ?: [];
    $accepted_currencies = $currencies['accepted'] ?? [];
    $needed_currencies = $currencies['needed'] ?? [];

    if (empty($enabled_exchanges) || empty($needed_currencies) || empty($accepted_currencies)) {
      $this->messenger->addError($this->t('Please configure your exchange settings first.'));
      return new RedirectResponse(Url::fromRoute('cc_cex.market_pairs')->toString());
    }

    if ($type === 'markets') {
      // Clear all caches related to markets and symbols
      \Drupal::service('cache_tags.invalidator')->invalidateTags(['cc:markets', 'cc:symbols', 'config:cc.settings']);
      $this->messenger->addStatus($this->t('Market data refreshed.'));
    } else {
      // Clear price caches to force a refresh
      \Drupal::service('cache_tags.invalidator')->invalidateTags(['cc:prices', 'config:cc.settings']);
      $this->messenger->addStatus($this->t('Prices refreshed.'));
    }

    return new RedirectResponse(Url::fromRoute('cc_cex.market_pairs')->toString());
  }
}

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

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