1

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!!!

user2511599
  • 796
  • 1
  • 13
  • 38

2 Answers2

1

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([...
            }
        ]
AwesomeGuy
  • 1,059
  • 9
  • 28
  • I was thinking the same, that maybe it's because there is in fact no connection between the model and the dataProvider. Is there no other way to make it happen? The model is not mapped to the db table. In fact the select is quite complicated with a lot of joins and I don't wanted to create all the connections because of one tiny page that is only showing some simple data. – user2511599 Sep 14 '18 at 08:21
  • I'm not sure that you can make the connection afterwards, I will put an update to the answer in a min how you can do it in GridView directly. – AwesomeGuy Sep 14 '18 at 08:23
  • @user2511599 Check my updated answer now and see if it fits you. – AwesomeGuy Sep 14 '18 at 08:27
  • Is there no way to move this somehow to the model? At least to search model when there is no connection to the base model? – user2511599 Sep 14 '18 at 08:29
  • I think there isn't a way sincce you're using SqlDataProvider. Well, at least I cannot find it on the web anywhere. You may try to do a little hack, but I'm not 100% sure will it work. SqlDataProvider has methods getModels and setModels, so you could do getModel() and iterate over them and try casting them to your model class, and then set that array of models using setModels() but you need to try that first, it's just an idea. – AwesomeGuy Sep 14 '18 at 08:40
  • 1
    Anyway thank you it is a lot of help for me, I can manage now to make it work. – user2511599 Sep 14 '18 at 08:43
1

If you really need Address model instance and use SqlDataProvider at the same time, you may convert array to model instance manually:

$models = [];
foreach ($dataProvider->getModels() as $row) {
    $model = Address::instantiate($row);
    Address::populateRecord($model, $row);
    $models[] = $model;
}
$dataProvider->setModels($models);

You can place this in AddressSearch::search() or create custom SqlDataProvider and override prepareModels().

rob006
  • 21,383
  • 5
  • 53
  • 74
  • So is it not possible to use a function on an array? I mean you are basically right, but I'm looking for a way so that I can keep my code as tight as possible. I have got rid of the model Address now so I have only AddressSearch. I was moving the function `name2` to AddressSearch. I'm looking for a way to make it work like this. – user2511599 Sep 14 '18 at 08:50
  • I don't really get your question, but you may try to replace `Address` in my answer by `AddressSearch` and see if it works - it may depend how your `AddressSearch` model looks like. – rob006 Sep 14 '18 at 08:52
  • AddressSearch is a standard gii generated SearchModel with a `search()` function that returns `$dataProvider`. – user2511599 Sep 14 '18 at 08:59
  • It still does not explain what you're trying to do now and why did you dropped `Address` model. But anyway `$dataProvider->getModels()` returns you data from data provider - you can do with it whatever you want in simple foreach. You can just add `name2` to this array and set it to data provider using `$dataProvider->setModels($models)` . – rob006 Sep 14 '18 at 09:09
  • Call to undefined method app\models\AddressSearch::instantiate() – user2511599 Sep 14 '18 at 09:49
  • `instantiate()` is implemented in `ActiveRecord`. If you're using simple `Model` you can use `$model->setAttributes($row, false)` or just `Yii::configure()`. – rob006 Sep 14 '18 at 09:53