Could Yii2 support validate virtual attribute

Dear,

I’ve tried to create a virtual attribute and validate it, but it only worked with client validation while the server validation didn’t work.

My environment version is:

My code as below:

Project.php (model)




<?php

namespace app\models;


use Yii;

use yii\bootstrap\Html;

use yii\helpers\ArrayHelper;


class Project extends \yii\db\ActiveRecord

{


    private $assignArray = [];


    /**

     * @inheritdoc

     */

    public function rules()

    {

        return [

            [['name', 'description', 'assignments'], 'required'],

            [['assignments', 'created', 'modified'], 'safe'],

            [['name'], 'string', 'max' => 160],

            [['description'], 'string', 'max' => 1000]

        ];

    }


    /**

     * @inheritdoc

     */

    public function afterSave($inserted)

    {

        if (!$inserted) {

            ProjectUser::deleteAll('project_id = :id', array('id'=>$this->id));

        }

        // insert it.

        return array_walk($this->assignArray, function ($val, $key) {

            $assignment = new ProjectUser();

            $assignment->project_id = $this->id;

            $assignment->user_id = $val;

            $assignment->save();

        });

    }


    /**

     * @return \yii\db\ActiveQuery

     */

    public function getProjectUsers()

    {

        return $this->hasMany(ProjectUser::className(), ['project_id' => 'id']);

    }


    /**

     * Set value for user assignment

     *

     * @param array $members

     */

    public function setAssignments($value)

    {

        $this->assignArray = (array) $value;

    }


    /**

     * @return ProjectUser[]

     */

    public function getAssignments()

    {

        if ($this->isNewRecord) {

            return $this->assignArray;

        }

        return ArrayHelper::getColumn($this->projectUsers, 'user_id');

    }

}



ProjecUser.php (model)




<?php

namespace app\models;


use Yii;


class ProjectUser extends \yii\db\ActiveRecord

{

    /**

     * @inheritdoc

     */

    public static function tableName()

    {

        return 'project_user';

    }


    /**

     * @inheritdoc

     */

    public function rules()

    {

        return [

            [['project_id', 'user_id'], 'required'],

            [['project_id', 'user_id'], 'integer'],

            [['created', 'modified'], 'safe']

        ];

    }


    /**

     * @return \yii\db\ActiveQuery

     */

    public function getProject()

    {

        return $this->hasOne(Project::className(), ['id' => 'project_id']);

    }


    /**

     * @return \yii\db\ActiveQuery

     */

    public function getUser()

    {

        return $this->hasOne(User::className(), ['id' => 'user_id']);

    }

}



_form.php(view)




<?php


use app\models\User;

use kartik\widgets\Select2;

use kartik\widgets\ActiveForm;

use yii\helpers\Html;


/* @var $this yii\web\View */

/* @var $model app\models\Project */

/* @var $form kartik\widgets\ActiveForm */

?>


<div class="project-form">


    <?php $form = ActiveForm::begin(); ?>


    <?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>


    <?= $form->field($model, 'description')->textarea(['maxlength' => true, 'rows' => '10']) ?>


    <?= $form->field($model, 'assignments')->widget(Select2::className(), [

        'data' => User::findListAsArrayMap('id', 'username'),

        'options' => [

            'multiple' => true,

            'placeholder' => 'Please select ...',

            'allowClear' => true,

        ]

    ]) ?>


    <div class="form-group">

        <?= Html::submitButton(

            $model->isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'),

            ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']

        ) ?>

    </div>


    <?php ActiveForm::end(); ?>


</div>



I use with kartik activeform and select2, but it doesn’t matter, because it work very well with normal attribute, or an public class property. I mean only when I use set and get function to make a virtual attribute, it just work with client validation, not server validation (focus on assignments required).

Doesn’t Yii2 support it ?

Some time ago I faced something similar.

My scenario was a bit different as it was a value (not an array).

I digged a bit in yii validation code and discovered that I need to declare the property as public otherwise validation can’t access it.

The strange think was that I had the get method but strangely the validation do not use it and instead try to access the property directly.

So in your case it is possible that validation try to read directly $mymodel->assignments

try to declare




//private $assignArray = [];

public $assignments = [];



and modify the rest of the code according to the new name.

Yes, you are right, a public property of model works perfectly with validation while a pair of set/get function is not. I also have tried your solution before. Thank for your sharing about the reason, it is really useful :)