Magento 2.4.6-p6
I am trying to create a button on Product edit form which can translate one store product attribute data and store into another store attribute data.
‘Vendor/ModuleName/view/adminhtml/ui_component/product_form.xml‘
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<settings>
<buttons>
<button name="Translate" class="VendorModuleNameBlockAdminhtmlProductEditButtonTranslateFrom"/>
</buttons>
</settings>
</form>
‘Vendor/ModuleName/Block/Adminhtml/Product/Edit/Button/TranslateFrom.php‘
<?php
namespace VendorModuleNameBlockAdminhtmlProductEditButton;
use MagentoCatalogBlockAdminhtmlProductEditButtonGeneric;
class TranslateFrom extends Generic
{
/**
* {@inheritdoc}
*/
public function getButtonData(): array
{
return [
'label' => __('Translate From'),
'class' => 'action-secondary',
'data_attribute' => [
'mage-init' => [
'Magento_Ui/js/form/button-adapter' => [
'actions' => [
[
'targetName' => 'product_form.product_form.translate_product_from',
'actionName' => 'toggleModal'
],
[
'targetName' => 'product_form.product_form.translate_product_from.translate_from_store_form',
'actionName' => 'render'
]
]
]
]
],
'on_click' => '',
'sort_order' => 20
];
}
}
‘etc/adminhtml/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">
<virtualType name="MagentoCatalogUiDataProviderProductFormModifierPool" type="MagentoUiDataProviderModifierPool">
<arguments>
<argument name="modifiers" xsi:type="array">
<item name="translateFromModal" xsi:type="array">
<item name="class" xsi:type="string">VendorModuleNameUiDataProviderProductFormModifierTranslateFromModal</item>
<item name="sortOrder" xsi:type="number">170</item>
</item>
</argument>
</arguments>
</virtualType>
</config>
‘TranslateFromModal.php’
<?php
namespace VendorModuleNameUiDataProviderProductFormModifier;
use MagentoCatalogUiDataProviderProductFormModifierAbstractModifier;
use MagentoCatalogModelLocatorLocatorInterface;
use MagentoFrameworkAuthorizationInterface;
use MagentoFrameworkExceptionNoSuchEntityException;
use MagentoFrameworkRegistry;
use MagentoFrameworkUrlInterface;
use MagentoStoreModelStoreManagerInterface;
use MagentoUiComponentContainer;
use MagentoUiComponentFormField;
use MagentoUiComponentFormFieldset;
use MagentoUiComponentModal;
class TranslateFromModal extends AbstractModifier
{
const GROUP_TRANSLATE_FROM = 'translate_product_from';
/**
* @var UrlInterface
*/
protected $urlBuilder;
/**
* @var Registry
*/
protected $registry;
/**
* @var LocatorInterface
*/
protected $locator;
/**
* @var AuthorizationInterface
*/
protected $authorization;
/**
* @var StoreManagerInterface
*/
protected $storeManager;
/**
* @param UrlInterface $urlBuilder
* @param Registry $registry
* @param AuthorizationInterface $authorization
* @param LocatorInterface $locator
* @param StoreManagerInterface $storeManager
*/
public function __construct(
UrlInterface $urlBuilder,
Registry $registry,
AuthorizationInterface $authorization,
LocatorInterface $locator,
StoreManagerInterface $storeManager
) {
$this->urlBuilder = $urlBuilder;
$this->registry = $registry;
$this->authorization = $authorization;
$this->locator = $locator;
$this->storeManager = $storeManager;
}
/**
* {@inheritdoc}
*/
public function modifyData(array $data)
{
return $data;
}
/**
* {@inheritdoc}
*/
public function modifyMeta(array $meta)
{
$meta = $this->addTranslateFromModal($meta);
return $meta;
}
/**
* Add Translate From modal to the meta.
*
* @param array $meta
* @return array
*/
protected function addTranslateFromModal(array $meta)
{
$meta[static::GROUP_TRANSLATE_FROM] = [
'arguments' => [
'data' => [
'config' => [
'isTemplate' => false,
'componentType' => Modal::NAME,
'dataScope' => '',
'provider' => 'product_form.product_form_data_source',
'options' => [
'title' => __('Translate From'),
'buttons' => [
[
'text' => __('Cancel'),
'actions' => [
[
'targetName' => '${ $.name }',
'__disableTmpl' => ['targetName' => false],
'actionName' => 'closeModal'
]
]
],
[
'text' => __('Translate'),
'class' => 'action-primary',
'actions' => [
[
'targetName' => '${ $.name }.translate_from_store_form',
'__disableTmpl' => ['targetName' => false],
'actionName' => 'save'
],
[
'closeModal'
]
]
]
],
],
],
],
],
'children' => [
'translate_from_store_form' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Container::NAME,
'label' => __('Translate From Form'),
'dataScope' => '',
'autoRender' => false,
'ns' => 'translate_from_store_form',
'externalProvider' => 'translate_from_store_form.translate_from_store_form_data_source',
'toolbarContainer' => '${ $.parentName }',
'__disableTmpl' => ['toolbarContainer' => false],
'formSubmitType' => 'ajax',
'saveUrl' => $this->urlBuilder->getUrl('translate/product/translateFrom'),
'productId' => $this->locator->getProduct()->getId(),
'currentStoreId' => $this->locator->getStore()->getId(),
'productType' => $this->locator->getProduct()->getTypeId(),
],
],
],
'children' => [
'select_store' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Fieldset::NAME,
'label' => __('Select Store'),
'dataScope' => '',
]
]
],
'children' => [
'current_store' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Field::NAME,
'formElement' => 'input',
'dataScope' => 'current_store',
'dataType' => 'text',
'label' => __('Current Store'),
'value' => $this->getCurrentStoreName(),
'disabled' => true
]
]
]
],
'store_id' => [
'arguments' => [
'data' => [
'config' => [
'componentType' => Field::NAME,
'label' => __('Store'),
'formElement' => 'select',
'dataScope' => 'store_id',
'dataType' => 'int',
'options' => $this->getStoreOptions(),
'validation' => [
'required-entry' => true
],
]
]
]
]
]
]
]
]
]
];
return $meta;
}
/**
* Retrieve the current store name.
*
* @return string
* @throws NoSuchEntityException
*/
protected function getCurrentStoreName(): string
{
$storeId = $this->locator->getStore()->getId();
$store = $this->storeManager->getStore($storeId);
return $store->getName();
}
/**
* Retrieve the store options for the select field, excluding the current store.
*
* @return array
*/
protected function getStoreOptions(): array
{
$stores = $this->storeManager->getStores();
$currentStoreId = $this->locator->getStore()->getId();
$options = [];
foreach ($stores as $store) {
if ($store->getId() != $currentStoreId) {
$options[] = ['value' => $store->getId(), 'label' => $store->getName()];
}
}
return $options;
}
}
‘Vendor/ModuleName/Controller/Adminhtml/Product/TranslateFrom.php’
<?php
namespace VendorModuleNameControllerAdminhtmlProduct;
use MagentoBackendAppAction;
use MagentoBackendAppActionContext;
use MagentoFrameworkControllerResultJsonFactory;
class TranslateFrom extends Action
{
/**
* @var JsonFactory
*/
protected $resultJsonFactory;
/**
* @param Context $context
* @param JsonFactory $resultJsonFactory
*/
public function __construct(
Context $context,
JsonFactory $resultJsonFactory
) {
parent::__construct($context);
$this->resultJsonFactory = $resultJsonFactory;
}
/**
* Execute method
*
* @return MagentoFrameworkControllerResultJson
*/
public function execute()
{
$resultJson = $this->resultJsonFactory->create();
// Retrieve the parameters
$storeId = $this->getRequest()->getParam('store_id');
$productId = $this->getRequest()->getParam('product_id');
// Your translation logic here
$response = [
'success' => true,
'message' => __('Translation successful for store ID %1 and product ID %2', $storeId, $productId)
];
return $resultJson->setData($response);
}
/**
* Check if the user has the permission to access this controller
*
* @return bool
*/
protected function _isAllowed()
{
return $this->_authorization->isAllowed('Paket24_TranslateProduct::translate');
}
}
Error:
registry.js:57 Uncaught TypeError: Cannot read properties of undefined (reading 'apply')
at registry.js:57:35
at Registry._resolveRequest (registry.js:416:30)
at Registry._addRequest (registry.js:383:22)
at Registry.get (registry.js:227:18)
at async (registry.js:56:22)
at UiClass.triggerAction (modal-component.js:305:24)
at modal-component.js:338:25
at Array.forEach (<anonymous>)
at $.<computed>.<computed>.<anonymous> (modal-component.js:336:25)
at executeBound (underscore.js:986:71)
Any help would be appreciated. Thanks 🙂