I want to change the remove-button, that removes line items from the cart to an image button representing a trash can like this:

enter image description here

The code I am using in a custom module looks like this:

<? php
function mymodule_custom_form_views_form_commerce_cart_form_default_alter(&$form, &$form_state, $form_id) {
  foreach ($form['edit_delete'] as &$item) {

That code shows the icon instead of the text as expected, but unfortunately it introduces a major issue: now pressing the button doesn't remove the correponding line item, but wrongly successively removes the last added line-items. So when I added item1, item2, item3 to the cart in this order, then no matter which corresponding remove-button you press, item3 is removed. Next time, you press one of the remove-buttons of the remaining 2 line-items, then item-2 is removed. What's wrong with the above code?


You need to unset the #value on your image_buttons:

  unset($item['#value']); // this line here

The line item that is deleted is determined by the #triggering_element in your $form_data. This element is determined by a function _form_button_was_clicked($element, &$form_state) in form.inc in the core.

It seems that when you a #value has been set on your image_button, every image button is detected to have been clicked.

  // line 2227 in form.inc
  elseif (!empty($element['#has_garbage_value']) && isset($element['#value']) && $element['#value'] !== '') {
    return TRUE;

That means at the end of the ride, the last image button is always set as the #triggering_element, and the last line item is always removed.

Unsetting the #value fixes that. Setting it to an empty string doesn't, somehow.


Instead of unsetting the #value property (which works, but I find it personally counter-intuitive), you could try setting it by using the form_type_image_button_value() function.

As an example:

function my_custom_module_form_views_form_commerce_cart_form_default_alter(&$form, 
&$form_state, $form_id) {
  if (!empty($form['edit_delete'])) {
    foreach (element_children($form['edit_delete']) as $key) {
      $form['edit_delete'][$key]['#type'] = 'image_button';
      $form['edit_delete'][$key]['#src'] = 'sites/all/themes/trash.png';
      $form['edit_delete'][$key]['#alt'] = 'Remove';
      $form['edit_delete'][$key]['#value'] = 
        form_type_image_button_value($form['edit_delete'][$key], NULL, $form_state);

The form_type_image_button_value() function is built to correctly extract the clicked image button's value from the $_POST array taking browsers' peculiarities into account.

Good luck!

P.S.: Rewrote the hook implementation mildly to use element_children() when iterating through the line item delete elements.

  • I implemented your above code and now the same wrong behavior in reversed order happens: on first button press, item-1 is removed, then item-2 ans so on …!
    – Madam Adam
    Oct 25 '19 at 20:50
  • Let me check that again, thanks for the feedback. Oct 26 '19 at 6:21
  • Sorry, but sine I won't be online tomorrow, I'll have to give the bounty to the first answer because this one is working despite your one may be more elaborated once working.
    – Madam Adam
    Oct 26 '19 at 8:48
  • Fair enough, that is AFAICT the most established approach for this issue (which is not Drupal specific). Cheers and good luck with your projects! Oct 26 '19 at 8:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.