arch-8.x-1.x-dev/modules/product/modules/downloadable/src/Controller/PurchasedFiles.php
modules/product/modules/downloadable/src/Controller/PurchasedFiles.php
<?php namespace Drupal\arch_downloadable_product\Controller; use Drupal\arch_downloadable_product\DownloadUrlBuilderInterface; use Drupal\arch_product\Entity\ProductTypeInterface; use Drupal\Component\Utility\SortArray; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Link; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Url; use Drupal\user\UserInterface; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\RequestStack; /** * Purchased files. * * @package Drupal\arch_downloadable_product\Controller */ class PurchasedFiles extends ControllerBase { /** * Product type storage. * * @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface */ protected $productTypeStorage; /** * Product storage. * * @var \Drupal\arch_product\Entity\Storage\ProductStorageInterface */ protected $productStorage; /** * Order storage. * * @var \Drupal\arch_order\Entity\Storage\OrderStorageInterface */ protected $orderStorage; /** * File storage. * * @var \Drupal\file\FileStorageInterface */ protected $fileStorage; /** * Download URL builder. * * @var \Drupal\arch_downloadable_product\DownloadUrlBuilderInterface */ protected $downloadUrlBuilder; /** * Date formatter. * * @var \Drupal\Core\Datetime\DateFormatterInterface */ protected $dateFormatter; /** * Current request. * * @var \Symfony\Component\HttpFoundation\Request */ protected $currentRequest; /** * ProductDownloadController constructor. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * Entity type manager. * @param \Drupal\arch_downloadable_product\DownloadUrlBuilderInterface $download_url_builder * Download URL builder. * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter * Date formatter. * @param \Drupal\Core\Session\AccountInterface $current_user * Current user. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * Request stack. * * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException */ public function __construct( EntityTypeManagerInterface $entity_type_manager, DownloadUrlBuilderInterface $download_url_builder, DateFormatterInterface $date_formatter, AccountInterface $current_user, RequestStack $request_stack, ) { $this->productTypeStorage = $entity_type_manager->getStorage('product_type'); $this->productStorage = $entity_type_manager->getStorage('product'); $this->orderStorage = $entity_type_manager->getStorage('order'); $this->fileStorage = $entity_type_manager->getStorage('file'); $this->downloadUrlBuilder = $download_url_builder; $this->dateFormatter = $date_formatter; $this->currentRequest = $request_stack->getCurrentRequest(); } /** * {@inheritdoc} */ public static function create(ContainerInterface $container) { return new static( $container->get('entity_type.manager'), $container->get('download_url_builder'), $container->get('date.formatter'), $container->get('current_user'), $container->get('request_stack') ); } /** * Access callback. * * @param \Drupal\Core\Session\AccountInterface $account * Current account. * @param \Drupal\user\UserInterface $user * User to view. * * @return bool|\Drupal\Core\Access\AccessResultInterface * Access result. */ public function fileListAccess(AccountInterface $account, UserInterface $user) { return $user->access('view', $account, TRUE); } /** * Build page of purchased files. * * @param \Drupal\user\UserInterface $user * Displayed user. * * @return array * Render array. */ public function fileList(UserInterface $user) { $bundles = $this->getDownloadableBundles(); if (empty($bundles)) { // @todo Display empty message. return []; } $bundle_ids = array_map(function (ProductTypeInterface $type) { return $type->id(); }, $bundles); $order_ids = $this->getOrderIds($user, $bundle_ids); if (empty($order_ids)) { // @todo Display empty message. return []; } $order_data = $this->getDataOfPurchasedProducts($order_ids, $bundle_ids); $list = []; foreach ($order_data as $product_data) { $list += $this->buildRowData($user, $product_data); } if (empty($list)) { // @todo Display empty message. return []; } $table = [ '#theme' => 'table__purchased_files', '#header' => [ 'name' => $this->t('Filename'), 'size' => $this->t('Size'), 'date' => $this->t('Date'), 'product' => $this->t('Product', [], ['context' => 'arch_product']), 'order' => $this->t('Order', [], ['context' => 'arch_order']), ], '$items' => $list, '#rows' => [], ]; foreach ($list as $item) { $row = [ 'name' => NULL, 'size' => NULL, 'date' => NULL, 'product' => NULL, 'order' => NULL, ]; $row['name'] = $item['file_name']; if ( $user->id() == $this->currentUser()->id() && $item['url'] instanceof Url ) { $download_link = Link::fromTextAndUrl($item['file_name'], $item['url']); $row['name'] = [ 'data' => $download_link->toRenderable(), ]; } $row['size'] = $this->formatSize($item['file_size']); $row['date'] = $this->dateFormatter->format($item['purchase_date']); $product_url = Url::fromRoute('entity.product.canonical', ['product' => $item['product_id']]); $product_link = Link::fromTextAndUrl($item['product_label'], $product_url); $row['product'] = [ 'data' => $product_link->toRenderable(), ]; $order_url = Url::fromRoute('entity.order.canonical', ['order' => $item['order_id']]); $order_link = Link::fromTextAndUrl('#' . $item['order_id'], $order_url); $row['order'] = [ 'data' => $order_link->toRenderable(), ]; $table['#rows'][] = $row; } return $table; } /** * Generates a string representation for the given byte count. * * @param int|float $size * A size in bytes. * @param null|string $langcode * Optional language code to translate to a language other than what is used * to display the page. * * @return \Drupal\Core\StringTranslation\TranslatableMarkup * A translated string representation of the size. * * @see \Drupal\Core\StringTranslation\ByteSizeMarkup::create() */ protected function formatSize($size, $langcode = NULL) { if (!function_exists('format_size')) { // phpcs:ignore Drupal.Classes.FullyQualifiedNamespace.UseStatementMissing return \Drupal\Core\StringTranslation\ByteSizeMarkup::create($size ?? 0, $langcode); } return format_size($size, $langcode); } /** * Get list of downloadable product types. * * @return \Drupal\arch_product\Entity\ProductTypeInterface[] * Product type list. */ protected function getDownloadableBundles() { $bundles = $this->productTypeStorage->loadMultiple(); return array_filter($bundles, function (ProductTypeInterface $type) { return $type->getThirdPartySetting('arch_downloadable_product', 'is_downloadable'); }); } /** * Get list of order IDs with purchased files. * * @param \Drupal\Core\Session\AccountInterface $user * Customer. * @param array $bundle_ids * List of downloadable product types. * * @return int[] * List of order IDs. */ protected function getOrderIds(AccountInterface $user, array $bundle_ids) { $order_query = $this->orderStorage->getQuery(); $order_query->condition('uid', $user->id()); $order_query->condition('line_items.product_bundle', $bundle_ids, 'IN'); $values = $order_query->accessCheck(TRUE)->execute(); return array_map('intval', array_values($values)); } /** * Get data of purchased products. * * @param array $order_ids * List of order IDs. * @param array $bundle_ids * Downloadable product types. * * @return array * Product data. */ protected function getDataOfPurchasedProducts(array $order_ids, array $bundle_ids) { $products = []; foreach ($order_ids as $order_id) { /** @var \Drupal\arch_order\Entity\OrderInterface $order */ $order = $this->orderStorage->load($order_id); foreach ($order->getProducts() as $product_line_item) { try { /** @var \Drupal\arch_order\Plugin\Field\FieldType\OrderLineItemFieldItem $product_line_item */ if (!in_array($product_line_item->get('product_bundle') ->getValue(), $bundle_ids)) { continue; } $product_id = (int) $product_line_item->get('product_id')->getValue(); $products[$product_id] = [ 'product_id' => $product_id, 'purchase_date' => (int) $order->getCreatedTime(), 'order_id' => (int) $order->id(), ]; } catch (\Exception $e) { // @todo Handle error!. } } } uasort($products, function ($a, $b) { $fields = [ 'purchase_date', 'order_id', 'product_id', ]; foreach ($fields as $field) { $date = SortArray::sortByKeyInt($a, $b, $field); if ($date !== 0) { return $date; } } return 0; }); return $products; } /** * Build row data. * * @param \Drupal\user\UserInterface $user * Customer. * @param array $data * Purchased product data. * * @return array * Purchased file data. */ protected function buildRowData(UserInterface $user, array $data) { $list = []; /** @var \Drupal\arch_product\Entity\ProductInterface $product */ $product = $this->productStorage->load($data['product_id']); $langcode = $this->languageManager()->getCurrentLanguage()->getId(); if ($product->hasTranslation($langcode)) { $product = $product->getTranslation($langcode); } /** @var \Drupal\file\Plugin\Field\FieldType\FileFieldItemList $file_field */ $file_field = $product->get('product_file'); foreach ($file_field as $item) { /** @var \Drupal\file\Plugin\Field\FieldType\FileItem $item */ /** @var \Drupal\file\FileInterface $file */ $file = $item->entity; $row = $data; $row['product_label'] = $product->label(); $row['fid'] = (int) $file->id(); $row['file_name'] = $file->getFilename(); $row['file_size'] = (int) $file->getSize(); $row['file_mime'] = $file->getMimeType(); try { $row['file_description'] = $item->get('description')->getValue(); } catch (\Exception $e) { $row['file_description'] = NULL; } $row['file_uri'] = $file->getFileUri(); $row['url'] = NULL; if ($user->id() == $this->currentUser()->id()) { $row['url'] = $this->downloadUrlBuilder->getDownloadUrl($product, $file, $user); } $list[$file->id()] = $row; } return $list; } }