Search code examples
yii2virtual-attributecsqldataprovider

Yii2 virtual attribute out of an attribute of a SqlDataProvider


My AddressController:

public function actionIndex() {
    $searchModel = new AddressSearch;
    $dataProvider = $searchModel->search($_GET);

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

My model AddressSearch:

class AddressSearch extends Model {

    public function search($params) {
        $dataProvider = new SqlDataProvider([
            'sql' => '
                SELECT
                    name
                FROM...

View:

GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        'name2',

I have an attribute called name in this dataProvider. I would like to create a new virtual attribute called name2 out of name by preg_replace()-ing a few parts of it. The function itself is working. I have tried a lot of different things, but I still can't make it to fill the attribute name2 with data. name2 is always empty. Can you please point me to the right direction? Many thanks!

UPDATE: based on the brilliant ideas of @Imaginaroom and @rob006 I've done the following:

  • moved getName2() and the attributes I'm filling with SqlDataProvider from base model Address to AddressSearch
  • deleted the empty base model Address because I don't need it anyway. Less is more!
  • in search() I've added:

    foreach ($dataProvider->getModels() as $row) {
        $model = new AddressSearch;
        $model->setAttributes($row, false);
        $models[] = $model;
    }
    $dataProvider->setModels($models);
    

It works! Many thanks guys! It's fantastic that you are there and help!!!


Solution

  • The problem is that you're using SqlDataProvider which returns rows from the table as array instead of model instance. So that's why your getter (virtual-attribute) does not work in GridView - GridView does not work on Address model instance, but on raw array without name2 field.

    You should change your SearchModel to use ActiveQuery:

    $dataProvider = ActiveDataProvider([
        'query' => Address::find()->select(['name']) //and whatever your query contains besides this
    ])
    ...
    

    UPDATE: If you don't want to do it like this, you can add your logic directly in GridView like so:

    GridView::widget([
        'dataProvider' => $dataProvider,
        'filterModel' => $searchModel,
        'columns' => [
            [
                'header' => 'Name 2',
                'value' => function($model) {
                    if (isset($this->_name2)) {
                        return $this->_name2;
                    }
    
                    return $this->_name2 = preg_replace([...
                }
            ]