ActiveRecord IndexBy with compound key

Hi,

I’m very much a newbie so I’m hoping that I’ve missed something obvious. Context is Yii 2.0.7, PHP 5.5 using Netbeans and the PHP builtin server. I have a database containing questionnaires, which are built up of pages which are in turn built up of questions. Because both questions and pages can be reused they are all linked together with junction tables. For example the questionnaire->pages relation is via a junction table containing a questionnaire id and a page id which form a composite key for the junction table. The model for the table has the requisite methods for getting the relations and the primary key.


    /**

     * 

     * Get a Questionnaire from an id

     */

    public function getQuestionnaire ()

    {

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

    }

    

    /**

     * Get a Page from an id

     */

    public function getPage ()

    {

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

    }

    

    /**

     * 

     * @inheritdoc

     */

    static public function primaryKey() 

    {

        return ['questionnaire_id', 'page_id'];

    }



This is then used to display a grid view (actually the Kartik extension) which all works very nicely. The problem comes when I add an ordering field to the junction table. The ordering field is required so that the user can reorder pages in a questionnaire and the pages will be sorted on the ordering field for display. To do this I’m using the Kartik editable extension. Again this works fine - up to a point. I was trying to trace down a nasty bug and then realised that I was not including the ‘indexBy’ clause in the creation of the data provider, as Kartik indicates. So I did this, and then it fell apart. The data provider is created with


    public function actionUpdate($id)

    {

        $model = $this->findModel($id);


        if ($model->load(Yii::$app->request->post()) && $model->save()) {

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

        } 

        else 

        {

            $query = QuestionnairePage::find()->where( ['questionnaire_id' => $model->id])->indexBy('primaryKey');

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

            return $this->render('update', [

                'model' => $model, 'dataProvider' => $dataProvider

            ]);

        }

    }

It’s probably worth pointing out that this comes from the questionnaire controller, the grid is on the Update page and that the editable will route to a ‘reorder’ controller action when triggered.

This doesn’t work - I get a PHP error Illegal offset type when rendering the grid. According to the dump the problem arises in the ‘CreateModels’ method of ‘ActiveQueryTrait’ and indeed using the debugger I can confirm this. The relevant code is


           foreach ($rows as $row) {

                    $model = $class::instantiate($row);

                    $modelClass = get_class($model);

                    $modelClass::populateRecord($model, $row);

                    if (is_string($this->indexBy)) {

                        $key = $model->{$this->indexBy};

                    } else {

                        $key = call_user_func($this->indexBy, $model);

                    }

                    $models[$key] = $model;

Clearly this last line can’t work with a composite key, as the documentation requires the callback to provide an array of key names. So my question: is this a bug, a missing feature have I done something stupid?

P.S. I could solve this by adding an auto increment as a primary key, but I’d rather not.

Great troubleshooting. I’m going to shoot from the hip on this one because I am far from expert but I think you inadvertantly answered your own question:

Did you see this comment on Kartik’s site:

https://github.com/kartik-v/yii2-builder/pull/55

Basically, sounds like you have to overload indexBy() and provide the imploded list (string, not array) of keys.

Thanks, that makes sense. Delving into the whole compound key think you apparently have tread very carefully here.