Even though a previous section looks like working cart views, in truth it is not. The form markup was embedded as expected, but unfortunately, the main important logic is not working. You cannot change the quantity, remove line item, etc. It’s still just a content view.
For those who are searching for a solution, I will describe how we’ve managed to make it work.
First of all, I’ve added a new button, which does the refreshing of the cart. Now my buildInlineForm method looks the following way:
public function buildInlineForm(array $inline_form, FormStateInterface $form_state) {
$inline_form = parent::buildInlineForm($inline_form, $form_state);
$order = $this->entityTypeManager->getStorage('commerce_order')->load($this->configuration['order_id']);
if (!$order) {
throw new \RuntimeException('Invalid order_id given to the coupon_redemption inline form.');
}
$inline_form['#configuration'] = $this->getConfiguration();
$inline_form['cart'] = [
'#prefix' => '<div class="cart cart-form">',
'#suffix' => '</div>',
'#type' => 'view',
'#name' => 'commerce_cart_form',
'#arguments' => [$order->id()],
'#embed' => TRUE,
'#attached' => [
'library' => ['sharethelove_global/cart_features'],
],
];
$inline_form['update'] = [
'#type' => 'submit',
'#value' => t('Update Cart'),
'#name' => 'update_cart',
'#attributes' => [
'class' => ['cart-refresh'],
],
'#limit_validation_errors' => [
$inline_form['#parents'],
],
'#submit' => [
[get_called_class(), 'refreshCart'],
],
'#ajax' => [
'callback' => [get_called_class(), 'ajaxRefreshForm'],
'element' => $inline_form['#parents'],
],
];
return $inline_form;
}
Note: #submit and #ajax properties on this button. Keep in mind that ‘ajaxRefreshForm' is introduced in Drupal\commerce\AjaxFormTrait and can be very useful when you are trying to build a one-page checkout. This ‘ajaxRefreshForm’ method refreshes the whole checkout form without any additional movements.
In this case, the only thing we needed to handle is a ‘refreshCart’ method itself. Most of it I’ve ripped off the commerce_cart module, though with some limitations. For example, for some reason, I could not access the $form_state value on this level and without a choice, needed to fetch data from the user input.
Also, to make my life easier, I avoided adding a canonical “remove” button and just used zero quantity to remove the order item.
In the end, my ‘refreshCart’ method, looked like this:
public static function refreshCart(array &$inline_form, FormStateInterface $form_state) {
$input = $form_state->getUserInput();
if (isset($input['edit_quantity'])) {
$cart = \Drupal::entityTypeManager()->getStorage('commerce_order')->load($inline_form['cart_pane']['form']['#configuration']['order_id']);
$cart_manager = \Drupal::service('commerce_cart.cart_manager');
$view = Views::getView('commerce_cart_form');
$view->setDisplay('default');
$view->setArguments([$cart->id()]);
$view->execute();
$save_cart = FALSE;
foreach ($input['edit_quantity'] as $row_index => $quantity) {
if (!is_numeric($quantity) || $quantity < 0) {
continue;
}
$row = $view->result[$row_index];
$order_item = $row->_relationship_entities['order_items'];
if ($order_item->getQuantity() == $quantity) {
continue;
}
if ($quantity > 0) {
$order_item->setQuantity($quantity);
$cart_manager->updateOrderItem($cart, $order_item, FALSE);
}
else {
$cart_manager->removeOrderItem($cart, $order_item, FALSE);
}
$save_cart = TRUE;
}
if ($save_cart) {
$cart->save();
$form_state->setRebuild();
}
}
}
A few hints for those, who are going to follow this path and embed the Cart form on a page:
- You might want to remove default actions from the cart view. Usually, these are “Update cart” and “Checkout” buttons. You can do this using a regular form alter.
- If you want to have a cart updated automatically, without need of “Update cart” button, you can simply add a short JS script which will trigger “Update cart” on “Quantity” input change. Keep in mind that you need to use setTimeout carefully to avoid race condition when the user changes quantity multiple times when the AJAX request is still in progress.
- BTW, I have a piece of code for the item #2. Feel free to shoot me a message if you think it needs to be included in the article. Honestly speaking, I excluded it to not make the blog post bloated.