magento2 checkout增長address field

我要在checkout加一個留言輸入欄,能夠在後臺order detail查獲得的,magento官方wiki有如下教程正好知足個人需求
http://devdocs.magento.com/gu...
然而我仍是花了不少時間沒搞定,官方教程也許是錯的,也許是不完整。教程的實現方法沒法在後臺讀到數據,完整應該包括建立attribute,但教程裏沒有。後來不得不用本身的方法實現,個人實現原理是在checkout模板上加個input,用mixins把保存觸發事件加到shipping進入下一步的按鈕,觸發事件再調用本身開發的WEBAPI,把message保存到quote表的message字段,再用event把quote message搬到order message。javascript

step1 先給quote和sales_order表添加message字段

quote和sales_order不少字段對應,當你把產品加入到cart,會建立一個quote,cart的數據就會存到quote中,checkout到了正式支付後,order纔會被真正產生出來,其實數據庫裏的操做是數據從quote搬到了sales_order。因此添加字段,應該在quote與sales_order同時添加,數據纔會傳到最終的order。php

Setup/InstallSchema.php (忽略類的細節,只有關鍵代碼)html

$setup->startSetup();

$setup->getConnection()->addColumn(
    $setup->getTable('quote'),
    'message',
    [
        'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
        'length' => 2048,
        'nullable' => false,
        'default' => '',
        'comment' => 'checkout message'
    ]
);
$setup->getConnection()->addColumn(
    $setup->getTable('sales_order'),
    'message',
    [
        'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
        'length' => 2048,
        'nullable' => false,
        'default' => '',
        'comment' => 'checkout message'
    ]
);

$setup->endSetup();

step2 建立webapi,把message保存到quote表

建立WEBAPI教程
https://segmentfault.com/a/11...
值得注意的是interface中的註釋是必須的,並且參數聲明必須有類型,不然系統會把參數當成是類。這對於老手特別容易出錯。java

<route url="/V1/<module>/checkout" method="POST">
    <service class="<vendor>\<module>\Api\ManagementInterface" method="saveMessage"/>
    <resources>
        <resource ref="self" />
    </resources>
    <data>
        <parameter name="cartId" force="true">%cart_id%</parameter>
    </data>
</route>
protected $quoteRepository;

public function __construct(
    \Magento\Quote\Api\CartRepositoryInterface $quoteRepository
) {
    $this->quoteRepository = $quoteRepository;
}

public function saveMessage($cartId, $message) {
    $quote = $this->quoteRepository->get($cartId);
    $quote->setMessage($message);
    $this->quoteRepository->save($quote);
    return true;
}

step3 把input加到checkout

我選擇加到shipping這一步
app/design/frontend/<vendor>/<theme>/Magento_Checkout/web/template/shipping.htmljquery

<textarea id="checkout_comment"></textarea>

step4 mixins注入到shpping的下一步按鈕

requirejs-config.jsweb

var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/action/set-shipping-information': {
                '<Vendor_Module>/js/mixin/set-shipping-information': true
            }
        }
    }
};

js/mixin/set-shipping-information.js數據庫

/*jshint browser:true jquery:true*/
/*global alert*/
define([
    'jquery',
    'mage/utils/wrapper',
    'Magento_Checkout/js/model/quote',
    'mage/storage',
    'Magento_Checkout/js/model/url-builder'
], function ($, wrapper, quote, storage, urlBuilder) {
    'use strict';

    return function (setShippingInformationAction) {

        return wrapper.wrap(setShippingInformationAction, function (originalAction) {

            if($('#checkout_comment').length > 0 && $('#checkout_comment').val() != '') {
                storage.post(
                    urlBuilder.createUrl('/<module>/checkout', {}),
                    JSON.stringify({
                        comment: $('#checkout_comment').val()
                    })
                ).fail(
                    function (response) {
                        console.log(response);
                    }
                );
            }

            return originalAction();
        });
    };
});

js mixins是magento實現的js注入功能,讓js能實現像DI同樣的注入效率,不得不說是一個亮點。具體內容能夠參考:http://devdocs.magento.com/gu...segmentfault

step5 生成訂單時把quote數據般到sales_order

etc/events.xmlapi

<event name="sales_model_service_quote_submit_before">
    <observer name="checkout_message_sales_model_service_quote_submit_before" instance="<vendor>\<module>\Observer\SaveOrderBeforeSalesModelQuoteObserver" />
</event>

SaveOrderBeforeSalesModelQuoteObserver.phpapp

private $attributes = [
    'message'
];

public function execute(\Magento\Framework\Event\Observer $observer)
{
    /* @var \Magento\Sales\Model\Order $order */
    $order = $observer->getEvent()->getData('order');
    /* @var \Magento\Quote\Model\Quote $quote */
    $quote = $observer->getEvent()->getData('quote');

    foreach ($this->attributes as $attribute) {
        if ($quote->hasData($attribute)) {
            $order->setData($attribute, $quote->getData($attribute));
        }
    }

    return $this;
}

step6 讓message在後臺顯示

爲了修改後臺的模板,我直接修改了magento的默認模板了,這是不建議的作法,但複寫後臺默認模板是件比較麻煩的事,後臺並無設置給後臺換新模板
vendor/magento/module-sales/view/adminhtml/templates/order/view/info.phtml

<?php /* @escapeNotVerified */ echo $_order->getData('message'); ?>

另外一種更好的方法

以上方法須要建立新的webapi,能夠利用原有的webapi固然更省事,但須要深刻理解webapi的拓展機制,主要須要用到extension attributes
http://devdocs.magento.com/gu...
以在shipping addess追加屬性爲例,如下是完整過程。

