Question

j'ai un ensemble de champs dans le panneau d'administration avec une sélection de parent (a 5 options) et 2 des champs, qui doit être affiché au cas où la valeur parent sélectionnée serait 3, 4 ou 5.Je n'ai pas trouvé d'exemples de logique similaire dans magento et j'ai essayé d'écrire par analogie avec la dépendance habituelle, mais ça ne marche pas.Dans mon exemple, les champs dépendants sont affichés uniquement lors du choix des options avec la valeur 5 lors de la sélection et ne sont pas affichés lors du choix de 1, 2, 3 ou 4.

Code complet (exemple de bloc) :

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

Résultat (vue) :

Option 1
Option 1 sélectionnée

Option 4 Option 4 sélectionnée

Without dependency Sans dépendance

Exemple de code 1 (ne fonctionne pas) :

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

Exemple de code 2 (ne fonctionne pas) :

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

Résultat:

Avis:Conversion du tableau en chaîne dans /var/www/magento2/app/code/magento/backend/block/widget/form/element/dependence.php sur la ligne 95

MISE À JOUR:


Exemple de code 3 (ne fonctionne pas si la valeur sélectionnée n'est pas '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'
    )
);

Quelqu'un a-t-il rencontré le même problème et trouvé une solution ?

Mise à jour:

Peut-être que quelqu'un d'autre peut vérifier la présence de ce problème ?Je l'ai vérifié sur 3 installations différentes et cette solution (la ligne avec les valeurs séparées par des virgules) ne fonctionne toujours pas.

Était-ce utile?

La solution

Si vous cochez le code qui se charge d'ajouter les champs correspondants conformément aux dépendances dans le fichier lib/web/mage/adminhtml/form.js, vous y verrez le schéma suivant :

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

Si vous définissez des valeurs séparées par des virgules, par exemple :

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

puis lors du débogage, vous verrez que indexOf essaie de trouver la valeur existante dans le tableau à un élément qui est, dans votre cas, une valeur séparée par des virgules.Cet élément est introuvable :

enter image description here

Une sortie étape par étape de console.log de la méthode :

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

Pour créer des champs multi-dépendances, vous pouvez utiliser la même valeur séparée par des virgules, mais avec quelques modifications.Vous aurez juste besoin du bloc, qui s'étendra \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;
    }
}

Comme vous pouvez le voir, la valeur est FORCÉE pour être transformée en un tableau à 1 valeur.

Le principal problème réside dans addFieldDependence de la classe \Magento\Backend\Block\Widget\Form\Element\Dependence:

Le fait est que la valeur (la ligne de la dépendance) est transférée comme seul élément du tableau. indexOf essaie de trouver la valeur de l'option choisie correspondante, mais ne parvient pas à localiser la correspondance exacte.En conséquence, il renvoie « faux ».

Il n'y a également aucun moyen de transférer les valeurs sous forme de tableau, car PHP renvoie le Notice: Array to string conversion à cause de la conversion de 'value' => (string)$refField.

Dans notre exemple, nous avons recréé le tableau à un élément en un tableau à plusieurs éléments, où chaque élément est constitué de plusieurs dépendances.

Le code de votre dépendance doit être modifié (il faudra changer de bloc).Voici comment :

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

Le résultat devrait ressembler à ceci :

enter image description here

MISE À JOUR

Si vous êtes sûr d'utiliser une valeur séparée par des virgules à l'avenir, la meilleure façon sera d'ajouter la const UNIQUE_DELIMITER avec la valeur requise du délimiteur de la classe Vendor\Module\Block\Widget\Form\Element\Dependence:Par exemple.

const UNIQUE_DELIMITER = '~#!~';

Ensuite, modifiez la méthode de partition :

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

Ensuite, utilisez Vendor\Module\Block\Widget\Form\Element\Dependence::UNIQUE_DELIMITER Dans votre classe Actions.

Pour votre commodité, ajoutez la classe Dependence (après l'espace de noms) :

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

Et écrivez le code de cette façon :

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

Autres conseils

Je me trompe peut-être mais malheureusement, je ne pense pas que vous puissiez le faire avec la valeur par défaut Magento\Backend\Block\Widget\Form\Element\Dependence classe.

Laisse-moi expliquer:

Le addFieldDependence la méthode ressemble à ceci :

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

Supposons que vous essayiez ce code :

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

Le value de la $refField sera la chaîne suivante : 2,4 donc comme il n'y a pas une telle valeur dans votre sélection, cela ne fonctionnera jamais.

Si vous essayez ce code :

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

Vous obtiendrez le Array to string conversion erreur à cause du (string)$refField code

Si vous essayez ce code :

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

Le premier appel définira le $refField avec la valeur 2 et affectez-le aux dépendances à l'aide du code suivant :

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

Cependant, le deuxième code écrasera cette dépendance car le $fieldName et $fieldNameFrom les variables sont les mêmes que lors du premier appel.

Quelles solutions avez-vous ?

  • Utilisez des préférences ou des plugins pour modifier le comportement du Magento\Backend\Block\Widget\Form\Element\Dependence classe

Les méthodes importantes à examiner ici sont addFieldDependence et _getDependsJson.Le problème ici est qu'il y a de fortes chances que vous deviez également modifier le code JavaScript. FormElementDependenceController classe qui gère les dépendances.

  • Utilisez plusieurs champs en double avec des noms différents :eh bien, c'est sale mais je pense que cela fonctionnerait.

Exemple:

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

Utilisez ensuite :

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

Le problème ici est que vous devrez ajouter plusieurs vérifications dans le contrôleur qui traite les données pour garantir que vous traitez les bonnes données et non le champ de copie masquée.

Je pense que tu devrais avoir l'air classe Magento\Backend\Block\Widget\Form\Element\Dependence.Vous pouvez créer votre propre bloc hérité de cette classe et le réécrire comme vous le souhaitez.Dans votre code remplace l'appel de bloc :

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

à:

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

Créer un bloc Siarhey\Test\Block\Widget\Form\Element\Dependence et vous pouvez y implémenter votre logique de vérification.

Ce n'est qu'un conseil.J'espère que cela vous aidera.

Créez un di.xml sous adminhtml et ajoutez le code suivant :

En gros, il faut écraser Magento\Backend\Block\Widget\Form\Element\Dépendance classe


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

Vous pouvez maintenant utiliser la méthode suivante.


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

Videz le cache Magento2.

Je n'en suis pas sûr, je ne l'ai pas testé, mais en regardant le addFieldDependence méthode et à \Magento\Config\Model\Config\Structure\Element\Dependency\Field cours, je pense que ça pourrait marcher.

Ajoutez ceci dans votre classe :

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

Ensuite, au lieu de

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

Essaye ça:

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

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

Définir $this->_fieldFactory et essayez-en un ci-dessous, cela fonctionne pour moi :

    $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);
Licencié sous: CC-BY-SA avec attribution
Non affilié à magento.stackexchange
scroll top