commercetools-8.x-1.2-alpha1/src/Plugin/Block/CommercetoolsProductListBlockBase.php
src/Plugin/Block/CommercetoolsProductListBlockBase.php
<?php
declare(strict_types=1);
namespace Drupal\commercetools\Plugin\Block;
use Drupal\commercetools\CommercetoolsLocalization;
use Drupal\commercetools\Exception\CommercetoolsOperationFailedException;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a base class for commercetools product list blocks.
*/
abstract class CommercetoolsProductListBlockBase extends CommercetoolsCatalogBlockBase {
/**
* The Commercetools service.
*
* @var \Drupal\commercetools\CommercetoolsService
*/
protected $ct;
/**
* The commercetools products service.
*
* @var \Drupal\commercetools\CommercetoolsProducts
*/
protected $ctProducts;
/**
* The pager manager service.
*
* @var \Drupal\Core\Pager\PagerManagerInterface
*/
protected $pagerManager;
/**
* The form builder.
*
* @var \Drupal\Core\Form\FormBuilderInterface
*/
protected $formBuilder;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The request stack service.
*
* @var \Symfony\Component\HttpFoundation\RequestStack
*/
protected $requestStack;
/**
* The Commercetools API service.
*
* @var \Drupal\commercetools\CommercetoolsApiServiceInterface
*/
protected $ctApi;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
$instance = parent::create(...func_get_args());
$instance->ct = $container->get('commercetools');
$instance->ctProducts = $container->get('commercetools.products');
$instance->pagerManager = $container->get('pager.manager');
$instance->formBuilder = $container->get('form_builder');
$instance->configFactory = $container->get('config.factory');
$instance->requestStack = $container->get('request_stack');
$instance->ctApi = $container->get('commercetools.api');
return $instance;
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state): array {
$form = parent::blockForm($form, $form_state);
try {
$categoriesTree = $this->ct->getProductCategoriesTree();
$category_options = ['_none' => $this->t('Any (Show all products)')] + $this->buildHierarchicalOptions($categoriesTree);
}
catch (CommercetoolsOperationFailedException $e) {
$form['error'] = [
'#type' => 'markup',
'#markup' => '<div class="messages messages--warning">' . $e->getMessage() . '</div>',
'#weight' => 0,
];
$category_options = ['_none' => $this->t('No categories available due to an error.')];
}
$form['style'] = [
'#type' => 'radios',
'#title' => $this->t('Style'),
'#options' => [
'cards' => $this->t('Cards'),
'list' => $this->t('List'),
'titles' => $this->t('Titles'),
],
'#default_value' => $this->configuration['style'] ?? 'cards',
];
$form['columns_number'] = [
'#type' => 'select',
'#title' => $this->t('Number of columns'),
'#options' => array_combine(range(1, 12), range(1, 12)),
'#default_value' => $this->configuration['columns_number'] ?? 4,
'#states' => [
'invisible' => [
':input[name="settings[style]"]' => ['value' => 'titles'],
],
],
];
$form['categories'] = [
'#type' => 'select',
'#title' => $this->t('Categories'),
'#options' => $category_options,
'#default_value' => $this->configuration['categories'] ?? ['_none'],
'#multiple' => TRUE,
'#description' => $this->t('Filter the product list by one or more categories by default. Leave "Any" to allow all categories.'),
];
$form['total_limit'] = [
'#type' => 'number',
'#title' => $this->t('Total number of items'),
'#default_value' => $this->configuration['total_limit'] ?? NULL,
'#min' => 1,
'#max' => 100,
'#description' => $this->t('The total limit of items to be displayed. Leave empty to display all matching products.'),
];
$form['items_per_page'] = [
'#type' => 'number',
'#title' => $this->t('Items per page'),
'#default_value' => $this->configuration['items_per_page'] ?? 10,
'#min' => 1,
'#description' => $this->t('The number of products to show per page for this block. If empty, the general items per page value will be used.'),
];
$form['sort_by'] = [
'#type' => 'select',
'#title' => $this->t('Sort by'),
'#options' => $this->getSortByOptions(),
'#default_value' => $this->configuration['sort_by'] ?? 'createdAt',
'#description' => $this->t('Choose the field to sort by.'),
];
$form['sort_order'] = [
'#type' => 'select',
'#title' => $this->t('Sort order'),
'#options' => [
'asc' => $this->t('Ascending'),
'desc' => $this->t('Descending'),
],
'#default_value' => $this->configuration['sort_order'] ?? 'asc',
'#description' => $this->t('Choose the sort order.'),
];
$form['advanced'] = [
'#type' => 'details',
'#title' => $this->t('Advanced'),
'#open' => FALSE,
];
$form['advanced']['display_by_sku'] = [
'#type' => 'textfield',
'#title' => $this->t('Display products by SKU'),
'#default_value' => $this->configuration['display_by_sku'] ?? NULL,
'#description' => $this->t('Using this field you can list product SKUs to display only them.'),
];
$form['advanced']['custom_filters'] = [
'#type' => 'textarea',
'#title' => $this->t('Additional filters'),
'#default_value' => $this->configuration['custom_filters_raw'] ?? '',
'#description' => $this->t('Here you can put a JSON with custom query filters to apply on the product list, <a href="https://docs.commercetools.com/api/projects/products-search#filters">documentation</a>'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockValidate($form, FormStateInterface $form_state): void {
parent::blockValidate($form, $form_state);
$customFiltersRaw = trim($form_state->getValue(['advanced', 'custom_filters']));
if (!empty($customFiltersRaw)) {
json_decode($customFiltersRaw, TRUE);
if (json_last_error() !== JSON_ERROR_NONE) {
$form_state->setErrorByName(
'advanced][custom_filters',
$this->t('Invalid JSON format for additional filters: @error', [
'@error' => json_last_error_msg(),
])
);
}
}
}
/**
* Gets the sort by options for the block form.
*/
protected function getSortByOptions(): array {
$language = $this->configFactory
->get(CommercetoolsLocalization::CONFIGURATION_NAME)
->get(CommercetoolsLocalization::CONFIG_LANGUAGE);
return [
'createdAt' => $this->t('Default'),
'name.' . $language => $this->t('Name'),
];
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state): void {
parent::blockSubmit($form, $form_state);
$this->configuration['style'] = $form_state->getValue('style');
$categories = array_filter($form_state->getValue('categories'), fn ($category): bool => $category !== '_none');
$this->configuration['categories'] = $categories;
$displayBySku = trim($form_state->getValue(['advanced', 'display_by_sku']));
$this->configuration['display_by_sku'] = $displayBySku === '' ? [] : array_map('trim', explode(',', $displayBySku));
$customFiltersRaw = trim($form_state->getValue(['advanced', 'custom_filters']));
$this->configuration['custom_filters_raw'] = $customFiltersRaw;
$this->configuration['custom_filters'] = $customFiltersRaw === '' ? [] : json_decode($customFiltersRaw, TRUE);
}
/**
* {@inheritdoc}
*/
public function getBlockConfigKeys(): array {
$keys = parent::getBlockConfigKeys();
array_push(
$keys,
'total_limit',
'style',
'items_per_page',
'sort_by',
'sort_order',
'columns_number',
);
return $keys;
}
/**
* Fetches product categories from commercetools.
*/
protected function fetchCategories(): array {
$categories = $this->ct->getProductCategories() ?? [];
$fillChildren = function ($parentId = NULL) use ($categories, &$fillChildren) {
$children = [];
foreach ($categories as $category) {
if (($category['parent']['id'] ?? NULL) == $parentId) {
$catChildren = $fillChildren($category['id']);
$category['children'] = $catChildren;
$children[] = $category;
}
}
return $children;
};
return array_filter($fillChildren(), fn ($cat) => empty($cat['parent']['id']));
}
}
