Drupal feeds module: FeedsProcessor with ubercart Order

使用者情境:批量使用 CSV 匯入 ubercart 訂單

Feeds 模組已經有一個 CSV 格式讀取 parser,又有批量 batch 支持,能處理大檔案 CSV ,Feeds 的代碼也有很多使用的經驗,而且最重要的是,模組自己已經有四種內容可以匯入:(nodes, users, terms, feednode?),有更高的機會支持其他內容 (ubercart orders)

先從 “The developer's guide to Feeds” 開始研究:(https://www.drupal.org/node/622700)
有三種 plugin: fetcher,parser 和 processor

  • Fetcher 是「獲得匯入源的方法」feeds module 已經有一個檔案上載的 fetcher
  • Parser 是「如何分析,讀取匯入源」feeds module 已經有一個 CSV parser
  • Processer 的工作是將處理過的資料儲存成為 Drupal 的內容

建立一個新模組 ubercart_order_feeds.module:

<?php
/**
 * Implementation of hook_feeds_plugins().
 * http://drupalcontrib.org/api/drupal/contributions%21feeds%21feeds.api.php/function/hook_feeds_plugins/6
 */
function ubercart_order_feeds_feeds_plugins() {
 
$path = drupal_get_path('module', 'ubercart_order_feeds');
 
$info = array();
 
$info['FeedsOrderProcessor'] = array(
   
'name' => 'Order processor',
   
'description' => 'Create orders.',
   
'help' => 'Create orders from parsed content.',
   
'handler' => array(
     
'parent' => 'FeedsProcessor',
     
'class' => 'FeedsOrderProcessor',
     
'file' => 'FeedsOrderProcessor.inc',
     
'path' => $path,
    ),
  );
  return
$info;
}
?>

建立一個新檔案 FeedsOrderProcessor.inc:
<?php
class FeedsOrderProcessor extends FeedsProcessor {
}
?>

首先在 getMappingTargets() 定義 CSV 中會用到的欄位:

<?php
 
/**
   * Return available mapping targets.
   */
 
public function getMappingTargets() {
   
$targets = array(
     
'sku' => array(
       
'name' => t('Product SKU'),
       
'description' => t('SKU of the product.'),
      ),
     
'quantity' => array(
       
'name' => t('Quantity'),
       
'description' => t(''),
      ),
     
'cost' => array(
       
'name' => t('Cost'),
       
'description' => t(''),
      ),
     
'delivery_first_name' => array(
       
'name' => t('Delivery: First name'),
       
'description' => t(''),
      ),
     
'delivery_last_name' => array(
       
'name' => t('Delivery: Last name'),
       
'description' => t(''),
      ),
   
//....
?>

在 CSV 內的資料處理過之後,資料的值會根據 Array 的 key 放到 object 之內.

我輸出了一個原生的 order object,找出原本就有的預設欄名,定義 mappings 的時候重用它們,以省卻一點點麻煩 (i.e. delivery_first_name, delivery_last_name above and more)

process() 是主要的程式進入點,複製一點 NodeProcessor 的代碼:

<?php
 
public function process(FeedsImportBatch $batch, FeedsSource $source) {
   
// Count number of created and updated nodes.
   
$created  = $updated = $failed = 0;

    while (
$item = $batch->shiftItem()) {
     
// Map item to a term.
     
$order = $this->map($batch);
     
//.......
?>

來到這一點出現一個新的函數 map() 來返回一個全新的 ubercart order object:

<?php
 
/**
   * Execute mapping on an item.
   */
 
protected function map(FeedsImportBatch $batch, $target_order = NULL) {
   
// Prepare user account object.
   
if (empty($target_order)) {
     
$order = uc_order_new($user->uid, 'completed');
    }

   
// Have parent class do the iterating.
   
return parent::map($batch, $order);
  }
?>

parent::map() 函數會將 CSV 內的資料處理過之後,根據 mapping array 的 key 放到 object

程式化的建立 order

process() 的下一步是將 product 加到 order,並儲存之

使用 vid 載入一個 product: <?php uc_product_load() ?>
http://drupalcontrib.org/api/drupal/contributions!ubercart!uc_product!uc...

加 product 到 order:
<?php $order->products[] = $product; ?>

再填入一些 order 的基本 mail 和 payment method 之後,儲存,完成 sale:

<?php
uc_order_save
($order);
uc_cart_complete_sale($order, TRUE);
?>

你亦可以加入 payment method
<?php uc_payment_enter($order->order_id, 'bank_transfer', 0, 0, NULL, t('Checkout completed for a free order.')); ?>

Github 源碼 repo: https://github.com/joetsuihk/ubercart_order_feeds

One more thing

Processor 可以有自己的設定頁面
在我的情境,因為客戶會將同一個模組放到不同的網站,而他們有不同的 webform node ID
所以我便將 ID 放到設定頁面令客戶可以更方便的修改設定了

<?php
 
/**
   * Override parent::configDefaults().
   */
 
public function configDefaults() {
    return array(
     
'card_form_id' => '',
     
'mappings' => array(),
    );
  }

  public function
configForm(&$form_state) {
   
$form = array();
   
$form['card_form_id'] = array(
     
'#type' => 'textfield',
     
'#title' => t('Card webform node ID'),
     
'#default_value' => $this->config['card_form_id'],
    );
    return
$form;
  }
?>
Google