修改Magento默认Export Customers功能

目前存在的不足

Magento 1.x的Export功能可以很方便地对Customers的数据进行导出,但是存在几个不足(或者说不方便)的地方:

  1. 默认导出的 .CSV文件是以UTF-8格式编码的,而MS Excle是无法识别UTF-8编码的,所以导出的 .CSV文件直接在Excle中打开时中文会乱码,乱码不仅会导致中文单元格无法阅读, 而且会影响其它单元格,会使其它单元格错位

  2. 缺少subscriber_status的信息。subscriber_status是用来标识一个Customer是否愿意接收订阅消息(Newsletter),这个信息经常会用到,但很可惜原生Magento Export无法导出subscriber_status信息,因为subscriber_status信息仅仅是Newsletter表的一个Field,不是Customer的attribute。

解决方案

  1. 第1条是可以通过文件转码的方式解决的,不需要修改程序。具体做法是用txt打开下载好的.CVS文件,然后选择“另存为”,在“另存为”对话框的下方,会有选择编码格式的下拉菜单,将编码格式从UTF-8改为ANSI,然后保存即可解决乱码问题。

save as

  1. 第2条是需要修改程序才能解决的。首先来看一下Magento的Default Export是如何实现的:

在后台的Export页面,通过Form的action(index.php/admin/export/export/key/……./)可以知道,Export请求是在一个名叫Export的Controller中的exportAction中完成的。

通过在IDE中搜索ExportController.php这个文件名可以很快定位代码位置,主要的工作是在以下这两个代码片段中完成的:

忽略上下不相关的代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/**
* Load data with filter applying and create file for download.
* @return Mage_ImportExport_Adminhtml_ExportController
*/
public function exportAction()
{
    // ......
    /** @var $model Mage_ImportExport_Model_Export */
    $model = Mage::getModel('importexport/export');
    $model->setData($this->getRequest()->getParams());
    return $this->_prepareDownloadResponse(
        $model->getFileName(),
        $model->export(),
        $model->getContentType()
    );
    // ......
}

$this->_prepareDownloadResponse($fileName, $content, $contentType = ‘application/octet-stream’, $contentLength = null)方法是Mage_Core_Controller_Varien_Action 类提供的封装好的方法,它根据参数中提供的文件名、文件内容、文件类型生成一个文件并发送到浏览器,这是一个公用的方法,我们只需要修改传给它的参数就可以修改Magento导出用户(Export customers)功能。

我现在要给导出的.CSV文件添加subscriber_status项,也就是修改传给_prepareDownloadResponse()方法的第2个参数$model->export():

具体思路:首先修改$collection,让$collection包含customers的subscriber_status数据

1
2
3
4
5
$collection->getSelect()->joinLeft(
    array('ns' => 'newsletter_subscriber'),
    'ns.customer_id = e.entity_id',
    'ns.subscriber_status'
);

然后在$row数组中添加subscriber_status数据:

1
$row['subscribe_status'] = $this->getSubscriberLabel($item);

其中, $this->getSubscriberLabel($item) 是新增的用来将subscriber_status转为容易阅读的label的方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
protected function getSubscriberLabel($item)
{
    $subscriberStatus = (int)$item->getSubscriberStatus();
    if(!empty($subscriberStatus)){
        switch($subscriberStatus){
            case 1: return 'Subscribed';
            case 2: return 'Not_Active';
            case 3: return 'Unsubscribed';
            case 4: return 'Unconfirmed';
            default: return null;
        }
    }
    return null;
}

别忘记添加表头:

1
2
3
4
5
6
7
// create export file
$writer->setHeaderCols(array_merge(
    array('subscribe_status'),
    $this->_permanentAttributes, $validAttrCodes,
    array('password'), $addrColNames,
    array_keys($defaultAddrMap)
));

最终修改后的export()方法(修改过或者新增的代码用红色标出):

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
/**
 * Export process.
 *
 * @return string
 */
