我要在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);
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。