Magento चेकआउट शिपिंग विधि अतिरिक्त डेटा के लिए किसी भी प्रकार के फ़ॉर्म का समर्थन नहीं करता है। लेकिन यह shippingAdditional
चेकआउट में ब्लॉक प्रदान करता है जिसका उपयोग इसके लिए किया जा सकता है। निम्न समाधान मानक Magento चेकआउट के लिए काम करेगा।
पहले अपने कंटेनर को तैयार करते हैं जहां हम कुछ फार्म डाल सकते हैं। ऐसा करने के लिए एक फ़ाइल बनाएँview/frontend/layout/checkout_index_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="checkout.root">
<arguments>
<argument name="jsLayout" xsi:type="array">
<item name="components" xsi:type="array">
<item name="checkout" xsi:type="array">
<item name="children" xsi:type="array">
<item name="steps" xsi:type="array">
<item name="children" xsi:type="array">
<item name="shipping-step" xsi:type="array">
<item name="children" xsi:type="array">
<item name="shippingAddress" xsi:type="array">
<item name="children" xsi:type="array">
<item name="shippingAdditional" xsi:type="array">
<item name="component" xsi:type="string">uiComponent</item>
<item name="displayArea" xsi:type="string">shippingAdditional</item>
<item name="children" xsi:type="array">
<item name="vendor_carrier_form" xsi:type="array">
<item name="component" xsi:type="string">Vendor_Module/js/view/checkout/shipping/form</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</item>
</argument>
</arguments>
</referenceBlock>
</body>
</page>
अब एक फाइल बनाएं Vendor/Module/view/frontend/web/js/view/checkout/shipping/form.js
जिसमें नॉकआउट टेम्पलेट रेंडर होगा। इसकी सामग्री इस तरह दिखती है
define([
'jquery',
'ko',
'uiComponent',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/model/shipping-service',
'Vendor_Module/js/view/checkout/shipping/office-service',
'mage/translate',
], function ($, ko, Component, quote, shippingService, officeService, t) {
'use strict';
return Component.extend({
defaults: {
template: 'Vendor_Module/checkout/shipping/form'
},
initialize: function (config) {
this.offices = ko.observableArray();
this.selectedOffice = ko.observable();
this._super();
},
initObservable: function () {
this._super();
this.showOfficeSelection = ko.computed(function() {
return this.ofices().length != 0
}, this);
this.selectedMethod = ko.computed(function() {
var method = quote.shippingMethod();
var selectedMethod = method != null ? method.carrier_code + '_' + method.method_code : null;
return selectedMethod;
}, this);
quote.shippingMethod.subscribe(function(method) {
var selectedMethod = method != null ? method.carrier_code + '_' + method.method_code : null;
if (selectedMethod == 'carrier_method') {
this.reloadOffices();
}
}, this);
this.selectedOffice.subscribe(function(office) {
if (quote.shippingAddress().extensionAttributes == undefined) {
quote.shippingAddress().extensionAttributes = {};
}
quote.shippingAddress().extensionAttributes.carrier_office = office;
});
return this;
},
setOfficeList: function(list) {
this.offices(list);
},
reloadOffices: function() {
officeService.getOfficeList(quote.shippingAddress(), this);
var defaultOffice = this.offices()[0];
if (defaultOffice) {
this.selectedOffice(defaultOffice);
}
},
getOffice: function() {
var office;
if (this.selectedOffice()) {
for (var i in this.offices()) {
var m = this.offices()[i];
if (m.name == this.selectedOffice()) {
office = m;
}
}
}
else {
office = this.offices()[0];
}
return office;
},
initSelector: function() {
var startOffice = this.getOffice();
}
});
});
यह फ़ाइल नॉकआउट टेम्पलेट का उपयोग करती है जिसे अंदर रखा जाना चाहिए Vendor/Module/view/frontend/web/template/checkout/shipping/form.html
<div id="carrier-office-list-wrapper" data-bind="visible: selectedMethod() == 'carrier_method'">
<p data-bind="visible: !showOfficeSelection(), i18n: 'Please provide postcode to see nearest offices'"></p>
<div data-bind="visible: showOfficeSelection()">
<p>
<span data-bind="i18n: 'Select pickup office.'"></span>
</p>
<select id="carrier-office-list" data-bind="options: offices(),
value: selectedOffice,
optionsValue: 'name',
optionsText: function(item){return item.location + ' (' + item.name +')';}">
</select>
</div>
</div>
अब हमारे पास एक चुनिंदा फ़ील्ड है जो तब दिखाई देगा जब हमारा तरीका (इसके कोड द्वारा परिभाषित) शिपिंग विधियों तालिका में चुना जाएगा। कुछ विकल्पों के साथ इसे भरने का समय। चूंकि मान पते पर निर्भर हैं, इसलिए सबसे अच्छा तरीका बाकी समापन बिंदु बनाना है जो उपलब्ध विकल्प प्रदान करेगा। मेंVendor/Module/etc/webapi.xml
<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
<!-- Managing Office List on Checkout page -->
<route url="/V1/module/get-office-list/:postcode/:city" method="GET">
<service class="Vendor\Module\Api\OfficeManagementInterface" method="fetchOffices"/>
<resources>
<resource ref="anonymous" />
</resources>
</route>
</routes>
अब इंटरफ़ेस को इस Vendor/Module/Api/OfficeManagementInterface.php
रूप में परिभाषित करें
namespace Vendor\Module\Api;
interface OfficeManagementInterface
{
/**
* Find offices for the customer
*
* @param string $postcode
* @param string $city
* @return \Vendor\Module\Api\Data\OfficeInterface[]
*/
public function fetchOffices($postcode, $city);
}
में कार्यालय डेटा के लिए इंटरफ़ेस को परिभाषित करें Vendor\Module\Api\Data\OfficeInterface.php
। इस इंटरफ़ेस का उपयोग वेबैपी मॉड्यूल द्वारा आउटपुट के लिए डेटा को फ़िल्टर करने के लिए किया जाएगा ताकि आपको प्रतिक्रिया में जोड़ने के लिए जो भी आवश्यक हो उसे परिभाषित करना पड़े।
namespace Vendor\Module\Api\Data;
/**
* Office Interface
*/
interface OfficeInterface
{
/**
* @return string
*/
public function getName();
/**
* @return string
*/
public function getLocation();
}
वास्तविक कक्षाओं के लिए समय। में सभी इंटरफेस के लिए प्राथमिकताएँ बनाने के साथ शुरू करेंVendor/Module/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Vendor\Module\Api\OfficeManagementInterface" type="Vendor\Module\Model\OfficeManagement" />
<preference for="Vendor\Module\Api\Data\OfficeInterface" type="Vendor\Module\Model\Office" />
</config>
अब Vendor\Module\Model\OfficeManagement.php
वह वर्ग बनाएं जो वास्तव में डेटा लाने का तर्क देगा।
namespace Vednor\Module\Model;
use Vednor\Module\Api\OfficeManagementInterface;
use Vednor\Module\Api\Data\OfficeInterfaceFactory;
class OfficeManagement implements OfficeManagementInterface
{
protected $officeFactory;
/**
* OfficeManagement constructor.
* @param OfficeInterfaceFactory $officeInterfaceFactory
*/
public function __construct(OfficeInterfaceFactory $officeInterfaceFactory)
{
$this->officeFactory = $officeInterfaceFactory;
}
/**
* Get offices for the given postcode and city
*
* @param string $postcode
* @param string $limit
* @return \Vendor\Module\Api\Data\OfficeInterface[]
*/
public function fetchOffices($postcode, $city)
{
$result = [];
for($i = 0, $i < 4;$i++) {
$office = $this->officeFactory->create();
$office->setName("Office {$i}");
$office->setLocation("Address {$i}");
$result[] = $office;
}
return $result;
}
}
और अंत में के लिए वर्ग OfficeInterface
मेंVendor/Module/Model/Office.php
namespace Vendor\Module\Model;
use Magento\Framework\DataObject;
use Vendor\Module\Api\Data\OfficeInterface;
class Office extends DataObject implements OfficeInterface
{
/**
* @return string
*/
public function getName()
{
return (string)$this->_getData('name');
}
/**
* @return string
*/
public function getLocation()
{
return (string)$this->_getData('location');
}
}
यह चुनिंदा फ़ील्ड दिखाना चाहिए और पता बदलने पर इसे अपडेट करना चाहिए। लेकिन हम दृश्यपटल हेरफेर के लिए एक और तत्व को याद कर रहे हैं। हमें फ़ंक्शन बनाने की आवश्यकता है जो समापन बिंदु को कॉल करेगा। इसमें कॉल पहले से ही शामिल है Vendor/Module/view/frontend/web/js/view/checkout/shipping/form.js
और यह Vendor_Module/js/view/checkout/shipping/office-service
वर्ग है जिसे Vendor/Module/view/frontend/web/js/view/checkout/shipping/office-service.js
निम्नलिखित कोड के साथ जाना चाहिए :
define(
[
'Vendor_Module/js/view/checkout/shipping/model/resource-url-manager',
'Magento_Checkout/js/model/quote',
'Magento_Customer/js/model/customer',
'mage/storage',
'Magento_Checkout/js/model/shipping-service',
'Vendor_Module/js/view/checkout/shipping/model/office-registry',
'Magento_Checkout/js/model/error-processor'
],
function (resourceUrlManager, quote, customer, storage, shippingService, officeRegistry, errorProcessor) {
'use strict';
return {
/**
* Get nearest machine list for specified address
* @param {Object} address
*/
getOfficeList: function (address, form) {
shippingService.isLoading(true);
var cacheKey = address.getCacheKey(),
cache = officeRegistry.get(cacheKey),
serviceUrl = resourceUrlManager.getUrlForOfficeList(quote);
if (cache) {
form.setOfficeList(cache);
shippingService.isLoading(false);
} else {
storage.get(
serviceUrl, false
).done(
function (result) {
officeRegistry.set(cacheKey, result);
form.setOfficeList(result);
}
).fail(
function (response) {
errorProcessor.process(response);
}
).always(
function () {
shippingService.isLoading(false);
}
);
}
}
};
}
);
यह 2 और js फ़ाइलों का उपयोग करता है। Vendor_Module/js/view/checkout/shipping/model/resource-url-manager
समापन बिंदु के लिए एक url बनाता है और बहुत सरल है
define(
[
'Magento_Customer/js/model/customer',
'Magento_Checkout/js/model/quote',
'Magento_Checkout/js/model/url-builder',
'mageUtils'
],
function(customer, quote, urlBuilder, utils) {
"use strict";
return {
getUrlForOfficeList: function(quote, limit) {
var params = {postcode: quote.shippingAddress().postcode, city: quote.shippingAddress().city};
var urls = {
'default': '/module/get-office-list/:postcode/:city'
};
return this.getUrl(urls, params);
},
/** Get url for service */
getUrl: function(urls, urlParams) {
var url;
if (utils.isEmpty(urls)) {
return 'Provided service call does not exist.';
}
if (!utils.isEmpty(urls['default'])) {
url = urls['default'];
} else {
url = urls[this.getCheckoutMethod()];
}
return urlBuilder.createUrl(url, urlParams);
},
getCheckoutMethod: function() {
return customer.isLoggedIn() ? 'customer' : 'guest';
}
};
}
);
Vendor_Module/js/view/checkout/shipping/model/office-registry
स्थानीय भंडारण में परिणाम रखने का एक तरीका है। इसका कोड है:
define(
[],
function() {
"use strict";
var cache = [];
return {
get: function(addressKey) {
if (cache[addressKey]) {
return cache[addressKey];
}
return false;
},
set: function(addressKey, data) {
cache[addressKey] = data;
}
};
}
);
ठीक है, इसलिए हम सभी को फ्रंटेंड पर काम करना चाहिए। लेकिन अब इसके समाधान के लिए एक और समस्या है। चूंकि चेकआउट को इस फॉर्म के बारे में कुछ भी पता नहीं है, इसलिए यह चयन परिणाम को बैकएंड पर नहीं भेजेगा। ऐसा करने के लिए हमें उपयोग करने की आवश्यकता हैextension_attributes
सुविधा । यह सिस्टम को सूचित करने के लिए magento2 में एक तरीका है कि बाकी कॉल में कुछ अतिरिक्त डेटा होने की उम्मीद है। इसके बिना Magento उन डेटा को फ़िल्टर कर देगा और वे कभी भी कोड तक नहीं पहुंचेंगे।
तो पहले Vendor/Module/etc/extension_attributes.xml
परिभाषित करें:
<?xml version="1.0"?>
<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="carrier_office" type="string"/>
</extension_attributes>
</config>
यह मान पहले से ही में अनुरोध में डाला जाता है form.js
द्वारा this.selectedOffice.subscribe()
परिभाषा। तो उपरोक्त कॉन्फ़िगरेशन केवल प्रवेश द्वार पर इसे पारित करेगा। कोड में लाने के लिए एक प्लगइन बनाएंVendor/Module/etc/di.xml
<type name="Magento\Quote\Model\Quote\Address">
<plugin name="inpost-address" type="Vendor\Module\Quote\AddressPlugin" sortOrder="1" disabled="false"/>
</type>
उस वर्ग के अंदर
namespace Vendor\Module\Plugin\Quote;
use Magento\Quote\Model\Quote\Address;
use Vendor\Module\Model\Carrier;
class AddressPlugin
{
/**
* Hook into setShippingMethod.
* As this is magic function processed by __call method we need to hook around __call
* to get the name of the called method. after__call does not provide this information.
*
* @param Address $subject
* @param callable $proceed
* @param string $method
* @param mixed $vars
* @return Address
*/
public function around__call($subject, $proceed, $method, $vars)
{
$result = $proceed($method, $vars);
if ($method == 'setShippingMethod'
&& $vars[0] == Carrier::CARRIER_CODE.'_'.Carrier::METHOD_CODE
&& $subject->getExtensionAttributes()
&& $subject->getExtensionAttributes()->getCarrierOffice()
) {
$subject->setCarrierOffice($subject->getExtensionAttributes()->getCarrierOffice());
}
elseif (
$method == 'setShippingMethod'
&& $vars[0] != Carrier::CARRIER_CODE.'_'.Carrier::METHOD_CODE
) {
//reset office when changing shipping method
$subject->getCarrierOffice(null);
}
return $result;
}
}
बेशक जहां आप मूल्य बचाएंगे, यह पूरी तरह से आपकी आवश्यकताओं पर निर्भर करता है। उपरोक्त कोड अतिरिक्त स्तंभ बनाने की आवश्यकता होगी carrier_office
में quote_address
और sales_address
टेबल और एक घटना (में Vendor/Module/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="sales_model_service_quote_submit_before">
<observer name="copy_carrier_office" instance="Vendor\Module\Observer\Model\Order" />
</event>
</config>
वह उद्धरण पते में सहेजे गए डेटा को बिक्री पते पर कॉपी करेगा।
मैंने पॉलिश कैरियर InPost के लिए अपने मॉड्यूल के लिए यह लिखा था इसलिए मैंने कुछ नाम बदले जो कोड को तोड़ सकते हैं लेकिन मुझे आशा है कि यह आपको वह देगा जो आपको चाहिए।
[संपादित करें]
कैरियर मॉडल @sangan द्वारा पूछा गया
namespace Vendor\Module\Model;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Phrase;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Carrier\AbstractCarrier;
use Magento\Shipping\Model\Carrier\CarrierInterface;
use Magento\Shipping\Model\Simplexml\ElementFactory;
class Carrier extends AbstractCarrier implements CarrierInterface
{
const CARRIER_CODE = 'mycarier';
const METHOD_CODE = 'mymethod';
/** @var string */
protected $_code = self::CARRIER_CODE;
/** @var bool */
protected $_isFixed = true;
/**
* Prepare stores to show on frontend
*
* @param RateRequest $request
* @return \Magento\Framework\DataObject|bool|null
*/
public function collectRates(RateRequest $request)
{
if (!$this->getConfigData('active')) {
return false;
}
/** @var \Magento\Shipping\Model\Rate\Result $result */
$result = $this->_rateFactory->create();
/** @var \Magento\Quote\Model\Quote\Address\RateResult\Method $method */
$method = $this->_rateMethodFactory->create();
$method->setCarrier($this->_code);
$method->setCarrierTitle($this->getConfigData('title'));
$price = $this->getFinalPriceWithHandlingFee(0);
$method->setMethod(self::METHOD_CODE);
$method->setMethodTitle(new Phrase('MyMethod'));
$method->setPrice($price);
$method->setCost($price);
$result->append($method);;
return $result;
}
/**
* @return array
*/
public function getAllowedMethods()
{
$methods = [
'mymethod' => new Phrase('MyMethod')
];
return $methods;
}
}