我要在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
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();
建立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; }
我選擇加到shipping這一步
app/design/frontend/<vendor>/<theme>/Magento_Checkout/web/template/shipping.htmljquery
<textarea id="checkout_comment"></textarea>
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
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; }
爲了修改後臺的模板,我直接修改了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追加屬性爲例,如下是完整過程。
$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' ] );
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。
完成了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; } }
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; } }
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);
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); } } }