如何使fieldset的字段具有多依赖性?
-
29-09-2020 - |
题
我有一个 场集 在管理面板与一个父选择(有5个选项)和2 字段, ,这应该显示的情况下,父值选择将是3,4或5。我还没有在magento中找到类似逻辑的例子,并试图通过类比通常的依赖来编写,但它不起作用。在我的示例中,依赖字段仅在选择select中值为5的选项时显示,而在选择1、2、3或4时不显示。
完整代码(块示例):
<?php
namespace Siarhey\Test\Block\Adminhtml\Promo\Quote\Edit\Tab;
class Actions extends \Magento\Backend\Block\Widget\Form\Generic implements
\Magento\Backend\Block\Widget\Tab\TabInterface
{
/**
* @param \Magento\Backend\Block\Template\Context $context
* @param \Magento\Framework\Registry $registry
* @param \Magento\Framework\Data\FormFactory $formFactory
* @param array $data
*/
public function __construct(
\Magento\Backend\Block\Template\Context $context,
\Magento\Framework\Registry $registry,
\Magento\Framework\Data\FormFactory $formFactory,
array $data = []
) {
parent::__construct($context, $registry, $formFactory, $data);
}
public function getTabLabel()
{
return __('Actions');
}
public function getTabTitle()
{
return __('Actions');
}
public function canShowTab()
{
return true;
}
public function isHidden()
{
return false;
}
protected function _prepareForm()
{
$model = $this->_coreRegistry->registry('current_promo_quote_rule');
/** @var \Magento\Framework\Data\Form $form */
$form = $this->_formFactory->create();
$form->setHtmlIdPrefix('rule_');
$fieldset = $form->addFieldset(
'action_fieldset',
['legend' => __('Rules')]
);
$parentField = $fieldset->addField(
'simple_action',
'select',
[
'label' => __('Apply'),
'name' => 'simple_action',
'options' => [
1 => __('Amount 1'),
2 => __('Discount 1'),
3 => __('Amount 2'),
4 => __('Discount 2'),
]
]
);
$childFieldOne = $fieldset->addField(
'amount',
'text',
[
'name' => 'amount',
'required' => true,
'class' => 'validate-not-negative-number',
'label' => __('Amount')
]
);
$model->setAmount($model->getAmount() * 1);
$childFieldTwo = $fieldset->addField(
'percent',
'text',
['name' => 'percent', 'label' => __('Percent')]
);
$model->setPercent($model->getPercent() * 1);
$this->setChild(
'form_after',
$this->getLayout()->createBlock(
'Magento\Backend\Block\Widget\Form\Element\Dependence'
)->addFieldMap(
$parentField->getHtmlId(),
$parentField->getName()
)->addFieldMap(
$childFieldOne->getHtmlId(),
$childFieldOne->getName()
)->addFieldMap(
$childFieldTwo->getHtmlId(),
$childFieldTwo->getName()
)->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
'1,3'
)->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
'2,4'
)
);
$form->setValues($model->getData());
if ($model->isReadonly()) {
foreach ($fieldset->getElements() as $element) {
$element->setReadonly(true, true);
}
}
$this->setForm($form);
return parent::_prepareForm();
}
}
结果(查看):
代码示例1(不起作用):
/*
* $parentField is select with values (0,1,2,3,4,5)
*/
$this->setChild(
'form_after',
$this->getLayout()->createBlock(
'Magento\Backend\Block\Widget\Form\Element\Dependence'
)->addFieldMap(
$parentField->getHtmlId(),
$parentField->getName()
)->addFieldMap(
$childFieldOne->getHtmlId(),
$childFieldOne->getName()
)->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
'3'
)->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
'4'
)->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
'5'
)->addFieldMap(
$parentField->getHtmlId(),
$parentField->getName()
)->addFieldMap(
$childFieldTwo->getHtmlId(),
$childFieldTwo->getName()
)->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
'3'
)->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
'4'
)->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
'5'
)
);
代码示例2(不起作用):
/*
* $parentField is select with values (0,1,2,3,4,5)
*/
$this->setChild(
'form_after',
$this->getLayout()->createBlock(
'Magento\Backend\Block\Widget\Form\Element\Dependence'
)->addFieldMap(
$parentField->getHtmlId(),
$parentField->getName()
)->addFieldMap(
$childFieldOne->getHtmlId(),
$childFieldOne->getName()
)->addFieldMap(
$parentField->getHtmlId(),
$parentField->getName()
)->addFieldMap(
$childFieldTwo->getHtmlId(),
$childFieldTwo->getName()
)->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
array('3', '4', '5')
)->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
array('3', '4', '5')
)
);
结果:
通知书:数组到字符串的转换 /var/www/magento2/app/code/Magento/Backend/Block/Widget/Form/Element/Dependency。php的 在95号线
更新资料:
代码示例3(不工作,如果选择的值不是
'3,4,5'
):
// Parent field
$typeField = $fieldset->addField(
'action_type',
'select',
[
'label' => __('Type'),
'name' => 'action_type',
'options' => ['1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '3,4,5' => '3,4,5']
]
);
$this->setChild(
'form_after',
$this->getLayout()->createBlock(
'Magento\Backend\Block\Widget\Form\Element\Dependence'
)->addFieldMap(
$parentField->getHtmlId(),
$parentField->getName()
)->addFieldMap(
$childFieldOne->getHtmlId(),
$childFieldOne->getName()
)->addFieldMap(
$typeField->getHtmlId(),
$typeField->getName()
)->addFieldMap(
$childFieldTwo->getHtmlId(),
$childFieldTwo->getName()
)->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
'3,4,5'
)->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
'3,4,5'
)
);
有人遇到过同样的问题并找到了解决方案吗?
更新资料:
也许别人可以检查这个问题的存在?我已经在3个不同的安装上检查了它,这个解决方案(与值分离的线)仍然不起作用。
解决方案
如果按照文件中的依赖关系检查负责添加相应字段的代码 lib/web/mage/adminhtml/form.js
, ,你会在那里看到下面的方案:
var shouldShowUp = true;
for (var idFrom in valuesFrom) {
var from = $(idFrom);
if (from) {
var values = valuesFrom[idFrom]['values'];
var isInArray = values.indexOf(from.value) != -1;
var isNegative = valuesFrom[idFrom]['negative'];
if (!from || isInArray && isNegative || !isInArray && !isNegative) {
shouldShowUp = false;
}
}
}
如果您设置逗号分隔值,例如:
/** @var \Magento\Backend\Block\Widget\Form\Element\Dependence $blockDependence */
$blockDependence->addFieldMap(
$actionType->getHtmlId(),
$actionType->getName()
)->addFieldMap(
$amountField->getHtmlId(),
$amountField->getName()
)->addFieldDependence(
$amountField->getName(),
$actionType->getName(),
implode(',', array(
Rule::ACTION_TYPE_OVERWRITE_COST,
Rule::ACTION_TYPE_ADD_SURCHARGE,
Rule::ACTION_TYPE_ENABLE_SM_AND_OVERWRITE_COST
))
);
然后在调试的时候你会看到 indexOf
试图在一个元素数组中找到现有的值,在你的情况下,是一个逗号分隔的值。找不到此元素:
一步一步的输出 console.log
从方法:
console.log(values);
console.log('Value: '+from.value);
console.log('Is in array: '+isInArray);
要创建字段多依赖项,您可以使用相同的昏迷分隔值,但需要进行一些修改。你只需要块,这将扩展 \Magento\Backend\Block\Widget\Form\Element\Dependence
:
<?php
namespace Vendor\Module\Block\Widget\Form\Element;
/**
* Form element dependencies mapper
* Assumes that one element may depend on other element values.
* Will toggle as "enabled" only if all elements it depends from toggle as true.
*/
class Dependence extends \Magento\Backend\Block\Widget\Form\Element\Dependence
{
/**
* @param \Magento\Backend\Block\Context $context
* @param \Magento\Framework\Json\EncoderInterface $jsonEncoder
* @param \Magento\Config\Model\Config\Structure\Element\Dependency\FieldFactory $fieldFactory
* @param array $data
*/
public function _construct(
\Magento\Backend\Block\Context $context,
\Magento\Framework\Json\EncoderInterface $jsonEncoder,
\Magento\Config\Model\Config\Structure\Element\Dependency\FieldFactory $fieldFactory,
array $data = []
)
{
parent::_construct($context, $jsonEncoder, $fieldFactory, $data);
}
/**
* {@inheritdoc}
*/
protected function _toHtml()
{
if (!$this->_depends) {
return '';
}
return '<script>
require(["uiRegistry", "mage/adminhtml/form"], function(registry) {
var controller = new FormElementDependenceController(' . $this->_getDependsJson() .
($this->_configOptions ? ', ' .
$this->_jsonEncoder->encode(
$this->_configOptions
) : '') . ');
registry.set("formDependenceController", controller);
});</script>';
}
/**
* Field dependences JSON map generator * @return string
*/
protected function _getDependsJson()
{
$result = [];
foreach ($this->_depends as $to => $row) {
foreach ($row as $from => $field) {
$values = $this->_prepareValues($field->getValues());
/** @var $field \Magento\Config\Model\Config\Structure\Element\Dependency\Field */
$result[$this->_fields[$to]][$this->_fields[$from]] = [
'values' => $values,
'negative' => $field->isNegative(),
];
}
}
return $this->_jsonEncoder->encode($result);
}
/**
* @param $values
* @return array
*/
protected function _prepareValues($values)
{
if (!is_array($values)) {
return $values;
}
$result = array();
foreach ($values as $value) {
if (stripos($value, ',')) {
$result += explode(',', $value);
} else {
$result += $value;
}
}
return $result;
}
}
如您所见,该值被强制更改为1值数组。
主要问题在于 addFieldDependence
类的 \Magento\Backend\Block\Widget\Form\Element\Dependence
:
重点是值(依赖项中的行)作为数组的唯一元素传输。 indexOf
尝试查找相应所选选项的值,但无法找到完全匹配项。结果,它返回'false'。
也没有办法将值作为数组传输,因为PHP返回 Notice: Array to string conversion
因为 'value' => (string)$refField
.
在我们的示例中,我们将一个元素数组重新创建为一个多元素数组,其中每个元素由多个依赖项组成。
你的依赖的代码应该被修改(你需要改变块)。这是如何:
// Dependency START
/** @var \Magento\Backend\Block\Widget\Form\Element\Dependence $blockDependence */
$blockDependence = $this->getLayout()->createBlock(
// 'Magento\Backend\Block\Widget\Form\Element\Dependence'
'{Vendor}\{Module}\Block\Widget\Form\Element\Dependence'
);
$blockDependence->addFieldMap(
$parentField->getHtmlId(),
$parentField->getName()
)->addFieldMap(
$childFieldOne->getHtmlId(),
$childFieldOne->getName()
)->addFieldMap(
$childFieldTwo->getHtmlId(),
$childFieldTwo->getName()
)->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
'1,3'
)->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
'2,4'
);
$this->setChild('form_after', $blockDependence);
// Dependency END
结果应该是这样的:
UPD,UPD
如果您确定将来将使用逗号分隔值,最好的方法是添加const UNIQUE_DELIMITER
与类的分隔符的所需值 Vendor\Module\Block\Widget\Form\Element\Dependence
:例如。
const UNIQUE_DELIMITER = '~#!~';
接下来,修改分区方法:
/**
* @param $values
* @return array
*/
protected function _prepareValues($values)
{
if (!is_array($values)) {
return $values;
}
$result = array();
foreach ($values as $value) {
if (stripos($value, self::UNIQUE_DELIMITER)) {
$result += explode(self::UNIQUE_DELIMITER, $value);
} else {
$result += $value;
}
}
return $result;
}
然后,使用 Vendor\Module\Block\Widget\Form\Element\Dependence::UNIQUE_DELIMITER
在你的班上 Actions
.
为了您的方便,添加类 Dependence
(命名空间之后):
use Vendor\Module\Block\Widget\Form\Element\Dependence;
并以这种方式编写代码:
// Dependency START
/** @var \Magento\Backend\Block\Widget\Form\Element\Dependence $blockDependence */
$blockDependence = $this->getLayout()->createBlock(
// 'Magento\Backend\Block\Widget\Form\Element\Dependence'
'{Vendor}\{Module}\Block\Widget\Form\Element\Dependence'
);
$childFieldOneToParentValues = implode(Dependence::UNIQUE_DELIMITER, array('1','3'));
$childFieldTwoToParentValues = implode(Dependence::UNIQUE_DELIMITER, array('2','4'));
$blockDependence->addFieldMap(
$parentField->getHtmlId(),
$parentField->getName()
)->addFieldMap(
$childFieldOne->getHtmlId(),
$childFieldOne->getName()
)->addFieldMap(
$childFieldTwo->getHtmlId(),
$childFieldTwo->getName()
)->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
$childFieldOneToParentValues
)->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
$childFieldTwoToParentValues
);
$this->setChild('form_after', $blockDependence);
// Dependency END
其他提示
我可能是错的,但不幸的是,我不认为你可以与默认 Magento\Backend\Block\Widget\Form\Element\Dependence
类。
让我解释一下:
该 addFieldDependence
方法看起来像这样:
public function addFieldDependence($fieldName, $fieldNameFrom, $refField)
{
if (!is_object($refField)) {
/** @var $refField \Magento\Config\Model\Config\Structure\Element\Dependency\Field */
$refField = $this->_fieldFactory->create(
['fieldData' => ['value' => (string)$refField], 'fieldPrefix' => '']
);
}
$this->_depends[$fieldName][$fieldNameFrom] = $refField;
return $this;
}
所以假设你试试这个代码:
addFieldDependence($child,$parent,'2,4')
该 value
的 $refField
将是以下字符串: 2,4
所以在你的选择中没有这样的价值,它永远不会起作用。
如果您尝试此代码:
addFieldDependence($child,$parent,array('2,4'))
你会得到 Array to string conversion
错误,因为 (string)$refField
密码
如果您尝试此代码:
addFieldDependence($child,$parent,'2')->addFieldDependence($child,$parent,'4')
第一次调用将设置 $refField
使用值2并使用以下代码将其分配给依赖项:
$this->_depends[$fieldName][$fieldNameFrom] = $refField;
但是,第二个代码将复盖该依赖项,因为 $fieldName
和 $fieldNameFrom
变量与第一次调用期间相同。
你有什么解决方案?
- 使用首选项或插件来修改
Magento\Backend\Block\Widget\Form\Element\Dependence
班级
这里要查看的重要方法是 addFieldDependence
和 _getDependsJson
.这里的问题是,有很多机会,你也可能不得不修改JavaScript FormElementDependenceController
处理依赖关系的类。
- 使用多个名称不同的重复字段:嗯,这是肮脏的,但我认为这将工作。
例子::
$parentField = $fieldset->addField(
'simple_action',
'select',
[
'label' => __('Apply'),
'name' => 'simple_action',
'options' => [
1 => __('Amount 1'),
2 => __('Discount 1'),
3 => __('Amount 2'),
4 => __('Discount 2'),
]
]
);
$childFieldOne = $fieldset->addField(
'amount',
'text',
[
'name' => 'amount',
'required' => true,
'class' => 'validate-not-negative-number',
'label' => __('Amount')
]
);
$childFieldOneCopy = $fieldset->addField(
'amount',
'text',
[
'name' => 'amount',
'required' => true,
'class' => 'validate-not-negative-number',
'label' => __('Amount')
]
);
然后使用:
->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
'1'
)
->addFieldDependence(
$childFieldOneCopy->getName(),
$parentField->getName(),
'3'
)
这里的问题是,您必须在处理数据的控制器中添加几个检查,以确保您正在处理正确的数据,而不是隐藏的副本字段。
我认为你应该看起来很有教养 Magento\Backend\Block\Widget\Form\Element\Dependence
.您可以创建从这个类继承的自己的块,并按您想要的方式重写它。在你的代码替换块调用:
$this->getLayout()->createBlock(
'Magento\Backend\Block\Widget\Form\Element\Dependence'
)
到:
this->getLayout()->createBlock(
'Siarhey\Test\Block\Widget\Form\Element\Dependence'
)
创建块 Siarhey\Test\Block\Widget\Form\Element\Dependence
你可以在其中实现你的验证逻辑。
这只是建议。我希望它能帮助你。
创建一个di。adminhtml下的xml并添加以下代码:
基本上需要复盖 Magento\Backend\Block\Widget\Form\Element\Dependency 班级
<preference for="Magento\Backend\Block\Widget\Form\Element\Dependence"
type="Vendor\Module\Block\Widget\Form\Element\Dependence" />
namespace Vendor\Module\Block\Widget\Form\Element;
class Dependence extends \Magento\Backend\Block\Widget\Form\Element\Dependence
{
/**
* Register field name dependence one from each other by specified values
*
* @param string $fieldName
* @param string $fieldNameFrom
* @param \Magento\Config\Model\Config\Structure\Element\Dependency\Field|string $refField
* @return \Magento\Backend\Block\Widget\Form\Element\Dependence
*/
public function addFieldDependence($fieldName, $fieldNameFrom, $refField)
{
if (!is_object($refField)) {
/** @var $refField \Magento\Config\Model\Config\Structure\Element\Dependency\Field */
$refField = $this->_fieldFactory->create(
['fieldData' => ['value' => (string)$refField, 'separator' => ','], 'fieldPrefix' => '']
);
}
$this->_depends[$fieldName][$fieldNameFrom] = $refField;
return $this;
}
}
现在您可以使用以下方式。
->addFieldDependence(
$childFieldTwo->getName(),
$parentField->getName(),
'2,4'
)
清除Magento2缓存。
我不确定这一点,我没有测试过,但看着 addFieldDependence
方法和在 \Magento\Config\Model\Config\Structure\Element\Dependency\Field
类我想它可能会工作。
将此添加到您的类中:
protected $fieldFactory;
public function __construct(
....
\Magento\Config\Model\Config\Structure\Element\Dependency\FieldFactory $fieldFactory,
....
) {
$this->fieldFactory = $fieldFactory;
}
然后,而不是
->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
'1,3'
)
试试这个:
$someField = $this->fieldFactory()->create([
'fieldData' => [
'separator' => ',',
'value' => '1,3',
],
'fieldPrefix' => ''
]);
->addFieldDependence(
$childFieldOne->getName(),
$parentField->getName(),
$someField
)
定义 $this->_fieldFactory
试试下面的一个,它对我有用:
$blockDependence = $this->getLayout()->createBlock(
'Magento\Backend\Block\Widget\Form\Element\Dependence'
);
$filter = $this->_fieldFactory->create([
'fieldData' => [
'separator' => ',',
'value' => '0,1',
],
'fieldPrefix' => '',
]);
$blockDependence->addFieldMap(
"discount_type",
'discount_type'
)->addFieldMap(
"discount",
'discount'
)->addFieldDependence(
'discount',
'discount_type',
$filter
);
$this->setChild('form_after', $blockDependence);