Yii2: How to map a CSV string in an attribute to C

2019-09-11 08:03发布

I have a model with an attribute that holds a CSV string.

(The model is actually an ActiveRecord object but I guess this is not important. Correct me if I'm wrong.)

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

And I have a form in which I'd like to display this attribute as a checkboxList so that the user can select the possible values with simple clicks instead of typing into a textInput.

Theoretically, it should look similar to this:

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

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

This does obviously not work since the field colors would need to be an array. But in my model it is a string.

What would be a good way to achieve that? With JS or pseudo attributes? The colors attribute must not be changed since it is already used in other contexts that shouldn't be modified.

4条回答
唯我独甜
2楼-- · 2019-09-11 08:20

Now I solved it with an extra model for the form. This seems to me a proper solution.

/**
 * @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'],
        ]);
    }
}

With this I can use the form that way:

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

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

Of course, now the controller has to use the inherited model class.

The solution deals also with the issue if no checkbox is selected. That is why 'none-value' is introduced.

查看更多
smile是对你的礼貌
3楼-- · 2019-09-11 08:24

CSV is a file format used for moving tabular data between programs that natively operate on incompatible formats. Using it as a model attribute is not very elegant (to say it nicely). In my opinion you should have started out storing your colors in an array.

That being said you can achieve converting the array data from the dropdown list to CSV using the beforeValidate() function in your model:

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

    return parent::beforeValidate();
}
查看更多
迷人小祖宗
4楼-- · 2019-09-11 08:34

You can override beforeValidate method in your model, to implode your colors array into string. In your view you can use following:

<?= $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,
                ]);
             }
       ]) ?>
查看更多
Anthone
5楼-- · 2019-09-11 08:47

I think this is a PHP question, but anyway you can use PHP explode for build the array you need. See here for more details and then user the array inside the checkboxList

查看更多
登录 后发表回答