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。

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

quote和sales_order很多字段对应,当你把产品加入到cart,会创建一个quote,cart的数据就会存到quote中,checkout到了正式支付后,order才会被真正产生出来,其实数据库里的操作是数据从quote搬到了sales_order。所以添加字段,应该在quote与sales_order同时添加,数据才会传到最终的order。

Setup/InstallSchema.php (忽略类的细节,只有关键代码)

$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中的注释是必须的,而且参数声明必须有类型,否则系统会把参数当成是类。这对于老手特别容易出错。

<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.html

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

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

requirejs-config.js

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...

step5 生成订单时把quote数据般到sales_order

etc/events.xml

<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.php

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...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值