You are here

commerce_physical.module in Commerce Physical Product 7

API for working with physical product types in Drupal Commerce.

File

commerce_physical.module
View source
<?php

/**
 * @file
 * API for working with physical product types in Drupal Commerce.
 */

/**
 * Determines the weight field to use for a given entity.
 *
 * @param string $entity_type
 *   The type of entity passed to the function.
 * @param object $entity
 *   The actual entity whose weight field name should be determined.
 *
 * @return string
 *   The name of the field to use on the entity to find a weight value or NULL
 *   if none was found.
 */
function commerce_physical_entity_weight_field_name($entity_type, $entity) {
  $field_name = commerce_physical_entity_field_name($entity_type, $entity, 'physical_weight');

  // Allow other modules to specify a different field name.
  drupal_alter('commerce_physical_entity_weight_field_name', $field_name, $entity_type, $entity);
  return $field_name;
}

/**
 * Determines the dimensions field to use for a given entity.
 *
 * @param string $entity_type
 *   The type of entity passed to the function.
 * @param object $entity
 *   The actual entity whose weight field name should be determined.
 *
 * @return string
 *   The name of the field to use on the entity to find a weight value or NULL
 *   if none was found.
 */
function commerce_physical_entity_dimensions_field_name($entity_type, $entity) {
  $field_name = commerce_physical_entity_field_name($entity_type, $entity, 'physical_dimensions');

  // Allow other modules to specify a different field name.
  drupal_alter('commerce_physical_entity_dimensions_field_name', $field_name, $entity_type, $entity);
  return $field_name;
}

/**
 * Determines the field of a certain type to use for a given entity.
 *
 * @param string $entity_type
 *   The type of entity passed to the function.
 * @param object $entity
 *   The actual entity whose weight field name should be determined.
 * @param string $field_type
 *   The field type to use.
 *
 * @return string
 *   The name of the field to use on the entity to find a weight value or NULL
 *   if none was found.
 */
function commerce_physical_entity_field_name($entity_type, $entity, $field_type) {
  $bundle = field_extract_bundle($entity_type, $entity);
  $field_name = NULL;

  // Look for the first field available for the entity.
  foreach (field_info_instances($entity_type, $bundle) as $instance_name => $instance) {

    // Load the field info for the current instance.
    $field = field_info_field($instance['field_name']);

    // If it's of the proper type...
    if ($field['type'] == $field_type) {

      // Save its name and exit the loop.
      $field_name = $instance_name;
      break;
    }
  }
  return $field_name;
}

/**
 * Determines the weight to use for a product line item on an order.
 *
 * @param commerce_line_item $line_item
 *   A product line item whose weight should be determined.
 *
 * @return array
 *   A weight field value array representing the weight of the product line item
 *   or NULL if none was found.
 */
function commerce_physical_product_line_item_weight($line_item) {
  $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
  $weight = NULL;

  // If the line item references a valid product...
  if (!empty($line_item_wrapper->commerce_product)) {
    $product = $line_item_wrapper->commerce_product
      ->value();
    if (!empty($product)) {

      // If the product has a valid weight field...
      $field_name = commerce_physical_entity_weight_field_name('commerce_product', $product);
      if (!empty($field_name) && !empty($product->{$field_name})) {

        // Extract the weight value from the product.
        $product_wrapper = entity_metadata_wrapper('commerce_product', $product);
        $weight = $product_wrapper->{$field_name}
          ->value();

        // Multiply the weight value by the quantity of the line item.
        $weight['weight'] *= $line_item->quantity;
      }
    }
  }

  // Allow other modules to alter the weight if necessary.
  drupal_alter('commerce_physical_product_line_item_weight', $weight, $line_item);
  return $weight;
}

/**
 * Determines the dimensions of a product line item on an order.
 *
 * @param commerce_line_item $line_item
 *   A product line item whose dimensions should be determined.
 *
 * @return array
 *   An array of dimensions field value arrays. There'll be one entry in the
 *   array per product, with the entry being an array of that product's
 *   dimensions. If this line item contains no products with dimensions, an
 *   empty array will be returned.
 */
function commerce_physical_product_line_item_dimensions($line_item) {
  $line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
  $dimensions = array();

  // If the line item references a valid product...
  if (!empty($line_item_wrapper->commerce_product)) {
    $product = $line_item_wrapper->commerce_product
      ->value();
    if (!empty($product)) {

      // If the product has a valid dimensions field...
      $field_name = commerce_physical_entity_dimensions_field_name('commerce_product', $product);
      if (!empty($field_name) && !empty($product->{$field_name})) {
        $product_wrapper = entity_metadata_wrapper('commerce_product', $product);

        // Add dimension values per product in the line item.
        for ($i = 0; $i < $line_item_wrapper->quantity
          ->value(); $i++) {
          $dimensions[] = $product_wrapper->{$field_name}
            ->value();
        }
      }
    }
  }

  // Allow other modules to alter the weight if necessary.
  drupal_alter('commerce_physical_product_line_item_dimensions', $dimensions, $line_item);
  return $dimensions;
}

