Customize Product

Posts: 20
Joined: 05/05/2008

I'm working on a module that will enable a customer to customize their product before they add it to their cart. I'm implementing the hook_form_alter on the product view to add additional form fields including: a text area and a file upload. (I've included an attachment of what that currently looks like.)

I'd like to capture the data from the altered form and do something useful with it. So far I'm able to upload a file to the server, from that point on I'm a little stuck. Ideally the file path and additional form info would be posted someplace convenient for the store owner to retrieve and associate with the order.

One idea would be to use the uc_order_comment_save function to append the data to the comment field in the admin order view, but I'm not sure when the function gets called or which hook to use.

Here's the code I'm working with, any ideas or suggestions would be extremely helpful.
(Please excuse me if some of my comments are wrong, I'm a PHP noob.)

<?php
/*
implementation of hook_form_alter
*/

function uc_upload_logo_form_alter($form_id, &$form) {
    if (
'uc_product_add_to_cart_form_' . $form['nid']['#value'] == $form_id) {
       
$node = node_load($form['nid']['#value']);
           
           
           
// This checks whether the product is a "personalized_cards" class
            // The form won't show up unless the product is a class named with the above!!!
           
if($node->type == 'personalized_cards'){
           
               
$form['personal_message'] = array(
                   
'#title' => t('Personalized message'),
                   
'#type' => 'textarea',
                   
'#description' => t('Please enter your personalized message here'),
                   
'#cols' => 40,
                   
'#rows' => 3,
                   
'#resizable' => FALSE,
                   
'#weight' => -1
                   
);
               
               
$form['logo'] = array(
                   
'#type' => 'file',
                   
'#title' => t('Upload a personalized image'),
                   
'#size' => 40,
                   
'#weight' => 0,
                   
'#description' => t('Please upload the image you would like to appear in your card.
                    Image resolution of at least 300dpi is required. Please be patient while your file uploads.'
)
                );   
           
// Required by Drupal form API               
           
$form['#attributes']['enctype'] = 'multipart/form-data';
           
// Appends the form data to the form submit function
            //($form_id = uc_product_add_to_cart_form, $form_values =
           
$form['#submit'] += array('uc_upload_logo_form_submit' => array($form_id, &$form_values));
  
        }
    }
}


// Call this function after the form submit has passed.
// Writes the submitted file to the server. Make sure a directory exists or it will fail silently!

function uc_upload_logo_form_submit($form_id, $form_values) {
$dir = variable_get('file_directory_path', NULL);   

// Commented out for testing
//$is_writable = file_check_directory($dir, 1);
   // if($is_writable) {
  
    // Save the file to the below path and append the actual file name
     
$dir .= 'imagecache/logos/' . $dir;
     
     
$source = file_check_upload('logo');
          
     
// Security measure to prevent exploit of file.php.png
     
$source->filename = upload_munge_filename($source->filename);
     
      if (
$file = file_save_upload($source,$dir )) {
        if (
image_get_info($file->filepath)) {
         
drupal_set_message(t('New image saved. '.$file->filepath.''));
         

        } else {
         
file_delete($file->filepath);
         
drupal_set_message('Uploaded file does not appear to be a valid image file. Please try again.');
        }
  
    }
}
?>

Thanks in advance for anyone willing to take the time to help me learn. I would definitely be interested in submitting the module as a contribution when it's finished, I think there's definitely a need for it.

AttachmentSize
product_view.jpg36.71 KB
Posts: 20
Joined: 05/05/2008

What, no takers?

I'm hoping someone can help me understand how to pass the form_alter data along to the correct hook that's responsible for writing data to the order view. Is this a mult-step process or can it be done with a single function call?

Thanks!

Posts: 5367
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

If I were you, I'd look into some way to store the information in the product's data array. Then it will be available in the cart, during checkout, and when dealing with orders later on. I believe you can use hook_add_to_cart_data() to accomplish this. Check out my example in the Userpoints Discount module.

Posts: 20
Joined: 05/05/2008

Hi Ryan, thank you for your advice. I've been looking at the module you pointed me to and I definitely think it will be helpful for me to reference. I've also been looking at the hook_add_to_cart_data() in the uc_attribute.module.

With my current code, it looks like I can view the collected form data with:

<?php
<pre>'. print_r($form_values, true) .'</pre>');
?>

It looks like everything except the file path shows up. Is there a better way to track whether the form data is added to the cart successfully? Once that's solved then what? Should I be writing to the database or will Ubercart handle that if I've used hook_add_to_cart_data() correctly?

Here's my code so far:

<?php
/*
* Implementation of hook_form_alter()
*/

function uc_upload_logo_form_alter($form_id, &$form) {
    if (
'uc_product_add_to_cart_form_' . $form['nid']['#value'] == $form_id) {
       
$node = node_load($form['nid']['#value']);
           
           
           
// Checks whether the product is a "personalized_cards" class
            // The form won't show up unless the product is a class named with the above!!!
           
if($node->type == 'personalized_cards'){
           
               
// Adds a text area field
               
$form['personal_message'] = array(
                   
'#title' => t('Personalized message'),
                   
'#type' => 'textarea',
                   
'#description' => t('Please enter your personalized message here'),
                   
'#cols' => 40,
                   
'#rows' => 3,
                   
'#resizable' => FALSE,
                   
'#weight' => -1
                   
);
               
               
// Adds an upload file field
               
$form['upload_logo'] = array(
                   
'#type' => 'file',
                   
'#title' => t('Upload a personalized image'),
                   
'#size' => 40,
                   
'#weight' => 0,
                   
'#description' => t('Please upload the image you would like to appear in your card.
                    Image resolution of at least 300dpi is required. Please be patient while your file uploads.'
)
                    );   
           
           
// Required by Drupal form API               
           
$form['#attributes']['enctype'] = 'multipart/form-data';
           
           
// Appends the form data to the form submit function 
           
$form['#submit'] += array('uc_upload_logo_form_submit' => array($form_id, &$form_values));
  
        }
    }
}

// Submit callback
function uc_upload_logo_form_submit($form_id, $form_values) {   
   
$dir = variable_get('file_directory_path', NULL);   
  
   
// Save the file to the below path and append the actual file name
   
$dir .= 'imagecache/logos/' . $dir;
     
     
$source = file_check_upload('upload_logo');
     
$source->filename = upload_munge_filename($source->filename);
     
      if (
$file = file_save_upload($source,$dir )) {
        if (
image_get_info($file->filepath)) {
         
drupal_set_message(t('New image saved. '.$file->filepath.''));
         
        } else {
         
file_delete($file->filepath);
         
drupal_set_message('Uploaded file does not appear to be a valid image file. Please try again.');
        }
    }
}

/**
* Implementation of hook_add_to_cart_data()
*/

function uc_upload_logo_add_to_cart_data($form_values){

    if (!isset(
$form_values['upload_logo'])){
       
drupal_set_message(t('No data was added to the cart...'));
    return array();
  }
 
 
// print_r outputs an array in a human readable format
 
drupal_set_message('Attributes to add to order Array(aid => oid):
      <pre>'
. print_r($form_values, true) .'</pre>');
 
  return array(
'logo_path' => $form_values['upload_logo'], 'personal_message' => $form_values['personal_message']);
}
?>

Sorry for the newbish questions and thanks again for responding back to me.

Z

Posts: 20
Joined: 05/05/2008

OK, I think I have this fairly well stubbed out, I made some code changes over the weekend and believe I'm utilizing all of the correct hooks. I still have some things to iron out - I'm still unsure if I should be storing the file paths in the database - or if this is handled by the hook_add_to_cart_data() function...

<?php
/*
* Implementation of hook_form_alter()
*/

function uc_upload_logo_form_alter($form_id, &$form) {
    if (
'uc_product_add_to_cart_form_' . $form['nid']['#value'] == $form_id) {
       
$node = node_load($form['nid']['#value']);
           
           
           
// Checks whether the product is a "personalized_cards" class
            // The form won't show up unless the product is a class named with the above!!!
           
if($node->type == 'personalized_cards'){
           
               
// Adds a text area field
               
$form['personal_message'] = array(
                   
'#title' => t('Personalized message'),
                   
'#type' => 'textarea',
                   
'#description' => t('Please enter your personalized message here'),
                   
'#cols' => 40,
                   
'#rows' => 3,
                   
'#resizable' => FALSE,
                   
'#weight' => -1
                   
);
               
               
// Adds an upload file field
               
$form['upload_logo'] = array(
                   
'#type' => 'file',
                   
'#title' => t('Upload a personalized image'),
                   
'#size' => 40,
                   
'#weight' => 0,
                   
'#description' => t('Please upload the image you would like to appear in your card.
                    Image resolution of at least 300dpi is required. Please be patient while your file uploads.'
),
                   
'#tree' => TRUE
                   
);   
               
$form['#validate']['uc_upload_logo_form_validate'] = array();
           
           
// Required by Drupal form API               
           
$form['#attributes']['enctype'] = 'multipart/form-data';
           
           
// Appends the form data to the form submit function 
           
$form['#submit'] += array('uc_upload_logo_form_submit' => array($form_id, &$form_values));
        }
    }
}

function
uc_upload_logo_form_validate($form_id, $form_values, $form){
   
// Validation code goes here
}
?>

I had to store the file path in the submit function with variable_set(), still haven't figured out the best way to pass that along to the other hooks. My guess would be to store it in the database...

<?php
// Submit function
function uc_upload_logo_form_submit($form_id, $form_values) {   
   
$dir = variable_get('file_directory_path', NULL);   
  
   
// Save the file to the below path and append the actual file name
   
$dir .= 'imagecache/logos/' . $dir;
   
     
     
$source = file_check_upload('upload_logo');
     
$source->filename = upload_munge_filename($source->filename);
     
      if (
$file = file_save_upload($source,$dir )) {
        if (
image_get_info($file->filepath)) {
         
drupal_set_message(t('New image saved. '.$file->filepath.''));
         
$filepath = $file->filepath;
         
$array["a"] = $filepath;
         
$str = serialize($array);
         
$strenc = urlencode($str);
         
         
variable_set($testpath, $filepath);
         
         
        } else {
         
file_delete($file->filepath);
         
drupal_set_message('Uploaded file does not appear to be a valid image file. Please try again.');
        }
    }
}

/**
* Implementation of hook_add_to_cart_data()
*/

function uc_upload_logo_add_to_cart_data($form_values){

 
// print_r outputs an array in a human readable format
 
drupal_set_message('Attributes to add to order Array(aid => oid):
      <pre>'
. print_r($form_values, true) . 'file path:'. variable_get($testpath, NULL) .'</pre>');
 
  return array(
'personal_message' => $form_values['personal_message'], 'upload_logo' => $testpath);

}

/**
* Implementation of hook_order_pane().
*/
function uc_upload_logo_order_pane() {
 
$panes[] = array(
   
'id' => 'test',
   
'callback' => 'uc_upload_logo_order_pane_file',
   
'title' => t('Customization info'),
   
'desc' => t('Specify and collect payment for an order.'),
   
'class' => 'pos-left',
   
'weight' => 4,
   
'show' => array('view', 'edit', 'customer'),
  );
  return
$panes;
}
?>

The problem here is that with variable_get, it stores a persistent variable, and the same file path will show up for every order.

<?php
function uc_upload_logo_order_pane_file($op, $arg1) {
  switch (
$op) {
    case
'view':
     
$output = t('File Path: ') . variable_get($testpath, NULL) . ('Personal Message') . $form_values['personal_message'] . '<br />';
      return
$output;
  }
}
?>

Posts: 20
Joined: 05/05/2008

OK, so I've tried out a few different ways of passing along the filepath string to the cart. Ryan suggested I implement hook_add_to_cart_data(), which makes perfect sense to me in theory but I've hit a brick wall. I don't understand how to finally display the filepath string in the order view pane. Ultimately, I need a filepath associated with each line item in the order so the order can be fulfilled by the store owner.

Currently all I get now is a new order pane with the same filepath string on EVERY order. (not too helpful!)

Here's a link to the custom product on my site for reference:
http://bwisepapers.com/node/13

Can anyone suggest how to successfully pass a simple string created at hook_form_alter() and pass it along to hook_order_pane()?

I'm losing sleep and sanity over this so any comments would be hugely appreciated.

Here's my latest code, sorry it's a little messy because I've been trying out a few different things.

<?php
/*
* Implementation of hook_form_alter()
*/

function uc_upload_logo_form_alter($form_id, &$form) {
    if (
'uc_product_add_to_cart_form_' . $form['nid']['#value'] == $form_id) {
       
$node = node_load($form['nid']['#value']);
           
           
           
// Checks whether the product is a "personalized_cards" class
            // The form won't show up unless the product is a class named with the above!!!
           
if($node->type == 'personalized_cards'){
           
               
// Adds a text area field
               
$form['personal'] = array(
                   
'#title' => t('Personalization'),
                   
'#type' => 'fieldset',
                   
'#weight' => -2,
                   
'#collapsible' => FALSE,
                   
'#collapsed' => FALSE
                   
);
               
               
$form['personal']['personal_message'] = array(
                   
'#title' => t('Personalized message'),
                   
'#type' => 'textarea',
                   
'#description' => t('Please enter your personalized message here'),
                   
'#cols' => 40,
                   
'#rows' => 3,
                   
'#resizable' => FALSE,
                   
'#weight' => -1
                   
);
               
               
// Adds an upload file field
               
$form['personal']['upload_logo'] = array(
                   
'#type' => 'file',
                   
'#title' => t('Upload a personalized image'),
                   
'#size' => 40,
                   
'#weight' => 0,
                   
'#description' => t('Please upload the image you would like to appear in your card.
                    Image resolution of at least 300dpi is required. Please be patient while your file uploads.'
),
                   
'#tree' => TRUE
                   
);   
               
//$form['#validate']['uc_upload_logo_form_validate'] = array();
           
            // Required by Drupal form API               
           
$form['#attributes']['enctype'] = 'multipart/form-data';
           
           
// Appends the form data to the form submit function 
            /*
            $form['#submit'] += array('uc_upload_logo_form_submit' => array($form_id, &$form_values));
            */
           
$form['#submit'] = array_merge(array('uc_upload_logo_form_submit' => array()), $form['#submit']);
           
        }
    }
}
/*
function uc_upload_logo_form_validate($form_id, $form_values, $form){
    // Validation code goes here
}
*/


/*
** Submit function
*/

function uc_upload_logo_form_submit($form_id, $form_values) {   
   
   
$dir = variable_get('file_directory_path', NULL);   
  
   
// Save the file to the below path and append the actual file name
   
$dir .= 'imagecache/logos/' . $dir;
   
     
     
$source = file_check_upload('upload_logo');
     
$source->filename = upload_munge_filename($source->filename);
     
      if (
$file = file_save_upload($source,$dir )) {
        if (
image_get_info($file->filepath)) {
         
drupal_set_message(t('New image saved. '.$file->filepath.''));
         
$filepath = $file->filepath;
         
$array["a"] = $filepath;
         
$str = serialize($array);
         
$strenc = urlencode($str);
         
         
//variable_set($testpath, $filepath);
         
         
          // Save the annotation to the database.
         
             
global $user;
             
$nid = $form_values['nid'];
             
$message = $form_values['personal_message'];
             
             
             
             
db_query("DELETE FROM {uc_upload_logo} WHERE uid = %d and nid = %d", $user->uid,
                 
$nid);
                 
                 
             
db_query("INSERT INTO {uc_upload_logo} (uid, nid, file_path, message, timestamp) VALUES (%d, %d,
                  '%s', '%s', %d)"
, $user->uid, $nid, $filepath, $message, time());
             
drupal_set_message(t('Your filepath was added to the database.'));
         
         
         
        } else {
         
file_delete($file->filepath);
         
drupal_set_message('Uploaded file does not appear to be a valid image file. Please try again.');
        }
    }
}

/**
* Implementation of hook_add_to_cart_data()
*/

function uc_upload_logo_add_to_cart_data($form_values){
   
   
// Get customization info
   
$result = db_query("SELECT file_path FROM {uc_upload_logo} WHERE uid = %d AND nid = %d", $user->uid, $node->nid);
   
//$node->file_path = db_result($result);

  // print_r outputs an array in a human readable format
 
drupal_set_message('Attributes to add to order Array(aid => oid):
      <pre>'
. print_r($form_values, true) . 'file path:'. variable_get($testpath, NULL) .'</pre>');
 
  return array(
'personal_message' => $form_values['personal_message'], 'upload_logo' => $result);

}

/**
* Implementation of hook_order_pane().
*/
function uc_upload_logo_order_pane() {
 
$panes[] = array(
   
'id' => 'customize_pane',
   
'callback' => 'uc_upload_logo_order_pane_file',
   
'title' => t('Customization info'),
   
'desc' => t('Customization info for the products.'),
   
'class' => 'pos-left',
   
'weight' => 4,
   
'show' => array('view'),
  );
  return
$panes;
}

function
uc_upload_logo_order_pane_file($op, $arg1) {
  switch (
$op) {
    case
'view':
     
$output = t('File Path: ') . variable_get($testpath, NULL) . '<br />' . ('Personal Message') . $form_values['personal_message'] . '<br />';
      return
$output;
  }
}
?>

Posts: 5367
Joined: 08/07/2007
AdministratorHead Code Monkey - I eat bugs.

On order panes, you'll need to be pulling the data from the order object represented by $arg1. For example:

<?php
 
foreach ($arg1->products as $product) {
    if (isset(
$product->data['upload_logo'])) {
     
$output .= t('File path: @filepath', array('@filepath' => $product->data['upload_logo']));
    }
  }
?>

This is untested code, but it should be enough to give you the gist of the idea.

Posts: 20
Joined: 05/05/2008

OK, you're probably my greatest hero right now! I kept getting a zero for the file path in the order pane, so I tried using the form data for ['personal_message'} instead. And it works!

I don't know why I can't get the file path but I'll keep trying and definitely post my results.

Thank you so much Ryan, this definitely is a life saver.

<?php
/*
* Implementation of hook_form_alter()
*/

function uc_upload_logo_form_alter($form_id, &$form) {
    if (
'uc_product_add_to_cart_form_' . $form['nid']['#value'] == $form_id) {
       
$node = node_load($form['nid']['#value']);
           
           
           
// Checks whether the product is a "personalized_cards" class
            // The form won't show up unless the product is a class named with the above!!!
           
if($node->type == 'personalized_cards'){
           
               
// Adds a text area field
               
$form['personal'] = array(
                   
'#title' => t('Personalization'),
                   
'#type' => 'fieldset',
                   
'#weight' => -2,
                   
'#collapsible' => FALSE,
                   
'#collapsed' => FALSE
                   
);
               
               
$form['personal']['personal_message'] = array(
                   
'#title' => t('Personalized message'),
                   
'#type' => 'textarea',
                   
'#description' => t('Please enter your personalized message here'),
                   
'#cols' => 40,
                   
'#rows' => 3,
                   
'#resizable' => FALSE,
                   
'#weight' => -1
                   
);
               
               
// Adds an upload file field
               
$form['personal']['upload_logo'] = array(
                   
'#type' => 'file',
                   
'#title' => t('Upload a personalized image'),
                   
'#size' => 40,
                   
'#weight' => 0,
                   
'#description' => t('Please upload the image you would like to appear in your card.
                    Image resolution of at least 300dpi is required. Please be patient while your file uploads.'
),
                   
'#tree' => TRUE
                   
);   
               
//$form['#validate']['uc_upload_logo_form_validate'] = array();
           
            // Required by Drupal form API               
           
$form['#attributes']['enctype'] = 'multipart/form-data';
           
           
// Appends the form data to the form submit function 
       
           
$form['#submit'] = array_merge(array('uc_upload_logo_form_submit' => array()), $form['#submit']);
           
        }
    }
}
/*
function uc_upload_logo_form_validate($form_id, $form_values, $form){
    // Validation code goes here
}
*/


/*
** Submit function
*/

function uc_upload_logo_form_submit($form_id, $form_values) {   
   
$dir = variable_get('file_directory_path', NULL);   
  
   
// Save the file to the below path and append the actual file name
   
$dir .= 'imagecache/logos/' . $dir;
    
     
$source = file_check_upload('upload_logo');
     
$source->filename = upload_munge_filename($source->filename);
     
      if (
$file = file_save_upload($source,$dir )) {
        if (
image_get_info($file->filepath)) {
         
drupal_set_message(t('New image saved. '.$file->filepath.''));
       
        } else {
         
file_delete($file->filepath);
         
drupal_set_message('Uploaded file does not appear to be a valid image file. Please try again.');
        }
    }
}

/**
* Implementation of hook_add_to_cart_data()
*/

function uc_upload_logo_add_to_cart_data($form_values){
   

 
// print_r outputs an array in a human readable format
 
drupal_set_message('Attributes to add to order Array(aid => oid):
      <pre>'
. print_r($form_values, true) . 'file path:'. variable_get($testpath, NULL) .'</pre>');
 
  return array(
'personal_message' => $form_values['personal_message'], 'upload_logo' => $form_values['upload_logo']);

}

/**
* Implementation of hook_order_pane().
*/
function uc_upload_logo_order_pane() {
 
$panes[] = array(
   
'id' => 'customize_pane',
   
'callback' => 'uc_upload_logo_order_pane_file',
   
'title' => t('Customization info'),
   
'desc' => t('Customization info for the products.'),
   
'class' => 'pos-left',
   
'weight' => 4,
   
'show' => array('view'),
  );
  return
$panes;
}

function
uc_upload_logo_order_pane_file($op, $arg1) {
  switch (
$op) {
    case
'view':
    
// $output = t('File Path: ') . variable_get($testpath, NULL) . '<br />' . ('Personal Message') . $form_values['personal_message'] . '<br />';
     
foreach ($arg1->products as $product) {
         if (isset(
$product->data['upload_logo'])) {
            
$output .= t('Message: @message', array('@message' => $product->data['personal_message']));
    
     return
$output;
  }
}
break;
  }
}
?>

Posts: 20
Joined: 05/05/2008

Also noticed that the original personalization data will get overwritten if there's multiple items added to the cart. I'm sure that's an easy one to figure out though, if the add_to_cart_data() variable can be stored as an array...

Posts: 20
Joined: 05/05/2008

After testing out the custom order pane, I realized this probably wouldn't work if there were multiple items ordered, it would be better to list the data for each product on the product pane. (I made a snapshot of this in the attached PDF for reference.)

So now I've figured out how to alter the product pane with Tapir using hook_table_alter(), and I can pull in data stored in a database table. How should I be structuring my database table to display the correct data? Should I use the order_id as a key? When is the order_id variable created and should I be looking at hook_order to add to my database table or am I barking up the wrong tree here?

<?php
/*
* Implementation of hook_form_alter()
*/

function uc_upload_logo_form_alter($form_id, &$form) {
    if (
'uc_product_add_to_cart_form_' . $form['nid']['#value'] == $form_id) {
       
$node = node_load($form['nid']['#value']);
           
           
           
// Checks whether the product is a "personalized_cards" class
            // The form won't show up unless the product is a class named with the above.
           
           
if($node->type == 'personalized_cards'){
           
               
// Adds a text area field
               
$form['personal'] = array(