Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough

Creating a custom Add to Cart form with Drupal Commerce

Parent Feed: 

I'm currently helping some friends rebuild the theological education website, BiblicalTraining.org, in Drupal 7 with Drupal Commerce. I built the Drupal 6 version some years ago to move the website from a bespoke PHP application into Drupal, using modules like Ubercart, Quiz, and Organic Groups to solve most of the requirements. However, the donation code was more or less a straight Drupal port of the PHP script handling donations via PayTrace at the time.

With the rebuild onto Drupal 7, we have the opportunity to unify the donation form with the course payment checkout form to begin using Commerce Reports and multiple payment methods, including the newly released Commerce PayPal 2.0 for Express Checkout payments. However, we still have to deal with actually creating an order to represent the donation payment on the checkout form.

On this site, we don't use product display nodes. I decided instead to directly instantiate the Add to Cart form at a custom URL and avoid the need to create a product display node type just for the one form.

I started by defining a donation product type so the site administrators could create a product to represent the various campaigns donors could give toward. Since I am building the form in a small page callback function, I can easily support as many donation products as get created without introducing the human process of making sure administrators both add the product and update a product display node to reference it.

I created a single menu item at /donate whose page callback is the following:

<?php
/**
* Page callback: builds a donation Add to Cart form.
*/
function bt_donation_form_page() {
// Create the donation line item defaulted to the General fund.
$line_item = commerce_product_line_item_new(commerce_product_load(5), 1, 0, array('context' => array('display_path' => 'donate')), 'donation');
$wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);// Set the line item context to reference all of the donation products.
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'commerce_product')
->
entityCondition('bundle', 'donation')
->
propertyCondition('status', TRUE);$result = $query->execute();

if (!empty(

$result['commerce_product'])) {
$line_item->data['context']['product_ids'] = array_keys($result['commerce_product']);
}
// Do not allow the Add to Cart form to combine line items.
$line_item->data['context']['add_to_cart_combine'] = FALSE;

return

drupal_get_form('commerce_cart_add_to_cart_form', $line_item);
}
?>

To build an Add to Cart form, you have to pass in a line item object that contains all the information required to build the form. Our page callback starts by building a donation product line item, a custom line item type that allows for custom donation amounts as demonstrated in this tutorial video from Randy Fay. The product the line item references, "General fund", will be the default selection on the Add to Cart form, and the context array I pass to the line item creation function populates the line item's display_path field to link this line item to the donation page.

Next I use a simple EntityFieldQuery to find all the enabled donation products on the site. These product IDs are added to the line item's build context array, which the Add to Cart form builder function uses to know which products the form should represent. In a product display scenario, these product IDs would come from the value of the product reference field. Here I pass them in directly and get a simple Add to Cart form that is now ready to be themed to perfection:

Additional improvements on the site involve changing the "Add to Cart" button to read "Donate Now" and using a custom message upon submission with a redirect to /checkout. In case the donor cancels checkout and ends up at /cart, I do two things to ensure they can't manipulate the quantity of the donation line items: I removed the quantity textfield field from the View, but in case we need to add it back in later for other line item types, I also use a form alter to convert any donation line item quantity textfield to the plain text quantity value:

<?php
/**
* Implements hook_form_FORM_ID_alter().
*/
function bt_donation_form_views_form_commerce_cart_form_default_alter(&$form, &$form_state) {
// Loop over the quantity textfields on the form.
foreach (element_children($form['edit_quantity']) as $key) {
$line_item_id = $form['edit_quantity'][$key]['#line_item_id'];
$line_item = commerce_line_item_load($line_item_id);// If it's for a donation line item...
if ($line_item->type == 'donation') {
// Turn it into a simple text representation of the quantity.
$form['edit_quantity'][$key]['#type'] = 'value';
$form['edit_quantity'][$key]['#suffix'] = check_plain($form['edit_quantity'][$key]['#default_value']);
}
}
}
?>

This might actually make a handy contrib module...

If the donor leaves a donation line item in the cart and goes back to the donation form, you'll notice toward the end of my page callback that I also indicate in the context array that the form should not attempt to combine like items during the Add to Cart submission process. I actually realized the form builder function was missing some documentation for context keys, so I added those in straightaway.

All told, I spent a couple hours building a custom donation form and workflow that now perfectly integrates with the checkout process used by the rest of the site. This will make it easier to customize and maintain long term, and it allows us to use existing Drupal Commerce payment method modules to manage donations instead of having to write and maintain a custom payment module for the task.

Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web