I was prepared to deliver a 5 minute "lightning talk" at the LA Drupal meeting tonight but we ran out of time. So, I thought I would take this opportunity to start posting tidbits to my blog.
The examples that I use in this post will be related to Donations but the technique will work for any product type. Donations have all sorts of special features that "regular" products do not. Variable prices (user assigned) being the most obvious.
I really want to thank Ryan for his excellent Ubercart contributions and Commerce Guys for sponsoring development of the contrib modules that I have used here. If you are taking Donations through Ubercart, you need to check out these modules:
http://drupal.org/project/uc_varprice
http://drupal.org/project/uc_atctweaks
http://drupal.org/project/uc_op_reports
http://drupal.org/project/uc_echecknet
Now, on to the cool stuff that I added into the mix to allow me to have one Donation Product (SKU) but still track which group is receiving the donation.
One Donation Product - Unlimited Causes
On our site, every Group (OG) is a Cause and we accept donations on their behalf.
There is one Product on the site. Donation.

This is how the Donation node appears by default:

By default the Donation is going to the "AllCause Foundation" (made up name). When I click on DONATE, it will be added to my cart with whatever amount I entered for the donation as the sale price.
If we go to our list of Causes, and click through to a Cause home page (OG Panels custom page), it looks like this:

Click on the Donations tab and we see the Donation product in a different form (see below). 1. the style has changed, but that's just Panels and Skinr and 2. the description text has changed.
The description text change is accomplished via some simple PHP code embedded in the Description field. Notice that the name of the Cause has been included in the product description. I'll show you how to do that shortly.

When I click on the DONATE button on this page, the product is added to my cart, but this time the name of the Cause is listed instead of the default. It appears that I have a new Donation product for every Cause on the site!
PHP code to alter the Product Description
A Product node is just like any other node in that is has a Title and a Body. In this case, the Body is called Description. Let's take a look at the Edit page for the Donation Product. I wrote some PHP if/else code to display a different description depending on whether the node was embedded in a Cause (OG) page or not.

Here is the actual code:
<?php
$nid = drupal_get_normal_path(arg(1));
$node = node_load(array('nid' => $nid));
if (og_is_group_type($node->type)) { // Check to see if we are embeded in an OG page
print ('Make a donation to <strong>'.$node->title.'</strong>');
} else {
global $base_path;
?>
Make a donation to AllCause Foundation here or <a href "<?php echo $base_path; ?>og/all">visit one of our many local Causes</a>.<br />
Visit the Cause of you choice - click on Donations tab. Use the form on that page to assign your donation to that Cause. <br /><br />
<cite>TODO: add the list of causes here - with a DONATE link next to each cause.</cite> <br />
<?php
}
?>[Update: I installed the codefilter module and the sample code looks much better now.]
When the Donation Product node is embedded inside a Panel pane that is associated with an Organic Group, I can detect that condition and use the name of the Cause (OG) in the description of the Product. Simple!
But, this is only superficial. I am not changing the Product in any way by doing this. Without doing something more, there would be no way to disctinguish one Donation from another. That's where Attributes come in.
Adding Attributes to the Donation Product

I have created two text attributes in Ubercart and I have assigned them both to the Donation Product. The two attributes are cause_name and cause_id. I need the name to list along with the product so the user knows where the donation went and I need the ID so I can easily run reports and track how much money has been collected for each Cause (OG).
Now, I can't just make these editable text fields and let the user fill them in - which is often how text attributes are used. I need to set them programmatically and immutably. So I turn to hook_form_alter().
First I need to know the form_id that I am altering. You can look at the HTML source to get this information, but I love Drupal for Firebug. I quickly determine that my form_id is uc_product_add_to_cart_form_134

and I can also see the exact structure of the form to determine how to reference the attributes in my code.

hook_form_alter()
Now I add some code in my modulename_form_alter() function to determine if the form is on an OG page (just like in the Product description code snippet shown previously) and then load the appropriate Title and ID into the Product attributes.
[Update: Nicole Bluto pointed out that it was not clear where this code should be added.]
In order to add this code, you will need to create a new module for your project. I always have a custom module for every Drupal install so I can invoke various hooks to alter forms or other modules behavior.
In the code below, replace modulename with the name of your custom module.
<?php
/*
* Implement hook_form_alter()
*/
function modulename_form_alter(&$form, $form_state, $form_id) {
/* Donate form - Associate a Cause with this Donation */
if ($form_id == 'uc_product_add_to_cart_form_134') {
$nid = drupal_get_normal_path(arg(1));
$node = node_load(array('nid' => $nid));
if (og_is_group_type($node->type)) {
$cause_id = <strong>$nid</strong>;
$cause_name = <strong>$node->title</strong>;
} else {
<em>$cause_id = 0</em>; // Default for AllCause Foundation
<em>$cause_name = 'AllCause Foundation'</em>;
}
$form['attributes'][1] = array(
'#type' => 'hidden',
'#value' => $cause_name,
);
$form['attributes'][2] = array(
'#type' => 'hidden',
'#value' => $nid,
);
}
}
?>Now you can see how the cause_id and cause_name would be set to something different on every Cause page.
Using attributes this way is very nice because I don't have to create any other special case to get the Cause name to show up in the shopping cart, in the receipt, on the "My Orders" section of the user profile page. It just works!

I hope you found this useful. I'm interested in hearing feedback. I'll enable comments on this blog once I get Facebook Connect or something like that configured for guests.
Cheers,
-ped-
- -ped-'s blog
- Login to post comments
