這一章節裡, 我們來了解一下如何在 Magento 中建立自定義的送貨/配送方式(Shipping Method)
建立自定義配送方式并不是很複雜, 不過需要了解在 shipping method 類中所有有用的方法,然後加以利用, 那就變得十分簡單了, 我會在這一節中示例不同 cases 情況下所使用的方法
現在我們就先來建立一個配送方式, 在接下去的示例中, 我将使用子產品 Excellence_Ship。 你可以建立你自己的子產品, 注意修改類名就可以了, 同樣我先附上源碼:
Shipping Method.zip (8.08KB)
當建立完成後, 配送方式主要會在如下兩個地方被顯示:
背景 - Admin Panel
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicGcq5SMw0CZvhGdl1WLn5WawBXaoN3LcNWawRnblRnbvN2Lcd2bsJ2LcNXZnFWbp9CXjlGbiVHcvwVbvNmLhVHan5WZopnb1NnL3d3dvw1LcpDc0RHaiojIsJye.jpg)
前台 - Frontend
為了實作這些, 我們需要對如下三個檔案進行修改:
config.xml | system.xml | Shipping Module
步驟一
在我們建立的時候, 需要為我們的配送方式決定一個辨別, 在示例中, 我使用唯一辨別就是 "excellence", 接下來建立 system.xml, 這樣就可以在背景 configuration 中進行配置
<?xmlversion="1.0"encoding="UTF-8"?>
<config>
<sections>
<carriers>
<groups>
<excellencetranslate="label"module="ship">
<label>Excellence Shipping Module</label>
<frontend_type>text</frontend_type>
<sort_order>99</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
<fields>
<activetranslate="label">
<label>Enabled</label>
<frontend_type>select</frontend_type>
<source_model>adminhtml/system_config_source_yesno</source_model>
<sort_order>1</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</active>
<titletranslate="label">
<label>Title</label>
<frontend_type>text</frontend_type>
<sort_order>2</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</title>
<nametranslate="label">
<label>Method Name</label>
<frontend_type>text</frontend_type>
<sort_order>2</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</name>
<pricetranslate="label">
<label>Price</label>
<frontend_type>text</frontend_type>
<sort_order>3</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</price>
<specificerrmsgtranslate="label">
<label>Displayed Error Message</label>
<frontend_type>textarea</frontend_type>
<sort_order>4</sort_order>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>1</show_in_store>
</specificerrmsg>
<sallowspecifictranslate="label">
<label>Ship to Applicable Countries</label>
<frontend_type>select</frontend_type>
<sort_order>90</sort_order>
<frontend_class>shipping-applicable-country</frontend_class>
<source_model>adminhtml/system_config_source_shipping_allspecificcountries</source_model>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
</sallowspecific>
<specificcountrytranslate="label">
<label>Ship to Specific Countries</label>
<frontend_type>multiselect</frontend_type>
<sort_order>91</sort_order>
<source_model>adminhtml/system_config_source_country</source_model>
<show_in_default>1</show_in_default>
<show_in_website>1</show_in_website>
<show_in_store>0</show_in_store>
<can_be_empty>1</can_be_empty>
</specificcountry>
</fields>
</excellence>
</groups>
</carriers>
</sections>
</config>
這裡需要說明一下的就是, 我們需要将配送方式代碼寫在如下标簽中, 和建立支付方式類似:
<sections>
<carriers>
<groups>
從這裡你可以看出在 'excellence' 标簽中的就是我們配送方式的代碼, 這些代碼就是用來建立該配送方式是否啟用, title, name, 和 price, 當然這些都是每一個配送方式所必須的
這裡我們也同時建立了2個額外的标簽, sallowspecific 和 specificcountry, 其實這也是需要, 因為有可能該配送方式隻對某一些國家開放, 比如說有一個使用者選擇配送到冰島, 然而我們的背景配置中并沒有包括該國家, 為了達到限制, 我們隻需要在我們的 xml 中寫入這兩個标簽,并在背景加以配置就可以了, 不需要做其他額外的工作
還有一個就是 specificerrmsg 标簽 - 錯誤資訊, 當配送方式産生錯誤的時候, 這個标簽裡的内容就能顯示給使用者
步驟二
在 config.xml 中的 <config> 标簽裡寫入如下代碼:
<default>
<carriers>
<excellence>
<active>1</active>
<model>ship/carrier_excellence</model>
<title>Carrier Title</title>
<name>Method Name</name>
<price>5.00</price>
<specificerrmsg>This shipping method is currently unavailable. If you would like to ship using this shipping method, please contact us.</specificerrmsg>
</excellence>
</carriers>
</default>
這裡的 <default> 标簽裡的内容就是用來在背景 configuration 中為剛建立的标簽設定預設值的, 可以看出基本和剛剛由 system.xml 所建立的一一對應, 唯一需要注意的就是 <model> 标簽, 它包含了該配送方式的 model 路徑, 這個十分重要, 否則該配送方式不會在前端頁面顯示
現在你可以到 Admin -> System -> Configuration -> Shipping Methods 中看到你剛建立的标簽以及初始化的預設值
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicGcq5SMw0CZvhGdl1WLn5WawBXaoN3LcNWawRnblRnbvN2Lcd2bsJ2LcNXZnFWbp9CXjlGbiVHcvwVbvNmLhVHan5WZopnb1NnL3d3dvw1LcpDc0RHaiojIsJye.jpg)
步驟三
現在我們就要來建立我們配送方式的 model 類檔案了, 對應和之前 <model> 标簽中所申明的
Excellence_Ship_Model_Carrier_Excellence
<?php
classExcellence_Ship_Model_Carrier_Excellence
extendsMage_Shipping_Model_Carrier_Abstract
implementsMage_Shipping_Model_Carrier_Interface
{
protected$_code = 'excellence';
publicfunction collectRates(Mage_Shipping_Model_Rate_Request $request)
{
if(!Mage::getStoreConfig('carriers/'.$this->_code.'/active')) {
returnfalse;
}
$handling= Mage::getStoreConfig('carriers/'.$this->_code.'/handling');
$result = Mage::getModel('shipping/rate_result');
$show = true;
if($show) { // 這裡這個 if 僅僅是為了示範如何在 shipping methods 中傳回成功和錯誤的
$method= Mage::getModel('shipping/rate_result_method');
$method->setCarrier($this->_code);
$method->setMethod($this->_code);
$method->setCarrierTitle($this->getConfigData('title'));
$method->setMethodTitle($this->getConfigData('name'));
$method->setPrice($this->getConfigData('price'));
$method->setCost($this->getConfigData('price'));
$result->append($method);
}else{
$error= Mage::getModel('shipping/rate_result_error');
$error->setCarrier($this->_code);
$error->setCarrierTitle($this->getConfigData('name'));
$error->setErrorMessage($this->getConfigData('specificerrmsg'));
$result->append($error);
}
return$result;
}
publicfunction getAllowedMethods()
{
returnarray('excellence'=>$this->getConfigData('name'));
}
}
如上的代碼還是比較容易了解的, 不過還是來逐行了解下:
publicfunction collectRates(Mage_Shipping_Model_Rate_Request $request)
這個方法需要被執行, 正确來說會被 Magento 自身調用, 用來查找出 shipping rates
</pre><pre name="code" class="php">if(!Mage::getStoreConfig('carriers/'.$this->_code.'/active')) {
returnfalse;
}
這個 if 隻是來檢查該配送方式在背景配置中是否被開啟
$result= Mage::getModel('shipping/rate_result');
這裡我們建立了 result 對象, 它總是在配送方式中的 collectRate 方法裡被傳回出來
$method= Mage::getModel('shipping/rate_result_method');
$method->setCarrier($this->_code);
$method->setMethod($this->_code);
$method->setCarrierTitle($this->getConfigData('title'));
$method->setMethodTitle($this->getConfigData('name'));
$method->setPrice($this->getConfigData('price'));
$method->setCost($this->getConfigData('price'));
$result->append($method);
這些就是用來傳回出運費價格(shipping price)的
$error= Mage::getModel('shipping/rate_result_error');
$error->setCarrier($this->_code);
$error->setCarrierTitle($this->getConfigData('name'));
$error->setErrorMessage($this->getConfigData('specificerrmsg'));
$result->append($error);
很明顯,這些就是用來傳回錯誤資訊的
Model 類檔案建立完畢後, 該配送方式應該就可以工作了, 你也可以在 checkout 和 cart 頁面中看到
不同 cases 情況下所使用的方法
如下我所寫的内容隻适用在 collectRates 方法中, 下面我會分别列出不同的情況, 你可以根據自己的需求加以利用, 不過價格最後需要被放在 $result 變量中, 和上面示例中一樣
運費價格基于目的地國家,州和郵政編碼
//Case1: 運費價格基于目的地國家,州和郵政編碼
echo$destCountry = $request->getDestCountryId().': Dest Country<br>';
echo$destRegion = $request->getDestRegionId().': Dest Region<br>';
echo$destRegionCode = $request->getDestRegionCode().': Dest Region Code<br>';
print_r($destStreet= $request->getDestStreet());echo': Dest Street<br>';
echo$destCity = $request->getDestCity().': Dest City<br>';
echo$destPostcode = $request->getDestPostcode().': Dest Postcode<br>';
echo$country_id = $request->getCountryId().': Package Source Country ID<br>';
echo$region_id = $request->getRegionId().': Package Source Region ID<br>';
echo$city = $request->getCity().': Package Source City<br>';
echo$postcode = $request->getPostcode().': Package Source Post Code<br>';
可以根據自己業務邏輯的需求添加 if 條件來得出最後的價格
運費價格基于訂單的價格或重量
//Case2: 運費價格基于訂單的價格或重量
echo$packageValue = $request->getPackageValue().': Dest Package Value<br>';
echo$packageValueDiscout = $request->getPackageValueWithDiscount().': Dest Package Value After Discount<br>';
echo$packageWeight = $request->getPackageWeight().': Package Weight<br>';
echo$packageQty = $request->getPackageQty().': Package Quantity <br>';
echo$packageCurrency = $request->getPackageCurrency().': Package Currency <br>';
運費價格基于尺寸(Dimension)
//Case3: 運費價格基于尺寸
echo$packageheight = $request->getPackageHeight() .': Package height <br>';
echo$request->getPackageWeight().': Package Width <br>';
echo$request->getPackageDepth().': Package Depth <br>';
運費價格基于産品屬性
假如你想讓每個産品都有自己不同的運費價格, 為此, 我們需要建立一個産品屬性叫: 'shipping_price’', 在每一個産品編輯頁面填寫該産品自己的運費價格, 這樣我們就可以在配送方法中通過這個 'shipping_price' 來計算出總運費價格
//Case4: 運費價格基于産品屬性
if($request->getAllItems()) {
foreach($request->getAllItems()as$item) {
if($item->getProduct()->isVirtual() || $item->getParentItem()) {
continue;
}
if($item->getHasChildren() && $item->isShipSeparately()) {
foreach($item->getChildren()as$child) {
if($child->getFreeShipping() && !$child->getProduct()->isVirtual()) {
$product_id= $child->getProductId();
$productObj= Mage::getModel('catalog/product')->load($product_id);
$ship_price= $productObj->getData('shipping_price');//擷取産品運費屬性
$price+= (float)$ship_price;
}
}
}else{
$product_id= $item->getProductId();
$productObj= Mage::getModel('catalog/product')->load($product_id);
$ship_price= $productObj->getData('shipping_price');//擷取産品運費屬性
$price+= (float)$ship_price;
}
}
}
運費價格基于産品屬性選項
假說說網站上有個産品含有多個尺寸, 在頁面中提供下拉框來供使用者選擇, 小号(small)運費為 15, 中号(medium)運費為: 20, 大号(large)運費為: 25
//Case5: 運費價格基于産品屬性選項
if($request->getAllItems()) {
foreach($request->getAllItems()as$item) {
if($item->getProduct()->isVirtual() || $item->getParentItem()) {
continue;
}
if($item->getHasChildren() && $item->isShipSeparately()) {
foreach($item->getChildren()as$child) {
if($child->getFreeShipping() && !$child->getProduct()->isVirtual()) {
$product_id= $child->getProductId();
$value= $item->getOptionByCode('info_buyRequest')->getValue();
$params= unserialize($value);
$attributeObj= Mage::getModel('eav/config')->getAttribute(Mage_Catalog_Model_Product::ENTITY,'shirt_size');// our configurable attribute
$attribute_id= $attributeObj->getAttributeId();
$attribute_selected= $params['super_attribute'][$attribute_id];
$label= '';
foreach($attributeObj->getSource()->getAllOptions(false)as$option){
if($option['value'] == $attribute_selected){
$label= $option['label'];
}
}
if($label= 'Small'){
$price+= 15;
}elseif($label= 'Medium'){
$price+= 20;
}elseif($label= 'Large'){
$price+= 22;
}
}
}
}else{
$product_id= $item->getProductId();
$value= $item->getOptionByCode('info_buyRequest')->getValue();
$params= unserialize($value);
$attributeObj= Mage::getModel('eav/config')->getAttribute(Mage_Catalog_Model_Product::ENTITY,'shirt_size');// our configurable attribute
$attribute_id= $attributeObj->getAttributeId();
$attribute_selected= $params['super_attribute'][$attribute_id];
$label= '';
foreach($attributeObj->getSource()->getAllOptions(false)as$option){
if($option['value'] == $attribute_selected){
$label= $option['label'];
}
}
if($label= 'Small'){
$price+= 15;
}elseif($label= 'Medium'){
$price+= 20;
}elseif($label= 'Large'){
$price+= 22;
}
}
}
}
運費價格基于産品自定義選項
假如說網站上有某些産品, 擁有下拉框選項: 快遞(Express) 和 郵局普遞(Normal), 當使用者選擇快遞(Express)時, 運費價格就是 50, 而郵局普遞(Normal)價格則是 10
//運費價格基于産品自定義選項
if($request->getAllItems()) {
foreach($request->getAllItems()as$item) {
if($item->getProduct()->isVirtual() || $item->getParentItem()) {
continue;
}
if($item->getHasChildren() && $item->isShipSeparately()) {
foreach($item->getChildren()as$child) {
if($child->getFreeShipping() && !$child->getProduct()->isVirtual()) {
$product_id= $child->getProductId();
$value= $item->getOptionByCode('info_buyRequest')->getValue();
$params= unserialize($value);
$options_select= $params['options'];
$product= Mage::getModel('catalog/product')->load($product_id);
$options= $product->getOptions();
foreach($optionsas $option) {
if($option->getGroupByType() == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) {
$option_id= $option->getId();
foreach($option->getValues()as$value) {
if($value->getId() == $options_select[$option_id]){
if($value->getTitle() == 'Express'){
$price+= 50;
}elseif($value->getTitle() == 'Normal'){
$price+= 10;
}
}
}
}
}
}
}
}else{
$product_id= $item->getProductId();
$value= $item->getOptionByCode('info_buyRequest')->getValue();
$params= unserialize($value);
$options_select= $params['options'];
$product= Mage::getModel('catalog/product')->load($product_id);
$options= $product->getOptions();
foreach($optionsas $option) {
if($option->getGroupByType() == Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) {
$option_id= $option->getId();
foreach($option->getValues()as$value) {
if($value->getId() == $options_select[$option_id]){
if($value->getTitle() == 'Express'){
$price+= 50;
}elseif($value->getTitle() == 'Normal'){
$price+= 10;
}
}
}
}
}
}
}
}
這些是我能想到所有的關于運費的, 如有更好的建議和代碼片段,歡迎留言提出