/**
 * Determines the weight of an entire order.
 *
 * @param commerce_order $order
 *   The order object whose weight value should be calculated.
 * @param string $unit
 *   The unit of measurement to use for the returned weight of the order.
 *
 * @return array
 *   A weight field value array representing the total weight of the order using
 *   the specified unit of measurement or NULL if no weight could be determined.
 */
function commerce_physical_order_weight($order, $unit = 'lb') {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  $weight = NULL;

  // Loop over each line item on the order.
  foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {

    // Get the weight value of product line items.
    if (in_array($line_item_wrapper
      ->getBundle(), commerce_product_line_item_types())) {
      $line_item_weight = commerce_physical_product_line_item_weight($line_item_wrapper
        ->value());

      // Add it to the running total converting it to the required weight unit.
      if (!empty($line_item_weight['weight'])) {
        $converted_weight = physical_weight_convert($line_item_weight, $unit);
        if (empty($weight['weight'])) {
          $weight = $converted_weight;
        }
        else {
          $weight['weight'] += $converted_weight['weight'];
        }
      }
    }
  }

  // Allow other modules to alter the weight if necessary.
  drupal_alter('commerce_physical_order_weight', $weight, $order, $unit);
  return $weight;
}

/**
 * Determines the volume of an entire order.
 *
 * @param commerce_order $order
 *   The order object whose volume should be calculated.
 * @param string $unit
 *   The unit of measurement to convert dimensions to before calculating the
 *   volume of the order in the related cubic unit.
 *
 * @return array
 *   A volume value array with keys representing the total 'volume' of the order
 *   in the 'unit' specified or NULL if no volume could be determined.
 */
function commerce_physical_order_volume($order, $unit = 'in') {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  $volume = NULL;

  // Loop over each line item on the order.
  foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {

    // Get the dimensions value of product line items.
    if (in_array($line_item_wrapper->type
      ->value(), commerce_product_line_item_types())) {
      $line_item_dimensions = commerce_physical_product_line_item_dimensions($line_item_wrapper
        ->value());

      // Each product in a line item has the same dimensions, so we index into
      // only the first product.
      $line_item_dimensions = reset($line_item_dimensions);

      // Add it to the running total converting it to the required weight unit.
      if (!physical_field_is_empty($line_item_dimensions, array(
        'type' => 'physical_dimensions',
      ))) {
        $converted_dimensions = physical_dimensions_convert($line_item_dimensions, $unit);
        $converted_dimensions['volume'] = $converted_dimensions['width'] * $converted_dimensions['length'] * $converted_dimensions['height'] * $line_item_wrapper->quantity
          ->value();
        if (empty($volume['volume'])) {

          // Create a volume value array using the converted unit type.
          $volume = array(
            'volume' => $converted_dimensions['volume'],
            'unit' => $unit,
          );
        }
        else {
          $volume['volume'] += $converted_dimensions['volume'];
        }
      }
    }
  }

  // Allow other modules to alter the volume if necessary.
  drupal_alter('commerce_physical_order_volume', $volume, $order, $unit);
  return $volume;
}

/**
 * Determines the dimensions of each product in an entire order.
 *
 * Other code can then use this data to figure out things like what the maximum
 * dimensions of any product in the order is, or what size shipping container
 * everything will fit into.
 *
 * @param commerce_order $order
 *   The order object whose dimensions should be returned.
 * @param string $unit
 *   The unit of measurement to use for the returned dimensions.
 *
 * @return array
 *   An array of dimension arrays. One per product in the order.
 *   weight field value array representing the total weight of the order using
 *   the specified unit of measurement or NULL if no weight could be determined.
 */
function commerce_physical_order_dimensions($order, $unit = 'cm') {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  $order_dimensions = array();

  // Loop over each line item on the order.
  foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {

    // Get the weight value of product line items.
    if (in_array($line_item_wrapper->type
      ->value(), commerce_product_line_item_types())) {
      $line_item_dimensions = commerce_physical_product_line_item_dimensions($line_item_wrapper
        ->value());
      $order_dimensions = array_merge($order_dimensions, $line_item_dimensions);
    }
  }

  // Now ensure that all dimensions supplied are in the requested units.
  foreach ($order_dimensions as $key => $dimensions) {
    $order_dimensions[$key] = physical_dimensions_convert($dimensions, $unit);
  }

  // Allow other modules to alter the weight if necessary.
  drupal_alter('commerce_physical_order_dimensions', $order_dimensions, $order, $unit);
  return $order_dimensions;
}

/**
 * Determines whether or not a line item should be considered shippable.
 *
 * @param commerce_line_item $line_item
 *   The line item object whose shippability is being determined.
 *
 * @return bool
 *   Boolean indicating whether or not the given line item represents something
 *   shippable; defaults to FALSE unless the line item represents a product line
 *   item with a discernible weight.
 */
