Array Of Model In Form

Hey there.

I’m pretty new to yii and not sure if there is a solution for this. I did some searching, but did not find a good way for my problem. Well, more a question than a problem.

So I have a model and created a crud with gii. The model contains a serialized array when stored to the db, so my question is what’s the best way to fill this array with values in a form, for the crud only gives me a textarea. My idea was to pass the array via the render function to the view (form), but I can’t (?) use the the ActiveForm widget then to add the textfields for the individual fields of the member array of the model. So they are also not validated on submit (which is not that dramatic), but would be good to know.

Any help or suggestens are pretty much appreciated. :)

Fussel

Hello and welcome to the forum.

Here’s what you can do:

  1. Create virtual attribute in your model, say, categoryIds. Like this:

public $categoryIds = array();

  1. create afterFind() trigger in your model, that will populate this attribute with deserialized values. For example:

public function afterFind()

{

	$this->categoryIds = explode(',', $this->categories); // deserialize here 

	return parent::afterFind();

}

where ‘categories’ is the name of real table field where your values are stored.

  1. create beforeSave trigger in your model, to serialize values back to the table. For example:

public function beforeSave()

{

	$this->categories = implode(',', $this->categoryIds); // serialize here

	return parent::beforeSave();

}

  1. use virtual attribute name (categoryIds) for creating form inputs like checkbox list or something.

That’s it.

Also you can create custom validation rules (see dosc on validation) to make all the required checks on that field.

PS. Btw you can use getters and setters for that too:


public function getCategoryIds()

{

    return explode(',', $this->categories);

}

public function setCategoryIds($values)

{

    $this->categories = implode(',', $values);

}

Well, its a list of different things in the array not just ids, but I will try this. Thank you very much for the quick response and the adivce with the afterFind and beforeSave, think they will definitely help.

hi, and welcome to yii framework,

your questin is not very clear for me, but i understand you want from your application to store values in a db currently hold in an array. So, what is your question ? how to receive data from a form and pass it to an array, or How to store the array in a database ?

anyway, you can always use JSON to encode your data.




/**

   @property $sample string a string field in your database filled by an array

*/

class MyClass extends CActiveRecord {

	public function setSampleData($data){

      	$this->sample = json_encode($data);

	}

	public function getSampleData(){

       	return json_decode($this->sample);

	}

}



In this way, you handled your $sample attribute via getter/setter:




	$ar = array("red"=>"rgb(255,0,0)", "blue"=>"rgb(0,255,0)");

 	$model = new MyClass;

 	$model->setSampleData($ar);

 	$model->save();


 	$loaded = MyClass::model()->findByPk($model->primarykey);

 	$ar2 = $loaded->getSampleData();  // or simply:  $loaded->sampledata;

 	foreach($ar2 as $color=>$rgb)

     	echo "<div style='color: $rgb;'>$color</div>";



Yeah, I was trying to simplify things just to explain the main idea.

You can do whatever you want using actual serialize/deserialize functions in that hooks.

Sorry, if I were not clear enough. Was not sure how to describe it correctly, but ORey’s solution will work for me I guess, will try it now.

Just have to add a virtual attribute for every field (index) of the array, correct?

Something like:




/**

 * @property int $id

 * @property string $myArray

 */

class Item extends CActiveRecord

{

  public $field1;

  public $field2; // and so on


  public function afterFind()

  {

    $values = unserialize($this->myArray);

    $this->field1 = $values['someIndex'];

    $this->field2 = $values['someOtherIndex'];

    return parent::afterFind();

  }

}



And the other way round when saving, of cause. Maybe I’ll not use afterFind and beforeSave and call these functions manually for I need the values in fields only for creating and editing and not when using the model in some other place, where the array itself is easyer to handle and use.

Not necessarily, you can do


public $deserialized;

public function afterFind()

{

    $this->deserialized = unserialize($this->myArray);

    return parent::afterFind();

}

and then


$model = MyModel::model()->findByPk(1);

echo $model->deserialized['someIndex'];

The other way is something like this:


public function getMyValue()

{

    return unserialize($this->myArray);

}


public function setMyValue($val)

{

    $this->myArray = serialize($val);

}

and then


<?= CHtml::activeTextArea($model, 'myValue'); ?>

Don’t forget to add myValue to validation rules and labels array to make forms work properly.

PS. As for me, I like getters/setters very much and use it all the time (probably, even overuse)

Sorry for my late response, I was not allowed to make more than three posts at the first day.

When using your last suggestion I’m getting an error: “htmlspecialchars() expects parameter 1 to be string, array given” in CHtml::encode function. Also I could hardly work with an array in a text box. What I made of your first suggestions works, so I will stick to that for the moment. :)

That’s because I need to sleep sometimes :)

I meant checkboxlist, not textarea :lol:

CHtml::activeCheckBoxList($model, ‘myValue’, $list_of_options)