kartik-v / yii2-dynagrid

Turbo charge the Yii 2 GridView with personalized columns, page size, and themes.
http://demos.krajee.com/dynagrid
Other
74 stars 66 forks source link

Array to string conversion exception in Dynagrid/GridView with multiple values filter #171

Closed Patricy closed 5 years ago

Patricy commented 7 years ago

Prerequisites

Steps to reproduce the issue

  1. In view with Dynagrid set FilterType as array and set multiple=true option:
    
        'columns' => [
            'id',
            'name',
            [
                'attribute' => 'status',
                'filterType' => GridView::FILTER_SELECT2,
                'filter' => [
                                        1 => 'active',
                                        2 => 'inactive'
                 ],
                'filterWidgetOptions'=>[
                    'pluginOptions'=>[
                            'allowClear'=>true,
                            'multiple'=>true
                        ]
                    ],
            ],
            ['class' => 'yii\grid\ActionColumn'],
        ],

Attribute ```status``` is integer, in SearchModel set as safe to search
(code models see below)

2. Choose one or multiple values in Select2 dropdown filter for this attribute, check if it works properly - filtering multiple values, displaying correctly

3. Personalize dynagrid settings - click green button and apply

## Expected behavior and actual behavior

When I follow those steps, I see
"Array To String conversion" exception

I was expecting normally saved filter values to Dynagrid settings

## Environment

#### Browsers

- [ x] Google Chrome

Does not matter I sure

#### Operating System

- [ x] Windows
Does not matter I sure

#### Libraries

- jQuery version: v2.2.4
- yii2-dynagrid version: 1.4.6

## Isolating the problem