function commerce_physical_line_item_shippable($line_item) {
  $shippable = FALSE;

  // If this is a product line item...
  if (in_array($line_item->type, commerce_product_line_item_types())) {

    // Mark this line item as shippable if we can determine its weight.
    $weight = commerce_physical_product_line_item_weight($line_item);
    if (!empty($weight)) {
      $shippable = TRUE;
    }
  }

  // Allow other modules to alter the shippability of the line item.
  drupal_alter('commerce_physical_line_item_shippable', $shippable, $line_item);
  return $shippable;
}

/**
 * Determines whether or not an order should be considered shippable.
 *
 * @param commerce_order $order
 *   The order object whose shippability should be determined.
 *
 * @return bool
 *   Boolean indicating whether or not the given order is shippable; defaults to
 *   FALSE unless any line item on the order is determined to be shippable.
 */
function commerce_physical_order_shippable($order) {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  $shippable = FALSE;

  // Loop over all the line items on the order.
  foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {

    // Mark the order as shippable if the current line item is determined to be
    // shippable.
    if (commerce_physical_line_item_shippable($line_item_wrapper
      ->value())) {
      $shippable = TRUE;
    }
  }

  // Allow other modules to alter the shippability of the line item.
  drupal_alter('commerce_physical_order_shippable', $shippable, $order);
  return $shippable;
}

/**
 * Name of the shipping customer profile reference field for the given order.
 *
 * @param commerce_order $order
 *   The order whose shipping customer profile reference field name should be
 *   determined.
 *
 * @return string
 *   The name of the field to use on the order to find shipping information or
 *   NULL if none was found; defaults to commerce_customer_shipping if available
 *   or another customer profile reference if not (preferring profiles other
 *   than the default billing profile if available).
 */
function commerce_physical_order_shipping_field_name($order) {
  $field_name = NULL;
  if (!empty($order->commerce_customer_shipping)) {
    $field_name = 'commerce_customer_shipping';
  }
  else {

    // Look for customer profile references fields available for the order.
    foreach (field_info_instances('commerce_order', $order->type) as $instance_name => $instance) {

      // Load the field info for the current instance.
      $field = field_info_field($instance['field_name']);

      // If it's of the proper type...
      if ($field['type'] == 'commerce_customer_profile_reference') {

        // Save its name and exit the loop if it isn't the billing profile.
        $field_name = $instance_name;
        if ($field_name != 'commerce_customer_billing') {
          break;
        }
      }
    }
  }

  // Allow other modules to specify a different field name.
  drupal_alter('commerce_physical_order_shipping_field_name', $field_name, $order);
  return $field_name;
}

/**
 * Determines the name of the phone number field of a customer profile.
 *
 * @param commerce_cutomer_profile $profile
 *   The customer profile whose phone number field name should be determined.
 *
 * @return string
 *   The name of the field to use on the customer profile to find a phone number
 *   or NULL if none was found. Defaults to field_phone or field_phone_number if
 *   available; otherwise it's up to you to alter it in a custom module since we
 *   don't want to depend on an actual phone number field.
 */
function commerce_physical_customer_profile_phone_number_field_name($profile) {
  if (!empty($profile->field_phone)) {
    $field_name = 'field_phone';
  }
  elseif (!empty($profile->field_phone_number)) {
    $field_name = 'field_phone_number';
  }
  else {
    $field_name = '';
  }

  // Allow other modules to specify a different field name.
  drupal_alter('commerce_physical_customer_profile_phone_number_field_name', $field_name, $profile);
  return $field_name;
}

/**
 * Whether or not a shipping customer profile is a residential address.
 *
 * @param commerce_customer_profile $profile
 *   The customer profile whose residential status should be determined.
 *
 * @return bool
 *   Boolean indicating whether or not the given profile has a residential
 *   address; defaults to TRUE unless otherwise determined by a custom module.
 */
function commerce_physical_customer_profile_residential($profile) {
  $residential = TRUE;

  // Allow other modules to determine if the profile has a residential address.
  drupal_alter('commerce_physical_customer_profile_residential', $residential, $profile);
  return $residential;
}

Functions

Namesort descending Description
commerce_physical_customer_profile_phone_number_field_name Determines the name of the phone number field of a customer profile.
commerce_physical_customer_profile_residential Whether or not a shipping customer profile is a residential address.
commerce_physical_entity_dimensions_field_name Determines the dimensions field to use for a given entity.
commerce_physical_entity_field_name Determines the field of a certain type to use for a given entity.
commerce_physical_entity_weight_field_name Determines the weight field to use for a given entity.
commerce_physical_line_item_shippable Determines whether or not a line item should be considered shippable.
commerce_physical_order_dimensions Determines the dimensions of each product in an entire order.
commerce_physical_order_shippable Determines whether or not an order should be considered shippable.
commerce_physical_order_shipping_field_name Name of the shipping customer profile reference field for the given order.
commerce_physical_order_volume Determines the volume of an entire order.
commerce_physical_order_weight Determines the weight of an entire order.
commerce_physical_product_line_item_dimensions Determines the dimensions of a product line item on an order.
commerce_physical_product_line_item_weight Determines the weight to use for a product line item on an order.