yiisoft / yii-widgets

Collection of useful widgets for Yii Framework
https://www.yiiframework.com/
BSD 3-Clause "New" or "Revised" License
17 stars 7 forks source link

ActiveField json parse error #5

Closed finnan444 closed 4 years ago

finnan444 commented 6 years ago

What steps will reproduce the problem?

  1. Load data from DB via ActiveRecord Model. One of the fields has JSON type, so starting from 2.0.14 it is converted to Array while populating
  2. Use this model while building yii\widgets\ActiveForm. For example,
    <?php 
    $form = ActiveForm::begin();
    echo $form->field($model, 'data')->textarea();
    ActiveForm::end(); 
    ?>

    What is the expected result?

    Form should be loaded and json field edited as string. !Or make new method for json editing in Active field (see https://www.yiiframework.com/doc/api/2.0/yii-widgets-activefield)

What do you get instead?

yii\base\ErrorException: htmlspecialchars() expects parameter 1 to be string, array given in /var/www/dev/vendor/yiisoft/yii2/helpers/BaseHtml.php:111
Stack trace:
#0 [internal function]: yii\base\ErrorHandler->handleError(2, 'htmlspecialchar...', '/var/www/dev-ge...', 111, Array)
yiisoft/view#1 /var/www/dev/vendor/yiisoft/yii2/helpers/BaseHtml.php(111): htmlspecialchars(Array, 11, 'UTF-8', true)
yiisoft/view#2 /var/www/dev/vendor/yiisoft/yii2/helpers/BaseHtml.php(699): yii\helpers\BaseHtml::encode(Array, true)
yiisoft/view#3 /var/www/dev/vendor/yiisoft/yii2/helpers/BaseHtml.php(1506): yii\helpers\BaseHtml::textarea('Menu[data]', Array, Array)
yiisoft/view#4 /var/www/dev/vendor/yiisoft/yii2/widgets/ActiveField.php(513): yii\helpers\BaseHtml::activeTextarea(Object(app\modules\admin\models\Menu), 'data', Array)

Additional info

Q A
Yii version 2.0.15.1
PHP version 7.0.28
Operating system CentOS
beroso commented 6 years ago

I think the value formatting can solve your problem:

echo $form->field($model, 'data', [
    'inputOptions' => [
        'value' => is_array($model->data) ? Json::encode($model->data) : $model->data
    ]
])->textarea();
finnan444 commented 6 years ago

@berosoboy Thanks for decision, it has 1 problem: After i save my form it saves this filed like that "{\"icon\":\"fa fa-circle-o\"}" and Model cant longer convert it to array. Plus it is unnecessary convertation (model decodes Json, then we encode it again). May be add this logic to field or create something like jsonArea()/JsonInput?

beroso commented 6 years ago

@finnan444 Understood. In that case you need to decode the json field before saving to database (you can do this after $model->load())

...
if ($model->load(Yii::$app->request->post()) {
    $model->data = Json::decode($model->data);
    ...
}
beroso commented 6 years ago

Maybe these steps can be automated by framework.

schmasterz commented 6 years ago

@finnan444 Probably you have Json::encode() somewhere in you code. Since 2.0.14 the framework encodes json sql fields, so it isn't necessary to convert it. If you want to preserve old behavior (before 2.0.14) check https://github.com/yiisoft/yii2/issues/15716#issuecomment-368143206

finnan444 commented 6 years ago

Guys, i found where Json is encoded - look at the Class yii\db\pgsql\JsonExpressionBuilder it has build method, see line 45:

$placeholder = $this->queryBuilder->bindParam(Json::encode($value), $params);

so as our form sends correct Json string via post, in JsonExpressionBuilder it is encoded again

schmasterz commented 6 years ago

@finnan444 : See my prevous comment. Remove Json::encode from your code or disable using Class yii\db\pgsql\JsonExpressionBuilder . How to do it is described here: https://github.com/yiisoft/yii2/issues/15716#issuecomment-368143206

beroso commented 6 years ago

@finnan444 , decoding the data before saving solved your problem?

finnan444 commented 6 years ago

@schmasterz if i disable this class in config like in #15716, it affects the whole project - so in other components JSON is decoded correctly while been populated. Dont try to solve my specific issue. Look wider, is there a need to check strings in yii\db\pgsql\JsonExpressionBuilder and if they are already valid Json and there is no need to encode them again

beroso commented 6 years ago

This behavior is documented: https://github.com/yiisoft/yii2/blob/master/docs/guide/db-active-record.md#json-in-mysql-and-postgresql I think we need to be aware of it before saving the ActiveRecord.

SilverFire commented 6 years ago

There is an issue https://github.com/yiisoft/yii2/issues/15837 where author asks whether it is possible to decode JSON {} to object. The discussion reaches the point where we need a possibility to specify encoding/decoding options per-attribute of a specific model.

The problem you report is partially related to that issue. I would suggest you to use AttributeTypecastBehavior with typecastAfterFind = true on your model to cast array back to json