Pregunta

tengo un conjunto de campos en el panel de administración con una selección de padres (tiene 5 opciones) y 2 campos, que debería mostrarse en caso de que el valor principal seleccionado sea 3, 4 o 5.No encontré ejemplos de lógica similar en magento y traté de escribir por analogía con la dependencia habitual, pero no funciona.En mi ejemplo, los campos dependientes se muestran solo al elegir las opciones con el valor 5 de la selección y no se muestran al elegir 1, 2, 3 o 4.

Código completo (ejemplo de bloque):

<?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();
    }
}

Resultado (ver):

Option 1
Opción 1 seleccionada

Option 4 Opción 4 seleccionada

Without dependency Sin dependencia

Ejemplo de código 1 (no funciona):

/*
 * $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'
    )
);

Ejemplo de código 2 (no funciona):

/*
 * $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')
    )
);

Resultado:

Aviso:Array a la conversión de cadena en /var/www/magento2/app/code/magento/backend/block/widget/form/element/dependence.php en la línea 95

ACTUALIZAR:


Ejemplo de código 3 (no funciona si el valor seleccionado no es '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'
    )
);

¿Alguien se ha enfrentado al mismo problema y ha encontrado una solución?

Actualizar:

¿Quizás alguien más pueda verificar la presencia de este problema?Lo he comprobado en 3 instalaciones diferentes y esta solución (la línea con los valores separados por comas) todavía no funciona.

¿Fue útil?

Solución

Si marca el código que se encarga de agregar los campos correspondientes de acuerdo con las dependencias en el archivo lib/web/mage/adminhtml/form.js, verá el siguiente esquema allí:

    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;
            }
        }
    }

En caso de que establezca valores separados por comas, por ejemplo:

    /** @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
        ))
    );

luego, mientras depuras, verás que indexOf está tratando de encontrar el valor existente en la matriz de un elemento que es, en su caso, un valor separado por comas.Este elemento no se puede encontrar:

enter image description here

Una salida paso a paso de console.log del método:

console.log(values);
console.log('Value: '+from.value);
console.log('Is in array: '+isInArray);

Para crear campos multidependientes, puede utilizar el mismo valor separado por comas, pero con algunas modificaciones.Sólo necesitarás el bloque, que se extenderá \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;
    }
}

Como puede ver, el valor está FORZADO a cambiarse a una matriz de 1 valor.

El principal problema radica en la addFieldDependence de la clase \Magento\Backend\Block\Widget\Form\Element\Dependence:

El punto es que el valor (la línea de la dependencia) se transfiere como el único elemento de la matriz. indexOf intenta encontrar el valor de la opción elegida correspondiente, pero no logra localizar la coincidencia exacta.Como resultado, devuelve "falso".

Tampoco hay forma de transferir los valores como una matriz, ya que PHP devuelve el Notice: Array to string conversion debido a la conversión de 'value' => (string)$refField.

En nuestro ejemplo, hemos recreado la matriz de un elemento en una de varios elementos, donde cada elemento consta de varias dependencias.

El código de tu dependencia debe modificarse (deberás cambiar el bloque).Así es como:

// 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

El resultado debería verse así:

enter image description here

UPD

Si está seguro de que utilizará un valor separado por comas en el futuro, la mejor manera será agregar la constante UNIQUE_DELIMITER con el valor requerido del delimitador de la clase Vendor\Module\Block\Widget\Form\Element\Dependence:P.ej.

const UNIQUE_DELIMITER = '~#!~';

A continuación, modifique el método de partición:

/**
 * @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;
}

Entonces, usa Vendor\Module\Block\Widget\Form\Element\Dependence::UNIQUE_DELIMITER en tu clase Actions.

Para su comodidad, agregue la clase Dependence (después del espacio de nombres):

use Vendor\Module\Block\Widget\Form\Element\Dependence;

Y escribe el código de esta manera:

 // 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

Otros consejos

Puede que me equivoque, pero lamentablemente no creo que puedas hacerlo con el valor predeterminado. Magento\Backend\Block\Widget\Form\Element\Dependence clase.

Dejame explicar:

El addFieldDependence El método se ve así:

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;
}

Entonces digamos que pruebas este código:

addFieldDependence($child,$parent,'2,4')

El value del $refField será la siguiente cadena: 2,4 Entonces, como no existe tal valor en su selección, nunca funcionará.

Si pruebas este código:

addFieldDependence($child,$parent,array('2,4'))

Obtendrás el Array to string conversion error debido a la (string)$refField código

Si pruebas este código:

addFieldDependence($child,$parent,'2')->addFieldDependence($child,$parent,'4')

La primera llamada establecerá el $refField con el valor 2 y asígnalo a las dependencias usando el siguiente código:

$this->_depends[$fieldName][$fieldNameFrom] = $refField;

Sin embargo, el segundo código sobrescribirá esa dependencia porque el $fieldName y $fieldNameFrom Las variables son las mismas que durante la primera llamada.

¿Qué soluciones tienes?

  • Usar preferencias o complementos para modificar el comportamiento del Magento\Backend\Block\Widget\Form\Element\Dependence clase

Los métodos importantes a considerar aquí son addFieldDependence y _getDependsJson.El problema aquí es que hay muchas posibilidades de que también tengas que modificar el JavaScript. FormElementDependenceController clase que maneja las dependencias.

  • Utilice varios campos duplicados con nombres diferentes:Bueno, esto está sucio pero creo que funcionaría.

Ejemplo:

    $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')
        ]
    );

Luego usa:

->addFieldDependence(
            $childFieldOne->getName(),
            $parentField->getName(),
            '1'
        )    
->addFieldDependence(
            $childFieldOneCopy->getName(),
            $parentField->getName(),
            '3'
        )

El problema aquí es que tendrá que agregar varias comprobaciones en el controlador que procesa los datos para asegurarse de que está procesando los datos correctos y no el campo de copia oculto.

Creo que deberías lucir clase. Magento\Backend\Block\Widget\Form\Element\Dependence.Puedes crear tu propio bloque heredado de esta clase y reescribirlo como quieras.En su código reemplaza la llamada de bloque:

$this->getLayout()->createBlock(
    'Magento\Backend\Block\Widget\Form\Element\Dependence'
)

a:

this->getLayout()->createBlock(
    'Siarhey\Test\Block\Widget\Form\Element\Dependence'
)

Crear bloque Siarhey\Test\Block\Widget\Form\Element\Dependence y puedes implementar en él tu lógica de verificación.

Es sólo un consejo.Espero que te ayude.

Crear un di.xml en adminhtml y agregar código siguiente:

básicamente necesita sobrescribir magento \ backend \ block \ widget \ formulent \ element \ dependence clase


<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;
    }
}

Ahora puede usar la siguiente manera.


->addFieldDependence(
    $childFieldTwo->getName(),
    $parentField->getName(),
    '2,4'
)

Clear Magento2 Cache.

No estoy seguro de esto, no lo he probado, pero mirando el addFieldDependence método y en \Magento\Config\Model\Config\Structure\Element\Dependency\Field clase, creo que podría funcionar.

Agrega esto en tu clase:

protected $fieldFactory;
public function __construct(
   ....
   \Magento\Config\Model\Config\Structure\Element\Dependency\FieldFactory $fieldFactory,
   ....
) {
    $this->fieldFactory = $fieldFactory;
}

Entonces, en lugar de

->addFieldDependence(
    $childFieldOne->getName(),
    $parentField->getName(),
    '1,3'
)

Prueba esto:

$someField = $this->fieldFactory()->create([
    'fieldData' => [
         'separator' => ',',
          'value' => '1,3',
    ],
    'fieldPrefix' => ''
]);

->addFieldDependence(
    $childFieldOne->getName(),
    $parentField->getName(),
    $someField
)

Defina $this->_fieldFactory y prueba por debajo de uno, funciona para mí:

    $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);

Licenciado bajo: CC-BY-SA con atribución
No afiliado a magento.stackexchange
scroll top