Yii2:如何将属性中的 CSV 字符串映射到表单中的 CheckboxList?

Yii2: How to map a CSV string in an attribute to CheckboxList in a form?

我有一个模型,其属性包含 CSV 字符串。

(该模型实际上是一个ActiveRecord对象,但我想这并不重要。如果我错了请纠正我。)

/**
 * @property string $colors Can be something like "red" or "red,green,blue" or ""
 */
class Product extends Model {        
}

我有一个表单,我想在其中将此属性显示为 checkboxList,这样用户就可以 select 通过简单的单击而不是在文本输入中键入可能的值。

理论上,它看起来应该类似于:

<?php $availableColors = ['red' => 'Red', 'green' => 'Green', 'blue' => 'Blue']; ?>

<?php $form = ActiveForm::begin([]); ?>
    <?= $form->field($model, 'colors')->checkboxList($availableColors) ?>
<?php ActiveForm::end(); ?>

这显然行不通,因为字段 colors 需要是一个数组。但在我的模型中它是一个字符串。

实现该目标的好方法是什么?用JS还是伪属性? colors 属性不得更改,因为它已在不应修改的其他上下文中使用。

我认为这是一个 PHP 问题,但无论如何您都可以使用 PHP explode 来构建您需要的数组。有关详细信息,请参阅 here,然后使用 checkboxList

中的数组

CSV 是一种文件格式,用于在本机以不兼容格式运行的程序之间移动表格数据。将它用作模型属性不是很优雅(说得好听)。在我看来,您应该开始将颜色存储在数组中。

也就是说,您可以使用模型中的 beforeValidate() 函数将数组数据从下拉列表转换为 CSV:

public function beforeValidate() {
    $this->colors = explode(';', $this->colors);

    return parent::beforeValidate();
}

您可以重写模型中的 beforeValidate 方法,将您的颜色数组 implode 转换为字符串。在您看来,您可以使用以下内容:

<?= $form->field($model, 'colors')->checkboxList($availableColors, 
       [
            'item'=> function ($index, $label, $name, $checked, $value) use ($model) {
                $colors = explode(';', $model->colors);
                $checked = in_array($value, $colors);
                return Html::checkbox($name, $checked, [
                    'value' => $value,
                    'label' => $label,
                ]);
             }
       ]) ?>

现在我用一个额外的表单模型解决了这个问题。这在我看来是一个合适的解决方案。

/**
 * @property string $colors Can be something like "red" or "red,green,blue" or ""
 */
class Product extends Model {
}

/**
 * @property string[] $colorsAsArray
 */
class ProductForm extends Product {

    public function rules() {
        return array_merge(parent::rules(), [
            ['colorsAsArray', 'safe'] // just to make it possible to use load()
        ]);
    }

    public function getColorsAsArray() {
        return explode(',', $this->colors);
    }

    public function setColorsAsArray($value) {
        $this->colors = self::implode($value);
    }

    protected static function implode($value) {
        if ($value == 'none-value') return '';
        return implode(',', $value);
    }

    /* - - - - - - - - - - optional - - - - - - - - - - */

    public function attributeLabels() {
        $attributeLabels = parent::attributeLabels();
        return array_merge($attributeLabels, [
            'colorsAsArray' => $attributeLabels['colors'],
        ]);
    }
}

有了这个我就可以这样使用表格了:

<?php $availableColors = ['red' => 'Red', 'green' => 'Green', 'blue' => 'Blue']; ?>

<?php $form = ActiveForm::begin([]); ?>
    <?= $form->field($model, 'colorsAsArray')
             ->checkboxList($availableColors, ['unselect' => 'none-value']) ?>
<?php ActiveForm::end(); ?>

当然,现在controller不得不使用继承模型class。

如果未选中复选框,该解决方案也会处理该问题。这就是引入 'none-value' 的原因。