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