- [x ] This bug not happens [on the demos page](https://demos.krajee.com/dynagrid-demo) 
 - **demo page not exists** 
- [x ] The bug happens consistently across all tested browsers

## Problem and Solution

I'm not sure what project to put this bug. Sorry if I'm wrong.

Exception happens **NOT** in Dynagrid but only when I use it.
Problem is in **kartik-v\yii2-grid**

in

**....\vendor\kartik-v\yii2-grid\DataColumn.php at line 341**

and it goes there

  **...\vendor\yiisoft\yii2\grid\DataColumn.php at line 199** – yii\helpers\BaseHtml::activeTextInput('model' => 'class app\models\SomeEntitySearc...', 'attribute' => ''status'', 'options' => 'array ('class' => 'form-control'...')

When it try to ```renderFilterCellContent()```

```php
$content = parent::renderFilterCellContent();

it has $this->filter variable = null

In parent class (which is yii\grid\DataColumn it caused exception because attribute is array but filter is null and it try to render activeTextInput with array attribute

My temporary solution for our project is In kartik\grid\DataColumn.php in method renderFilterCellContent() (line 341) put:

    protected function renderFilterCellContent()
    {
        if (isset($this->grid->filterModel->{$this->attribute}) && is_array($this->grid->filterModel->{$this->attribute}) && is_null($this->filter) && is_null($this->filterType)){
            $this->filter = false;
        }

        $content = parent::renderFilterCellContent();

Full code of view and models

sql:

CREATE TABLE `some_entity` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `status` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `some_entity` (`id`, `name`, `status`) VALUES
(1, 'Name one', 1),
(2, 'Name Two', 2),
(3, 'Name Three',   1);

view index.php

<?php

use yii\helpers\Html;

use yii\widgets\Pjax;
use kartik\dynagrid\DynaGrid;
use kartik\grid\GridView;

$this->title = 'Some Entities';
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="some-entity-index">

    <h1><?= Html::encode($this->title) ?></h1>

    <p>
        <?= Html::a('Create Some Entity', ['create'], ['class' => 'btn btn-success']) ?>
    </p>
<?php Pjax::begin(); ?>    <?= DynaGrid::widget([
        'storage' => DynaGrid::TYPE_COOKIE,
        'options' => ['id'=>'entity-list'],
        'gridOptions' => [
            'dataProvider' => $dataProvider,
            'filterModel' => $searchModel,
            'condensed'=>true,
            'toolbar' => [
                '{dynagrid}',
                '{export}',
                '{toggleData}',
            ],
            'toolbar' => false,
            'panel' => [
                'type'=>'info',
                'heading' => '{dynagrid} {export} {toggleData}',
                'after' => false,
            ],
            'rowOptions'=>function($model){
                    return ['data-class' => 'danger'];
            },
        ],
        'columns' => [
            'id',
            'name',
            [
                'attribute' => 'status',
                'filterType' => GridView::FILTER_SELECT2,
                'filter' => [
                                       1 => 'active',
                                       2 => 'inactive'
                 ],
                'filterWidgetOptions'=>[
                    'pluginOptions'=>[
                            'allowClear'=>true,
                            'multiple'=>true
                        ]
                    ],
            ],
            ['class' => 'yii\grid\ActionColumn'],
        ],
    ]); ?>
<?php Pjax::end(); ?></div>

Model SomeEntity.php

<?php

namespace app\models;

use Yii;

/**
 * This is the model class for table "some_entity".
 *
 * @property integer $id
 * @property string $name
 * @property integer $status
 */
class SomeEntity extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'some_entity';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['name', 'status'], 'required'],
            [['status'], 'integer'],
            [['name'], 'string', 'max' => 255],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'name' => 'Name',
            'status' => 'Status',
        ];
    }
}

Search model SomeEntitySearch.php:

<?php

namespace app\models;

use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use app\models\SomeEntity;

/**
 * SomeEntitySearch represents the model behind the search form about `app\models\SomeEntity`.
 */
class SomeEntitySearch extends SomeEntity
{
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['id'], 'integer'],
            [['name', 'status'], 'safe'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function scenarios()
    {
        // bypass scenarios() implementation in the parent class
        return Model::scenarios();
    }

    /**
     * Creates data provider instance with search query applied
     *
     * @param array $params
     *
     * @return ActiveDataProvider
     */
    public function search($params)
    {
        $query = SomeEntity::find();

        // add conditions that should always apply here

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);

        $this->load($params);

        if (!$this->validate()) {
            // uncomment the following line if you do not want to return any records when validation fails
            // $query->where('0=1');
            return $dataProvider;
        }

        // grid filtering conditions
        $query->andFilterWhere([
            'id' => $this->id,
            'status' => $this->status,
        ]);

        $query->andFilterWhere(['like', 'name', $this->name]);

        return $dataProvider;
    }
}

SomeEntityController.php (generated, standard):

<?php

namespace frontend\controllers;

use Yii;
use app\models\SomeEntity;
use app\models\SomeEntitySearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

/**
 * SomeEntityController implements the CRUD actions for SomeEntity model.
 */
class SomeEntityController extends Controller
{
    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }

    /**
     * Lists all SomeEntity models.
     * @return mixed
     */
    public function actionIndex()
    {
        $searchModel = new SomeEntitySearch();
        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);

        return $this->render('index', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    /**
     * Displays a single SomeEntity model.
     * @param integer $id
     * @return mixed
     */
    public function actionView($id)
    {
        return $this->render('view', [
            'model' => $this->findModel($id),
        ]);
    }

    /**
     * Creates a new SomeEntity model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate()
    {
        $model = new SomeEntity();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        } else {
            return $this->render('create', [
                'model' => $model,
            ]);
        }
    }

    /**
     * Updates an existing SomeEntity model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @return mixed
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view', 'id' => $model->id]);
        } else {
            return $this->render('update', [
                'model' => $model,
            ]);
        }
    }

    /**
     * Deletes an existing SomeEntity model.
     * If deletion is successful, the browser will be redirected to the 'index' page.
     * @param integer $id
     * @return mixed
     */
    public function actionDelete($id)
    {
        $this->findModel($id)->delete();

        return $this->redirect(['index']);
    }

    /**
     * Finds the SomeEntity model based on its primary key value.
     * If the model is not found, a 404 HTTP exception will be thrown.
     * @param integer $id
     * @return SomeEntity the loaded model
     * @throws NotFoundHttpException if the model cannot be found
     */
    protected function findModel($id)
    {
        if (($model = SomeEntity::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }
}
csatarij commented 6 years ago

I am hereby also confirming the validity of the issue.

abuammar commented 6 years ago

@Patricy even your temporary solution doesnt work.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.