public function export()
{
    $collection = $this->_prepareEntityCollection(Mage::getResourceModel('customer/customer_collection'));
    $validAttrCodes = $this->_getExportAttrCodes();
    $writer = $this->getWriter();
    $defaultAddrMap = Mage_ImportExport_Model_Import_Entity_Customer_Address::getDefaultAddressAttrMapping();

    // prepare address data
    $addrAttributes = array();
    $addrColNames = array();
    $customerAddrs = array();

    foreach (Mage::getResourceModel('customer/address_attribute_collection')
                 ->addSystemHiddenFilter()
                 ->addExcludeHiddenFrontendFilter() as $attribute) {
        $options = array();
        $attrCode = $attribute->getAttributeCode();

        if ($attribute->usesSource() && 'country_id' != $attrCode) {
            foreach ($attribute->getSource()->getAllOptions(false) as $option) {
                foreach (is_array($option['value']) ? $option['value'] : array($option) as $innerOption) {
                    if (strlen($innerOption['value'])) { // skip ' -- Please Select -- ' option
                        $options[$innerOption['value']] = $innerOption['label'];
                    }
                }
            }
        }
        $addrAttributes[$attrCode] = $options;
        $addrColNames[] = Mage_ImportExport_Model_Import_Entity_Customer_Address::getColNameForAttrCode($attrCode);
    }
    foreach (Mage::getResourceModel('customer/address_collection')->addAttributeToSelect('*') as $address) {
        $addrRow = array();

        foreach ($addrAttributes as $attrCode => $attrValues) {
            if (null !== $address->getData($attrCode)) {
                $value = $address->getData($attrCode);

                if ($attrValues) {
                    $value = $attrValues[$value];
                }
                $column = Mage_ImportExport_Model_Import_Entity_Customer_Address::getColNameForAttrCode($attrCode);
                $addrRow[$column] = $value;
            }
        }
        $customerAddrs[$address['parent_id']][$address->getId()] = $addrRow;
    }

    // create export file
    $writer->setHeaderCols(array_merge(
        array('subscribe_status'),
        $this->_permanentAttributes, $validAttrCodes,
        array('password'), $addrColNames,
        array_keys($defaultAddrMap)
    ));

    $collection->getSelect()->joinLeft(
        array('ns' => 'newsletter_subscriber'),
        'ns.customer_id = e.entity_id',
        'ns.subscriber_status'
    );
    foreach ($collection as $itemId => $item) { // go through all customers
        $row = array();

    // go through all valid attribute codes
        foreach ($validAttrCodes as $attrCode) {
            $attrValue = $item->getData($attrCode);

            if (isset($this->_attributeValues[$attrCode])
                && isset($this->_attributeValues[$attrCode][$attrValue])
            ) {
                $attrValue = $this->_attributeValues[$attrCode][$attrValue];
            }
            if (null !== $attrValue) {
                $row[$attrCode] = $attrValue;
            }
        }
        $row[self::COL_WEBSITE] = $this->_websiteIdToCode[$item['website_id']];
        $row[self::COL_STORE] = $this->_storeIdToCode[$item['store_id']];
        $row['subscribe_status'] = $this->getSubscriberLabel($item);

        // addresses injection
        $defaultAddrs = array();

        foreach ($defaultAddrMap as $colName => $addrAttrCode) {
            if (!empty($item[$addrAttrCode])) {
                $defaultAddrs[$item[$addrAttrCode]][] = $colName;
            }
        }
        if (isset($customerAddrs[$itemId])) {
            while (($addrRow = each($customerAddrs[$itemId]))) {
                if (isset($defaultAddrs[$addrRow['key']])) {
                    foreach ($defaultAddrs[$addrRow['key']] as $colName) {
                        $row[$colName] = 1;
                    }
                }
                $writer->writeRow(array_merge($row, $addrRow['value']));

                $row = array();
            }
        } else {
            $writer->writeRow($row);
        }
    }
    return $writer->getContents();
}

建议不要直接修改Magento Default的代码,例如上面所做的修改,可以通过重写Magento Model的方法来实现。

Built with Hugo
主题 StackJimmy 设计