Setup

$this->_quoteSetup = $this->_quoteSetupFactory->create( [ 'setup' => $setup ] );
$this->_salesSetup = $this->_salesSetupFactory->create( [ 'setup' => $setup ] );

$this->_quoteSetup->addAttribute( 'quote_address', 'delivery_date_1', [ 'type' => 'varchar' ] );
$this->_salesSetup->addAttribute( 'order_address', 'delivery_date_1', [ 'type' => 'varchar' ] );

$this->_quoteSetup->addAttribute( 'quote_address', 'delivery_date_2', [ 'type' => 'varchar' ] );
$this->_salesSetup->addAttribute( 'order_address', 'delivery_date_2', [ 'type' => 'varchar' ] );

聲明Extension類

etc/extension_attributes.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Quote\Api\Data\AddressInterface">
        <attribute code="delivery_date_1" type="string">
            <resources>
                <resource ref="Magento_Customer::customer" />
            </resources>
        </attribute>
        <attribute code="delivery_date_2" type="string">
            <resources>
                <resource ref="Magento_Customer::customer" />
            </resources>
        </attribute>
    </extension_attributes>
</config>

上邊這步的做用僅僅是爲系統自動生成的 extension 類(本例爲 MagentoQuoteApiDataAddressExtension,可在 var/generation/Magento/Quote/Api/Data 目錄找到)添加 getter 和 setter。

數據保存到quote_address

完成了extension attributes的聲明,與數據表字段建立,系統並不會把WEBAPI提交過來的extension attributes自動保存到表中,這須要寫代碼完成這個操做。方法是讓Model在save時就把extension attributes寫入屬性,Model的屬性會自動更新至數據表。不該該直接修改原代碼,建議使用event機制。
etc/di.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Quote\Model\Quote\Address">
        <plugin name="xxxx_checkout_extension_attributes_processor" type="<Vendor>\<Module>\Plugin\Magento\Quote\Model\Quote\Address" />
    </type>
</config>

Address.php

namespace Infinity\Checkout\Plugin\Magento\Quote\Model\Quote;

class Address
{
    public function afterBeforeSave(\Magento\Quote\Model\Quote\Address $subject, \Magento\Quote\Model\Quote\Address $return) {

        $extensionAttributes = $return->getExtensionAttributes();
        if($extensionAttributes != null) {
            $return->addData( $extensionAttributes->__toArray() );
        }
        return $return;
    }
}

數據從quote表到order表

etc/events.xml

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="sales_model_service_quote_submit_before">
        <observer name="xxxxx_checkout_sales_model_service_quote_submit_before" instance="<Vendor>\<Model>\Observer\SaveOrderBeforeSalesModelQuoteObserver" />
    </event>
</config>

SaveOrderBeforeSalesModelQuoteObserver.php

namespace Infinity\Checkout\Observer;

use Magento\Framework\Event\ObserverInterface;

class SaveOrderBeforeSalesModelQuoteObserver implements ObserverInterface
{
    public function execute(\Magento\Framework\Event\Observer $observer) {
        /* @var \Magento\Sales\Model\Order $order */
        $order = $observer->getEvent()->getData('order');
        /* @var \Magento\Quote\Model\Quote $quote */
        $quote = $observer->getEvent()->getData('quote');

        $attributes = ['delivery_date_1', 'delivery_date_2', 'installation_date_1', 'installation_date_2'];

        foreach($attributes as $attribute) {
            if($quote->getShippingAddress()->hasData($attribute)) {
                $order->getShippingAddress()->setData($attribute, $quote->getShippingAddress()->getData($attribute));
            }
        }

        return $this;
    }
}

checkout過程當中提交extension attributes

vendor/magento/module-checkout/view/frontend/web/js/model/shipping-save-processor/default.js

var shippingAddress = quote.shippingAddress();
shippingAddress['extension_attributes'] = {
    delivery_date_1: 'xxxxx',
    delivery_date_2: 'yyyyy'
};
quote.shippingAddress(shippingAddress);

後臺order detail 裏顯示這個數據

Address在後臺、PDF或者是單行狀況下,都是能夠指定模板的,可在Customer Configuration > Address Templates設置對應模板。可是在2.1版本中新增的Address字段沒法被模板引用,官方也認可這個BUG,而且可能在2.4才獲得解決。我目前能找到的辦法是利用customer_address_format事件。

etc/events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="customer_address_format">
        <observer name="infinity_checkout_customer_address_format" instance="Infinity\Checkout\Observer\CustomerAddressFormat" />
    </event>
</config>

app/code/Infinity/Checkout/Observer/CustomerAddressFormat.php

namespace Infinity\Checkout\Observer;

use Magento\Framework\Event\ObserverInterface;

class CustomerAddressFormat implements ObserverInterface
{
    public function execute(\Magento\Framework\Event\Observer $observer) {
        $formatType = $observer->getEvent()->getData('type');
        $address = $observer->getEvent()->getData('address');

        $format = $formatType->getDefaultFormat();
        if($formatType->getCode() == 'html') {
            $html ='';
            if($address->getData('delivery_date_1')) {
                $html .= __('Delivery Date').':'.$address->getData('delivery_date_1');
                if($address->getData('delivery_date_2'))
                    $html .= ':'.__('or').' '.$address->getData('delivery_date_2');
                $html .= '<br/>';
            }
            $formatType->setData('default_format', $format.'<br/>'.$html);
        }
    }
}

Quote Item to Order Item

https://stackoverflow.com/que...

相關文章
相關標籤/搜索