How to set validation rules for custom CActiveRecord attributes in Yii?

226 views Asked by At

I'm working on a Yii project with a database, that contains a table, where almost all it's data is saved in a field as JSON (it's crazy, but it is so as it is):

id      INTEGER
user_id INTEGER
data    LONGTEXT

This "JSON field" data has following structure and contains inter alia an image:

{
   "id":"1",
   "foo":"bar",
   ...
   "data":{
      "baz":"buz",
      ...
    }
}

Displaying it is no problem, but now I want to make the data ediable. My form looks like this:

<?php
$form = $this->beginWidget('CActiveForm', array(
    'id' => 'my-form',
    'htmlOptions' => array('enctype' => 'multipart/form-data'),
    'enableAjaxValidation'=>false,
));
?>
<div class="row">
    <?php echo $form->labelEx($model, 'foo'); ?>
    <?php
    echo $form->textField($model, 'foo', array(...));
    ?>
    <?php echo $form->error($model, 'foo'); ?>
</div>
<div class="row">
    <?php echo $form->labelEx($model, 'baz'); ?>
    <?php
    echo $form->textField($model, 'data[baz]', array(...));
    ?>
    <?php echo $form->error($model, 'data[baz]'); ?>
</div>

It works. But there are multiple problems, that seem to be caused by the same thing -- that he form fields are not referenced to the model attributes/properties:

  1. When I make fields foo and baz required (public function rules() { return array(array('foo, baz', 'required')); } -- the property $foo is defined) foo bahaves as wished, but baz causes an "foo cannot be blank" error. So I cannot set a data[*] as required.

  2. If the form is not valid and gets reloaded, all the data[*] fields are empty.

  3. The data[*] fields are not marked as required.

Is there a to solve this without to change the datase structure? There will not be a correct way for it, but maybe a workaround.

1

There are 1 answers

0
SiZE On

It's impossible to validate fields in such way. First of all if you are using field in model it must be defined or exist in table for active record. So if you want to validate such structure the only right way to do it:

class Model extends CActiveRecord {
  // Define public varialble
  public $data_baz;

  public function rules(){
    return array(
      // Add it to rules
      array( 'data_baz', 'required' )
    );
  }

  public function attributeLabels(){
    return array(
      // Add it to list of labels
      'data_baz' => 'Some field'
    );
  }

  protected function beforeSave(){
    if ( !parent::beforeSave() ) {
      return false;
    }

    // Also you may create a list with names to automate append
    $this->data['baz'] = $this->data_baz;

    // And serialize data before save
    $this->data = serialize( $this->data );

    return true;
  }
}

And your form should looks like

<div class="row">
    <?php echo $form->labelEx($model, 'data_baz'); ?>
    <?php echo $form->textField($model, 'data_baz'); ?>
    <?php echo $form->error($model, 'data_baz'); ?>
</div>