commercetools-8.x-1.2-alpha1/src/Plugin/Block/CommercetoolsCategoriesListBlockBase.php
src/Plugin/Block/CommercetoolsCategoriesListBlockBase.php
<?php
namespace Drupal\commercetools\Plugin\Block;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a base class for listing blocks of commercetools categories.
*/
abstract class CommercetoolsCategoriesListBlockBase extends CommercetoolsCatalogActionBlockBase {
const MAXIMUM_EXPANDED_TREE = 20;
/**
* The Commercetools service.
*
* @var \Drupal\commercetools\CommercetoolsService
*/
protected $ct;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
$instance = parent::create(...func_get_args());
$instance->ct = $container->get('commercetools');
return $instance;
}
/**
* {@inheritdoc}
*/
public function getBlockConfigKeys(): array {
$keys = parent::getBlockConfigKeys();
$keys[] = 'max_level';
$keys[] = 'display_from_current';
$keys[] = 'initial_level';
$keys[] = 'display_style';
$keys[] = 'cards_columns';
$keys[] = 'parent_category';
return $keys;
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration(): array {
return [
'max_level' => 1,
'display_from_current' => FALSE,
'initial_level' => 1,
'display_style' => 'list',
'cards_columns' => 3,
'parent_category' => '',
];
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state): array {
$form = parent::blockForm($form, $form_state);
$categoriesTree = $this->getCategoriesTree();
$categoryOptions = $this->buildHierarchicalOptions($categoriesTree);
$categoryOptions = ['' => $this->t('Display from the root')] + $categoryOptions;
$form['display_from_current'] = [
'#type' => 'checkbox',
'#title' => $this->t('Display from current category'),
'#default_value' => $this->configuration['display_from_current'] ?? FALSE,
'#description' => $this->t('If checked, the block will use the category from URL.'),
];
$form['parent_category'] = [
'#type' => 'select',
'#title' => $this->t('Parent category'),
'#default_value' => $this->configuration['parent_category'] ?? '',
'#options' => $categoryOptions,
'#states' => [
'visible' => [
':input[name="settings[display_from_current]"]' => ['checked' => FALSE],
],
],
];
$form['display_style'] = [
'#type' => 'radios',
'#title' => $this->t('Display style'),
'#default_value' => $this->configuration['display_style'] ?? 'list',
'#options' => [
'list' => $this->t('List'),
'cards' => $this->t('Cards'),
],
];
$form['cards_columns'] = [
'#type' => 'select',
'#title' => $this->t('Number of columns (cards only)'),
'#default_value' => $this->configuration['cards_columns'] ?? 3,
'#options' => array_combine(range(1, 6), range(1, 6)),
'#states' => [
'visible' => [
':input[name="settings[display_style]"]' => ['value' => 'cards'],
],
],
];
$categories = $this->ct->getProductCategoriesTree();
$maxDepth = $this->getCategoriesTreeDepth($categories);
$options = range(0, $maxDepth);
$options[0] = $this->t('Unlimited');
$form['max_level'] = [
'#type' => 'select',
'#title' => $this->t('Number of levels to display'),
'#default_value' => $this->configuration['max_level'],
'#options' => $options,
'#description' => $this->t('Currently active category always expands its children. Sibling categories of level that exceeds this number will be hidden from display.'),
'#required' => TRUE,
'#states' => [
'visible' => [
':input[name="settings[display_style]"]' => ['value' => 'list'],
],
],
];
$form['initial_level'] = [
'#type' => 'select',
'#title' => $this->t('Initial visibility level'),
'#default_value' => $this->configuration['initial_level'] ?? 1,
'#options' => array_combine(range(1, 10), range(1, 10)),
'#description' => $this->t('Levels below this number are collapsed by default.'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts(): array {
$cacheContexts = parent::getCacheContexts();
return Cache::mergeContexts($cacheContexts, [
'url.query_args',
]);
}
/**
* Provides a categories tree.
*
* @param string|null $activeCategory
* Any category ID to build active-trail on. Optional.
*
* @return array
* A tree of categories.
*/
protected function getCategoriesTree(?string $activeCategory = NULL): array {
$categories = $this->ct->getProductCategories();
// Set active-trails if available.
if ($activeCategory && !empty($categories[$activeCategory])) {
$categories[$activeCategory]['is_active'] = TRUE;
do {
$categories[$activeCategory]['in_active_trail'] = TRUE;
$activeCategory = $categories[$activeCategory]['parent'] ?? NULL;
} while (array_key_exists($activeCategory, $categories));
}
return $this->ct->getProductCategoriesTree($categories);
}
/**
* Returns the maximum depth of categories tree.
*
* @param array $list
* A data structure representing the categories tree.
*
* @return int
* The maximum array depth.
*/
protected function getCategoriesTreeDepth(array $list): int {
$max_indentation = 1;
$array_str = print_r($list, TRUE);
$lines = explode("\n", $array_str);
foreach ($lines as $line) {
if (!stripos($line, '=> Array')) {
continue;
}
$indentation = (strlen($line) - strlen(ltrim($line))) / 4;
if ($indentation > $max_indentation) {
$max_indentation = $indentation;
}
}
return (int) ceil(($max_indentation - 1) / 4);
}
}
