Yii2 - gridView with hasMany viaTable

I have 3 tables - products, components, componentsMap (tables are populated correctly)

Products are connected with components via componentsMap table - structure:


id | component_id | product_id

I am using this method for connection:


public function getComponents()

        {

            return $this->hasMany(Components::className(),

                ['id' => 'product_id'])

                ->viaTable('componentsMap', ['component_id' => 'id']);

        }

The search function:


public function search($params)

        {

            $query = Products::find();

    

            $this->load($params);

    

            $query->select(

                    'products.*,

                    tradeNames.name AS tradeName,

                    components.name AS componentName, components.no_cas AS cas, components.no_einecs AS einecs')

                ->from('products')

                ->with(['components', 'tradeName'])

                ->leftJoin('componentsMap', 'componentsMap.product_id = products.id')

                ->leftJoin('components', 'componentsMap.component_id = components.id')

                ->leftJoin('tradeNamesMap', 'tradeNamesMap.product_id = products.id')

                ->leftJoin('tradeNames', 'tradeNamesMap.tradeName_id = tradeNames.id')

                ->andFilterWhere(['LIKE', 'LOWER(inci)', strtolower($this->inci)])

                ->andFilterWhere(['LIKE', 'LOWER(tradeNames.name)', strtolower($this->tradeName)])

                ->andFilterWhere(['LIKE', 'LOWER(chemical_name)', strtolower($this->chemical_name)])

                ->andFilterWhere(['LIKE', 'created_at', $this->created_at]);

    

            return new ActiveDataProvider([

                'query' => $query,

                'pagination' => [

                    'pageSize' => 10,

                ],

                'sort' => [

                    'defaultOrder' => [

                        'inci' => SORT_ASC,

                    ]

                ],

            ]);

        }

In the gridView I am using this code to show components name:


[

        'attribute' => 'components.name',

        'label' => 'Components'

    ],

Nothing is returned - (not set) is shown. What am I doing wrong?

  1. You dont needidincomponentsMap`

  2. Your relation is incorrect. Right:




public function getComponents()

        {

            return $this->hasMany(Components::className(),

                ['id' => 'component_id'])

                ->viaTable('componentsMap', ['product_id' => 'id']);

        }



  1. In GridView components - is array, so this code is incorrect too:



[

        'attribute' => 'components.name',

        'label' => 'Components'

    ],



You must iterate over components something like this:




[

    'label' => 'Components',

    'value' => function ($model) {

        $components = ArrayHelper::map($model->components, 'id', 'name');

        $components = array_map(function ($id, $name) {

            return Html::a($name, ['/component/view', 'id' => $id]);

        }, array_keys($components), $components);

        return implode(', ', $components);

    },

    'format' => 'raw'

],