commercetools-8.x-1.2-alpha1/modules/commercetools_content/src/Form/AddToCartForm.php
modules/commercetools_content/src/Form/AddToCartForm.php
<?php
namespace Drupal\commercetools_content\Form;
use Drupal\commercetools\CommercetoolsCarts;
use Drupal\commercetools\CommercetoolsService;
use Drupal\commercetools\Routing\UiModulesRouteProviderBase;
use Drupal\commercetools_content\Routing\RouteProvider;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Security\TrustedCallbackInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides add to cart form.
*/
class AddToCartForm extends FormBase implements TrustedCallbackInterface {
/**
* The Commercetools service.
*
* @var \Drupal\commercetools\CommercetoolsService
*/
protected CommercetoolsService $ct;
/**
* The commercetools cart service.
*
* @var \Drupal\commercetools\CommercetoolsCarts
*/
protected CommercetoolsCarts $ctCart;
/**
* Array of product types.
*
* @var array
*/
protected array $productTypes;
/**
* Current product.
*
* @var array
*/
protected array $product;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
$instance = parent::create($container);
$instance->ct = $container->get('commercetools');
$instance->ctCart = $container->get('commercetools.carts');
$instance->productTypes = $instance->ct->getProductsTypes();
return $instance;
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'commercetools_content_add_to_cart_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $install_state = NULL) {
[$this->product] = $form_state->getBuildInfo()['args'];
$currentVariant = $form_state->isValueEmpty('product_attributes')
? $this->getCurrentVariant(['sku' => $this->product['current_sku']])
: $this->getCurrentVariant(['attributes' => $form_state->getValue('product_attributes')]);
$attributes = $this->getEnabledProductAttributes();
uasort($attributes, function ($attr1) {
return count($attr1['options']) > 1 ? 1 : -1;
});
$form['#theme'] = 'commercetools_add_to_cart_form';
$form['#attributes'] = [
'id' => ['product-add-to-cart-form'],
'class' => ['add-to-cart-form'],
];
$form['product_display'] = [
'#theme' => 'commercetools_product_page',
'#product' => array_merge($this->product, $currentVariant, ['id' => $this->product['id']]),
'#unavailable_data_text' => $this->config(CommercetoolsService::CONFIGURATION_NAME)->get(CommercetoolsService::CONFIG_UNAVAILABLE_DATA_TEXT),
'#attributes' => [
'class' => ['my-4'],
],
];
$form['product_attributes'] = [
'#type' => 'container',
'#tree' => TRUE,
'#attributes' => [
'class' => ['product__variants', 'row'],
],
];
foreach ($attributes as $attrName => $attr) {
if (count($attr['options']) > 1) {
$defaultValue = $currentVariant['attributes'][$attrName]['value'] ?? NULL;
$form['product_attributes'][$attrName] = [
'#type' => 'select',
'#pre_render' => [static::class . '::buildProductAttributeFormElement'],
'#attributes' => ['class' => ['w-50']],
'#title' => new FormattableMarkup('<span class="fw-medium align-self-center">@label</span>', [
'@label' => $attr['label'],
]),
'#options' => $attr['options'],
'#default_value' => $defaultValue,
'#ajax' => [
'callback' => '::ajaxCallback',
'disable-refocus' => FALSE,
'event' => 'change',
'wrapper' => 'product-add-to-cart-form',
'progress' => [
'type' => 'throbber',
'message' => $this->t('Updating...'),
],
],
];
}
else {
$value = reset($attr['options']);
$form['product_attributes'][$attrName] = [
'#theme' => 'commercetools_product_attribute',
'#label' => $attr['label'],
'#value' => [
'#theme' => 'commercetools_product_attribute_label',
'#key' => $attrName,
'#label' => $value,
],
];
}
}
$form['sku'] = [
'#type' => 'hidden',
'#value' => $currentVariant['sku'],
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Add to Cart'),
'#disabled' => empty($currentVariant['availability']),
'#attributes' => ['class' => ['btn-lg']],
];
return $form;
}
/**
* {@inheritdoc}
*/
public static function trustedCallbacks() {
return ['buildProductAttributeFormElement'];
}
/**
* Replace default form element wrapper.
*/
public static function buildProductAttributeFormElement(array $build) {
$build['#theme_wrappers'] = ['commercetools_product_attribute_form_element'];
return $build;
}
/**
* Ajax callback to update product variant.
*/
public function ajaxCallback(array &$form, FormStateInterface $form_state) {
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$cartResponse = $this->ctCart->getCurrentCart();
if (empty($cartResponse)) {
$cartResponse = $this->ctCart->createCart([
'lineItems' => [['quantity' => 1, 'sku' => $form_state->getValue('sku')]],
]);
$this->ctCart->setCurrentCart($cartResponse->getData());
}
else {
$cart = $cartResponse->getData();
$actions = [['addLineItem' => ['quantity' => 1, 'sku' => $form_state->getValue('sku')]]];
$this->ctCart->updateCart($cart['id'], $cart['version'], $actions);
}
[$product] = $form_state->getBuildInfo()['args'];
$this->messenger()->addMessage(new FormattableMarkup("@product_name added to @cart.", [
'@product_name' => $product['name'],
'@cart' => Link::createFromRoute('cart', RouteProvider::ROUTE_PREFIX . UiModulesRouteProviderBase::PAGE_CART_ROUTE)->toString(),
]));
}
/**
* Get enabled product attributes.
*
* @return array
* An array of all enabled attribute options.
*/
protected function getEnabledProductAttributes(): array {
$attrVariants = [];
$productType = $this->product['type']['key'];
if (empty($this->ct->getEnabledAttributes()[$productType])) {
return $attrVariants;
}
$allVariants = array_merge($this->product['variants'], [$this->product['masterVariant']]);
foreach ($this->ct->getEnabledAttributes()[$productType] as $attr) {
$attrName = $attr['name'];
$attrVariants[$attrName] = [
'label' => $attr['label'],
'options' => [],
];
foreach ($allVariants as $variant) {
if (!empty($variant['attributes'][$attrName])) {
$value = $variant['attributes'][$attrName]['value'];
$attrVariants[$attrName]['options'][$value] = $variant['attributes'][$attrName]['labelValue'];
}
}
}
return $attrVariants;
}
/**
* Get current product variant.
*
* @param array $variantArgs
* An array of args to find necessary variant.
*
* @return array
* The variant according to args.
*/
protected function getCurrentVariant(array $variantArgs): array {
if (empty($variantArgs['sku']) && empty($variantArgs['attributes'])) {
return $this->product['masterVariant'];
}
foreach ($this->product['variants'] as $variant) {
if (isset($variantArgs['sku']) && $variantArgs['sku'] != $variant['sku']) {
continue;
}
if (isset($variantArgs['attributes'])) {
foreach ($variantArgs['attributes'] as $attrName => $attrValue) {
$variantValue = $variant['attributes'][$attrName]['value'] ?? NULL;
if ($variantValue !== $attrValue) {
continue 2;
}
}
}
return $variant;
}
return $this->product['masterVariant'];
}
}
