I'm trying to add a new field to the shipping address in Magento 2.3.4.
I would like to add it "Magento way" that's why I used these tutorials:
https://devdocs.magento.com/guides/v2.3/howdoi/checkout/checkout_new_field.html
https://www.edmondscommerce.co.uk/handbook/Platforms/Magento-2/Guides/Custom-Shipping-Address-Field/
The field appears correctly on the frontend, but after adding new address and filling that field and click "Ship here":
I got that error:
Here is my code:
1) At first, I thought that it is not necessary (this step is not in Magento 2 devdocs but appears in second tutorial) - app/code/Company/Module/Setup/Patch/Data/AddVipCodeAttribute.php:
<?php
namespace Company\Module\Setup\Patch\Data;
use Magento\Catalog\Model\Product;
use Magento\Customer\Setup\CustomerSetupFactory;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\DB\Ddl\Table;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchRevertableInterface;
class AddVipCodeAttribute implements DataPatchInterface, PatchRevertableInterface
{
/**
* #var ModuleDataSetupInterface
*/
private $moduleDataSetup;
/**
* #var EavSetupFactory
*/
private $eavSetupFactory;
/**
* #var CustomerSetupFactory
*/
private $customerSetupFactory;
/**
* Constructor
*
* #param ModuleDataSetupInterface $moduleDataSetup
* #param EavSetupFactory $eavSetupFactory
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
EavSetupFactory $eavSetupFactory,
CustomerSetupFactory $customerSetupFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->eavSetupFactory = $eavSetupFactory;
$this->customerSetupFactory = $customerSetupFactory;
}
/**
* #inheritdoc
*/
public function apply()
{
$this->moduleDataSetup->getConnection()->startSetup();
/** #var CustomerSetup $customerSetup */
$customerSetup = $this->customerSetupFactory->create(['setup' => $this->moduleDataSetup]);
$customerSetup->addAttribute('customer_address', 'vip_code', [
'label' => 'Vip account code',
'input' => 'text',
'type' => Table::TYPE_TEXT,
'source' => '',
'required' => false,
'position' => 333,
'visible' => true,
'system' => false,
'is_used_in_grid' => false,
'is_visible_in_grid' => false,
'is_filterable_in_grid' => false,
'is_searchable_in_grid' => false,
'backend' => ''
]);
$attribute = $customerSetup->getEavConfig()->getAttribute('customer_address', 'vip_code')
->addData(['used_in_forms' => [
'adminhtml_customer_address',
'adminhtml_customer',
'customer_address_edit',
'customer_register_address',
'customer_address',
]]);
$attribute->save();
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* #inheritDoc
*/
public function revert()
{
}
/**
* #inheritdoc
*/
public function getAliases()
{
return [];
}
/**
* #inheritdoc
*/
public static function getDependencies()
{
return [];
}
}
2)I created a plugin class:
etc/frontend/di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Checkout\Block\Checkout\LayoutProcessor">
<plugin name="add_custom_field_checkout_form" type="Company\Module\Plugin\Checkout\LayoutProcessor" sortOrder="100"/>
</type>
</config>
And layout processor app/code/Company/Module/Plugin/Checkout/LayoutProcessor.php
<?php
namespace Company\Module\Plugin\Checkout;
use Magento\Checkout\Block\Checkout\LayoutProcessor as LayoutProcessorCore;
use Magento\Customer\Model\Session;
class LayoutProcessor
{
/**
* #var Session
*/
protected $session;
public function __construct(
Session $session
) {
$this->session = $session;
}
/**
* #param LayoutProcessorCore $subject
* #param array $jsLayout
*
* #return array
*/
public function afterProcess(
LayoutProcessorCore $subject,
array $jsLayout
) {
$customAttributeCode = 'vip_code';
$customField = [
'component' => 'Magento_Ui/js/form/element/abstract',
'config' => [
// customScope is used to group elements within a single form (e.g. they can be validated separately)
'customScope' => 'shippingAddress.custom_attributes',
'customEntry' => null,
'template' => 'ui/form/field',
'elementTmpl' => 'ui/form/element/input',
'tooltip' => [
'description' => 'Vip accounts code. Example: 123123123ASD',
],
],
'dataScope' => 'shippingAddress.custom_attributes' . '.' . $customAttributeCode,
'label' => 'Vip code',
'provider' => 'checkoutProvider',
'sortOrder' => 0,
'validation' => [
'required-entry' => false
],
'options' => [],
'filterBy' => null,
'customEntry' => null,
'visible' => true,
];
$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
['shippingAddress']['children']['shipping-address-fieldset']['children'][$customAttributeCode] = $customField;
return $jsLayout;
}
}
3)JS files:
app/code/Company/Module/view/frontend/requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/action/set-shipping-information': {
'Company_Module/js/add-new-field': true
}
}
}
};
app/code/Company/Module/view/frontend/web/js/add-new-field.js
/*jshint browser:true jquery:true*/
/*global alert*/
define([
'jquery',
'mage/utils/wrapper',
'Magento_Checkout/js/model/quote'
], function ($, wrapper, quote) {
'use strict';
return function (setShippingInformationAction) {
return wrapper.wrap(setShippingInformationAction, function (originalAction) {
var shippingAddress = quote.shippingAddress();
if (shippingAddress['extension_attributes'] === undefined) {
shippingAddress['extension_attributes'] = {};
}
shippingAddress['extension_attributes']['vip_code'] = shippingAddress.customAttributes['vip_code'];
console.log(shippingAddress);
// pass execution to original action ('Magento_Checkout/js/action/set-shipping-information')
return originalAction();
});
};
});
[UPDATE]
app/code/BartCompany/VipAccounts/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="vip_code" type="string" />
</extension_attributes>
</config>
Can anybody help me with that, I'm working on it almost a week, and tried a lot of solutions, but it is for me very important to do that by custom_attributes field.
I will be grateful, thank You.
I think i found the issue, here is a follow:
https://github.com/magento/magento2/issues/26740
Related
Key question is, does the \yii\widgets\ListView actually support filters with Pjax, like it's \yii\widgets\GridView counterpart? Here's what I have tried that has led to a duplicate url params issue:
I have a Gii-created search model with a custom param, $userRoleFilter:
<?php
namespace common\models;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use yii\rbac\Item;
/**
* UserSearch represents the model behind the search form of `common\models\User`.
*/
class UserSearch extends User
{
public $userRoleFilter = null;
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['id', 'flags', 'confirmed_at', 'blocked_at', 'updated_at', 'created_at', 'last_login_at', 'auth_tf_enabled', 'password_changed_at', 'gdpr_consent', 'gdpr_consent_date', 'gdpr_deleted'], 'integer'],
[['username', 'email', 'password_hash', 'auth_key', 'unconfirmed_email', 'registration_ip', 'last_login_ip', 'auth_tf_key', 'userRoleFilter'], 'safe'],
];
}
/**
* {#inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = User::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 20,
],
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'flags' => $this->flags,
'confirmed_at' => $this->confirmed_at,
'blocked_at' => $this->blocked_at,
'updated_at' => $this->updated_at,
'created_at' => $this->created_at,
'last_login_at' => $this->last_login_at,
'auth_tf_enabled' => $this->auth_tf_enabled,
'password_changed_at' => $this->password_changed_at,
'gdpr_consent' => $this->gdpr_consent,
'gdpr_consent_date' => $this->gdpr_consent_date,
'gdpr_deleted' => $this->gdpr_deleted,
]);
$query->andFilterWhere(['like', 'username', $this->username])
->andFilterWhere(['like', 'email', $this->email])
->andFilterWhere(['like', 'password_hash', $this->password_hash])
->andFilterWhere(['like', 'auth_key', $this->auth_key])
->andFilterWhere(['like', 'unconfirmed_email', $this->unconfirmed_email])
->andFilterWhere(['like', 'registration_ip', $this->registration_ip])
->andFilterWhere(['like', 'last_login_ip', $this->last_login_ip])
->andFilterWhere(['like', 'auth_tf_key', $this->auth_tf_key]);
if (!empty($params['UserSearch']['userRoleFilter'])) {
$userRoleFilter = $params['UserSearch']['userRoleFilter'];
// inner join to the user role items to filter by assigned role
$query->alias('u')
->innerJoin(
'auth_assignment AS aa',
'u.id = aa.user_id AND aa.item_name = :roleName',
['roleName' => $userRoleFilter]
)
->leftJoin(
'auth_item AS ai',
'aa.item_name = ai.name AND ai.type = :typeRole',
['typeRole' => Item::TYPE_ROLE]
);
}
return $dataProvider;
}
}
Controller method:
/**
* Displays pricing calculation & data exports index.
*
* #return string
*/
public function actionDisplayUsers()
{
$searchModel = new UserSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('display-users', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
And manually wrapped my custom yii\bootstrap\ActiveForm in the Pjax tags:
<?php
/* #var $this yii\web\View */
/* #var $searchModel common\models\UserSearch */
/* #var $dataProvider yii\data\ActiveDataProvider */
use common\components\rbac\UserManager;
use yii\bootstrap\ActiveForm;
use yii\widgets\ListView;
use yii\widgets\Pjax;
$this->title = Yii::t('app', 'Display Users');
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="site-display-users">
<div class="body-content">
<h1><?= $this->title ?></h1>
<?php Pjax::begin([
'options' => [
'id' => 'site-display-users-pjax-container',
],
'enablePushState' => true,
'enableReplaceState' => false,
]); ?>
<div class="well center-block meta-control">
<?php $form = ActiveForm::begin([
'id' => 'displayUsersForm',
'method' => 'get',
'options' => [
'data-pjax' => 1,
],
]); ?>
<div class="row row-grid">
<div class="col-xs-6">
<?=
$form
->field($searchModel, 'userRoleFilter')
->dropDownList(UserManager::getAvailableRoles(), [
'prompt' => 'Select User Role',
'id' => 'userRoleFilter',
])
?>
</div>
<div class="col-xs-6">
</div>
</div>
<?php ActiveForm::end(); ?>
</div>
<div class="row">
<div class="col-lg-12">
<?php
echo ListView::widget([
'dataProvider' => $dataProvider,
'itemView' => '_display-user',
'viewParams' => [
// add params to pass into view here
],
]);
?>
</div>
</div>
<?php Pjax::end(); ?>
</div>
</div>
This works fine and filters the users in the ListView according to the selected role. But it is creating a duplicate url param after each time the filter is changed:
/site/display-users
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin&UserSearch%5BuserRoleFilter%5D=role-importer-user
/site/display-users?UserSearch%5BuserRoleFilter%5D=admin&UserSearch%5BuserRoleFilter%5D=role-importer-user&UserSearch%5BuserRoleFilter%5D=admin
and so on...
I know that I can set the Pjax enablePushState and enableReplaceState values to false and then it does not keep creating history items and modifying the url in the browser, but just sends the same ever-lengthening url in the ajax request...
What can be done? Is there a better way to handle this? A setting I am missing to stop this duplication of get param keys stacking up in the url?
Found out the solution... turns out that the ActiveForm form action parameter needs to be explicitly defined so that this URI is used for each form submission rather than relying on the URL from the address bar.
<?php $form = ActiveForm::begin([
'id' => 'displayUsersForm',
'method' => 'get',
'action' => Url::to(['site/display-users']),
'options' => [
'data-pjax' => 1,
],
]); ?>
See here for more details.
I am new to laravel and vue.js. I am fetching data from API using resources. The idea is am using 2 div's GOAL and VALUE.
The Value should be updated when there is a change in the value from the server through PUSHER..
without refreshing the page.
here is my code
1.model
class Progress extends Model
{
protected $fillable = [
'id', 'name', 'goal', 'description'
];
public $timestamps = false;
}
2.Controller
class ProgressController extends Controller
{
public function index()
{
return ProgressResource::collection(Progress::paginate(4));
event(new UpdatedValue());
}
}
3.Resource.php
class ProgressResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return[
'the_custom_id' => $this->id,
'name' => $this->name,
'goal' => $this->goal,
'description' => $this->description,
'value' => ProgressResource::mockData($this->goal),
];
}
public static function mockData($goal=1000)
{
// 0 to $goal takes 17 minutes
$multiplier = ($goal + 7) / 1000;
$count = substr(time(), -3);
return intval(round($multiplier * $count, 0));
}
}
4.Events
class UpdatedValue implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $value, $goal;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct($value)
{
//$this->progress = ProgressResource::collection(Progress::paginate(4));
$this->value = ProgressResource::mockData($this->goal);
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('progress');
}
}
5.Components/Front.vue
<template>
<div class="container">
<h1> Progress </h1>
<div class= "progress" v-for = "progressdata in progress" v-bind:id="progressdata.id">
<div id="div1">{{ progressdata.goal }}</div>
<div id="div2" class="value">{{ progressdata.value }}</div>
</div>
</div>
</template>
<script>
export default {
data: function() {
return {
progress: [],
}
},
mounted() {
this.loadContents();
this.listen();
},
methods: {
loadContents: function() {
//load Api
axios.get('/api/progress')
.then((response) => {
this.progress = response.data.data;
})
.catch(function (error) {
console.log(error);
});
},
listen(){
Echo.channel('progress')
.listen('UpdatedValue', (e) =>{
this.value = e.value;
console.log(this.value);
//console.log(e);
});
}
}
}
</script>
6.BLade.php
<div id ="app">
<front-page ></front-page>
</div>
<script src = "{{ mix('js/app.js') }}"></script>
</body>
Once the event has been triggered, the value should be updated in the front end without refreshing the page. I have installed PUSHER PACKAGES , PUSHER-JS AND Laravel Echo THROUGH NPM.I couldn't get the value which is updated through event in the front end..Could anyone help to solve this issue?.
Thanks.
loadContents: function() {
//load Api
let self = this ; // we are storing VueComponent object here.Cause this wont work in callback it will be undefined.
axios.get('/api/progress')
.then((response) => {
self.progress = response.data.data;
})
.catch(function (error) {
console.log(error);
});
You didn't define value in data properties. Define Value there and use self object to store this(object).
data: function() {
return {
progress: [],
value:null,
}
},
I'm working with Drupal 8 and I have custom ajax form it works fine when I test it using [Ajax htmlCommands]. but now I want to send the data to the javascript file to show it in a div and do some other things.
what I did is create js file under themes/custom/myform/assets/js/simple-form.js but nothing shows in the console when the element clicked.
Drupal.behaviors.simple_form = function(context) {
$('.btn-primary').click(function() {
console.log('clicked');
});
};
and add it to themes/custom/myform/myform.libraries.yml
simple-form-js:
js:
assets/js/simple-form.js: {}
my custom form
modules/custom/my_module/src/Form/SimpleForm.php
<?php
namespace Drupal\my_module\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
/**
* Our simple form class.
*/
class SimpleForm extends FormBase {
/**
* {#inheritdoc}
*/
public function getFormId() {
return 'my_module';
}
/**
* {#inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
//$form = parent::buildForm($form, $form_state);
$form['#theme'] = 'simple_form';
$form['massage'] = [
'#type' => 'markup',
'#markup' => '<div class="result_message"></div>',
];
$form['number_1'] = [
'#type' => 'textfield',
'#title' => $this->t('number 1'),
'#attributes' => ['class' => ['form-control ml-sm-2 w-100']],
'#required' => TRUE,
];
$form['number_2'] = [
'#type' => 'textfield',
'#title' => $this->t('number one'),
'#attributes' => ['class' => ['form-control ml-sm-2 w-100']],
'#required' => TRUE,
];
$form['actions'] = [
'#type' => 'button',
'#value' => $this->t('Calculate'),
'#attributes' => ['onclick'=>'return false;','class' => ['mt-3 btn btn-primary btn-me2']],
'#attached' => [
'library'=>[
'simple-form',
]
],
'#ajax' => [
'callback' => '::setMessage',
]
];
return $form;
}
public function setMessage(array &$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$letter = $form_state->getValue('number_1');
$code = $form_state->getValue('number_2');
$fetchUrl = 'http://example.com/api';
$responseAPI = \Drupal::httpClient()->get($fetchUrl, array('headers' => array('Accept' => 'text/plain')));
$data = (string) $responseAPI->getBody();
//drupal_set_message($data);
$response->addCommand(
new HtmlCommand(
'.result_message',
'<div class="my_top_message">' . $this->t('The result is #result', ['#result' => ($data)])
)
);
return $response;
}
public function validateForm(array &$form, FormStateInterface $form_state) {
}
/**
* {#inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
}
}
and themes/custom/myform/myform.theme
if($route_namee=="my_module.simple_form"){
$variables['#attached']['library'][] = 'myform/simple-form-css';
$variables['#attached']['library'][] = 'myform/simple-form-js';
}
You can use HOOK_form_alter to attach libraries
*/**
* Implements hook_form_alter().
*/
function my_module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
if($form_id == "my_module") {
$form['#attached']['library'][] = 'myform/simple-form-js';
$form['#attached']['library'][] = 'myform/simple-form-css';
$form['#attached']['drupalSettings']['my_module']['variable'] = 'Your Custom values to JS';
return $form;
}
}
What you are looking for is a ajaxCommand and not a behavior.
Create a custom ajaxCommand and pass your variables to it when creating the ajax response.
Here is help https://weknowinc.com/blog/creating-custom-ajax-command-drupal-8
And get Inspired with how Drupal Cores InvokeCommand is working
To attach library you dont need theme or module files for example :
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
is enough to attach a library in your case you should call your js file like :
$form['#attached']['library'][] = 'simple-form-js';
Then files under your library will loaded with your form.
I am actually working on customization of the checkout form. I need to add a custom input field such as Fiscal Code inside the form.
Via Plugin I added the field, but after Placing Order the reference to this field disappear, how i can save and display it in Sales Order?
This is the code i used
Module.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Lutech_CheckoutAdditionalField" setup_version="1.0.0">
<sequence>
<module name="Magento_Checkout"/>
</sequence>
</module>
</config>
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">
<type name="Magento\Checkout\Block\Checkout\LayoutProcessor">
<plugin name="fiscal_code_plugin" type="Lutech\CheckoutAdditionalField\Plugin\FiscalCodePlugin" sortOrder="1" />
</type>
<type name="Magento\Checkout\Model\ShippingInformationManagement">
<plugin name="fiscal_code_save_plugin" type="Lutech\CheckoutAdditionalField\Plugin\SaveAddressInformation" sortOrder="10"/>
</type>
</config>
extensions_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="fiscal_code" type="string" />
</extension_attributes>
</config>
app/code/Lutech/CheckoutAdditionalField/Plugin/FiscalCodePlugin.php
namespace Lutech\CheckoutAdditionalField\Plugin;
use Magento\Checkout\Block\Checkout\LayoutProcessor;
class FiscalCodePlugin {
public function afterProcess(LayoutProcessor $subject, $result) {
$customAttributeCode = 'fiscal_code';
$customField = [
'component' => 'Magento_Ui/js/form/element/abstract',
'config' => [
'customScope' => 'shippingAddress.custom_attributes',
'template' => 'ui/form/field',
'elementTmpl' => 'ui/form/element/input',
'id' => 'fiscal_code',
],
'dataScope' => 'shippingAddress.custom_attributes.fiscal_code',
'label' => 'Fiscal Code',
'provider' => 'checkoutProvider',
'visible' => true,
'validation' => ['required-entry' => true],
'sortOrder' => 150,
'id' => 'fiscal_code',
];
$result['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children'][$customAttributeCode] = $customField;
return $result;
}
}
app/code/Lutech/CheckoutAdditionalFiield/Plugin/SaveAddressInformation.php
<?php
namespace Lutech\CheckoutAdditionalField\Plugin;
class SaveAddressInformation {
protected $quoteRepository;
public function __construct(
\Magento\Quote\Model\QuoteRepository $quoteRepository
) {
$this->quoteRepository = $quoteRepository;
}
/**
* #param \Magento\Checkout\Model\ShippingInformationManagement $subject
* #param $cartId
* #param \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
*/
public function beforeSaveAddressInformation(
\Magento\Checkout\Model\ShippingInformationManagement $subject,
$cartId,
\Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
) {
$shippingAddress = $addressInformation->getShippingAddress();
$shippingAddressExtensionAttributes = $shippingAddress->getExtensionAttributes();
if ($shippingAddressExtensionAttributes) {
$customField = $shippingAddressExtensionAttributes->getFiscalCode();
$shippingAddress->setFiscalCode($customField);
}
}
}
app/code/Lutech/CheckoutAdditionalField/Setup/InstallData.php
<?php
namespace Lutech\CheckoutAdditionalField\Setup;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
class InstallData implements InstallDataInterface {
/**
* Installs data for a module
*
* #param ModuleDataSetupInterface $setup
* #param ModuleContextInterface $context
* #return void
*/
public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context) {
$installer = $setup;
$setup->startSetup();
$connection = $setup->getConnection();
$connection->addColumn(
$installer->getTable('quote_address'),
'fiscal_code',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
'nullable' => true,
'default' => NULL,
'length' => 255,
'comment' => 'Fiscal Code for address'
]
);
$connection->addColumn(
$installer->getTable('sales_order_address'),
'fiscal_code',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
'nullable' => true,
'default' => NULL,
'length' => 255,
'comment' => 'Fiscal Code for address'
]
);
$installer->endSetup();
}
}
app/code/Lutech/CheckoutAdditionalField/view/frontend/requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/action/set-shipping-information': {
'Lutech_CheckoutAdditionalField/js/action/set-shipping-information-mixin': true
}
}
}
};
app/code/Lutech/CheckoutAdditionalField/view/frontend/web/js/action/set-shipping-information.js
/*jshint browser:true jquery:true*/
/*global alert*/
define([
'jquery',
'mage/utils/wrapper',
'Magento_Checkout/js/model/quote'
], function ($, wrapper, quote) {
'use strict';
return function (setShippingInformationAction) {
return wrapper.wrap(setShippingInformationAction, function (originalAction) {
var shippingAddress = quote.shippingAddress();
if (shippingAddress['extension_attributes'] === undefined) {
shippingAddress['extension_attributes'] = {};
}
shippingAddress['extension_attributes']['fiscal_code'] = shippingAddress.customAttributes['fiscal_code'];
// pass execution to original action ('Magento_Checkout/js/action/set-shipping-information')
return originalAction();
});
};
});
I have noticed one thing with PHPStorm.
In SaveAddressInformation.php $shippingAddressExtensionAttributes->getFiscalCode() will be autocompleted, instead of $shippingAddress->setFiscalCode($customField)
Additional Information on my Magento 2 installation
Version used: 2.2.4
I using symfony and have template with array with some entities array, and need create in for in check box for all entity and when checked some entities and click ready go to action with ids(from all check box) - example - taskExecution.id
I don't used symfony form with type entity because taskExecutions complicated DTO, from this DTO i need only id for to send on another action
$taskExecutions = $this->getTaskExecution()
->getTaskExecutionByFilter($form->getData());
return [
'form' => $form->createView(),
'taskExecutions' => $taskExecutions
];
{% for taskExecution in taskExecutions %}
<input class="searchType" type="checkbox" name="SharingNotification" id={{ taskExecution.id }}>
<label class="searchtype2label">{{ taskExecution.id }}</label>
</input>
{% endfor %}
{% javascripts
'#EconomyBundle/Resources/public/js/check-task-executions.js'
filter='?yui_js' combine=true %}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}
add js
$('.searchType').click(function() {
alert($(this).attr('id'));
if(this.checked){
$.ajax({
type: "POST",
url: '/manage/outbound_invoices/task_executions/ids',
data: $(this).attr('id'), //--> send id of checked checkbox on other page
success: function(data) {
alert('it worked');
alert(data);
$('#container').html(data);
},
error: function() {
alert('it broke');
},
complete: function() {
alert('it completed');
}
});
}
});
this my action
/**
* give task executions ids for created row.
*
* #Route("/manage/outbound_invoices/task_executions/ids", name="ids_task_executions_")
* #Method({"POST", "GET"})
*/
public function getIdsTaskExecutionsAction(Request $request)
{
$ids = $request->get('ids');
}
I don't know js, help please for understand how get check box value (1 or 0) and entity id parameter and send to another action
I don't think you need javascript for that. Instead you should have a look to the Symfony doc on how to use a form without data_class
your form will looks like :
<?php
class TaskExecutionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('taskExecution', EntityType::class, array(
'class' => 'AppBundle/TaskExecution',
'expanded' => true,
'multiple' => true
))
->add('submit', SubmitType::class)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'csrf_protection' => false
));
}
/**
* #return string
*/
public function getName()
{
return 'execution_task_type';
}
}
And in your controller:
<?php
/**
* give task executions ids for created row.
*
* #Route("/manage/outbound_invoices/task_executions/ids", name="ids_task_executions_")
* #Method({"POST", "GET"})
*/
public function getIdsTaskExecutionsAction(Request $request)
{
$form = $this->createForm(TaskExecutionType::class, null, array(
'method' => 'POST',
'action' => 'ids_task_executions'
));
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData(); //this will be an array of all the TaskExecution entities you selected
//your own logic
}
return $this->render('template.html.twig', array(
'form' => $form->createView()
));
}