[Help] GridView, create new column with data from another tablet with indirect relationship

Hello,

I need your help, please.

I have a database with 3 tables. My database model is in attachment!

Now, in the Gridview of "colaborador", i want to create a new column with data from "carreira" table.

The values must be filled this way: "colaborador" has a "categoriaprofissional" that belong to a "carreira".

So the atribute "categoriaprofissional_fk" in the table "colaborador" permits to access to the "categoriaprofissional" which has a foreign key "carreira_fk" that access to the "carreira".

I’m in trouble to achieve the solution for this problem.

This is my index view of "colaborador":




    <?= GridView::widget([

        'dataProvider' => $dataProvider,

        'filterModel' => $searchModel,

        'columns' => [

            ['class' => 'yii\grid\SerialColumn'],


            'numerofuncionario',

        	'nome',

       		//now this is the new column that i want to create

        	[

         		'label' => 'Carreira',

				'attribute' => 'categoriaprofissional_fk',

         		'value' => categoriaprofissional::get_carreira('categoriaprofissionalFk.carreira_fk'),

	       		'filter'=> categoriaprofissional::get_carreiras(),

        	],

        	//this is the "categoriaprofissional" related directly with "colaborador" and works perfectly! 

        	[

        		'attribute' => 'categoriaprofissional_fk',

        		'value' => 'categoriaprofissionalFk.nome',

        		'filter'=> categoriaprofissional::get_nomes(),

        	],

			[

				'class' => 'yii\grid\ActionColumn',

				'template' => '{view}',

			],

        ],

    ]); ?>



And in the “categoriaprofissional” model i’ve created some functions to retrived data from “carreira”:




   public static  function  get_nomes(){

    	$cat = Categoriaprofissional::find()->orderBy(['nome' => SORT_ASC])->all();

    	$cat = ArrayHelper::map($cat, 'idcategoriaprofissional', 'nome');

    	return $cat;

    }

    

    public static  function  get_carreira($id){

    	$car = Carreira::find()->where(['idcarreira' => $id])->one();

    	return $car;

    }

    

    public static  function  get_carreiras(){

    	$cars = Carreira::find()->orderBy(['nome' => SORT_ASC])->all();

    	$cars = ArrayHelper::map($cars, 'idcarreira', 'nome');

    	return $cars;

    }

    

    

    /**

     * @return \yii\db\ActiveQuery

     */

    public function getCarreiraFk()

    {

        return $this->hasOne(Carreira::className(), ['idcarreira' => 'carreira_fk']);

    }


    /**

     * @return \yii\db\ActiveQuery

     */

    public function getColaboradors()

    {

        return $this->hasMany(Colaborador::className(), ['categoriaprofissional_fk' => 'idcategoriaprofissional']);

    }



It doesn’t crash but the result wasn’t the expected (or what is my wish :) )

The filter is populated with the correct values.

The rows are populated with the categoriaprofissional_fk and i wish that the categoriaprofissional_fk worked as a related atribute to obain the categoriaprofissionalFk.carreira_fk (the same way that i obtain the categoriaprofissionalFk.carreira_fk in the last column (categoria profissional) to work as parameter for the categoriaprofissional::get_carreira(‘categoriaprofissionalFk.carreira_fk’) function.

Need your help, please.

Thank you

Look at this link, everything you need is clearly explained

http://www.yiiframework.com/wiki/653/displaying-sorting-and-filtering-model-relations-on-a-gridview/

Sorry Grischer, but i’ve already seen this page.

The big difference (i think) of that exemple is the fact that the two tables "country" and "city" are directly related with the "tour" table.

My problem is a little different because "colaborador" has only a relationship with "categoriaprofissional" and is the "categoriaprofissional" that have a relationship with "carreira".

You can use the "via" relation




 public function getValues()

      {

    return $this->hasOne(categoriaprofisional::className(), ['id_categoriaprofisional' =>'id_categoriaprofisional'])

                ->via('careira',['id_careira' => 'id_careira']);

    }



I didn’t test the code but I guess this is the solution you are looking for

here the documentation:

http://www.yiiframework.com/doc-2.0/guide-db-active-record.html#relations-via-a-junction-table

Grischer,

Thank you… with your help I advanced a little more.

In my gridview the “carreira” data for each row is being shown correctly :)

But now i’m stuck in the filter and ordering in gridview!!!

The new index view is:




    <?= GridView::widget([

        'dataProvider' => $dataProvider,

        'filterModel' => $searchModel,

        'columns' => [

            ['class' => 'yii\grid\SerialColumn'],


            'numerofuncionario',

        	'nome',


       		//now this is the new column that i want to create

        	[

        		'label' => 'Carreira',

        		'value' => 'carreiraFk.nome',

        		'filter'=> carreira::get_nomes(),

        	],

        	//this is the "categoriaprofissional" related directly with "colaborador" and works perfectly! 

        	[

        		'attribute' => 'categoriaprofissional_fk',

        		'value' => 'categoriaprofissionalFk.nome',

        		'filter'=> categoriaprofissional::get_nomes(),

        	],

			[

				'class' => 'yii\grid\ActionColumn',

				'template' => '{view}',

			],

        ],

    ]); ?>



The ordering and filtering of "categoriaprofissional" works fine with dropdown box.

But the one for "carreira" does not!

In Attachment is a screenshot from the result of this gridview.

My new model code is




    public function getCategoriaprofissionalFk()

    {

        return $this->hasOne(Categoriaprofissional::className(), ['idcategoriaprofissional' => 'categoriaprofissional_fk']);

    }


    //new funciont 

    public function getCarreiraFk()

    {

    	return $this->hasOne(Carreira::className(), ['idcarreira' =>'carreira_fk'])

    	->via('categoriaprofissionalFk');

    }



In the "carreira" model i have a function, similar to the one I use in "categoriaprofissional" that creates the array for the filter:




    public static  function  get_nomes(){

    	$car = Carreira::find()->all();

    	$car = ArrayHelper::map($car, 'idcarreira', 'nome');

    	return $car;

    }



I didn’t understand if careira doesn’t show anything or you see results in dropdown but the filter doesn’t apply to results in gridview?

Sorry,

i forgot to upload the screenshot.

I’ve already edited the previous message and uploaded the screenshot.

The filter in not shown!

And the “label” isn’t clickable to order!

ok

for relational field you have to modify your search model creating a calculated field




Class yourSearchModel extends YourModel{


  //your calculated field

  public $careira;


  public function rules()

  {

   //add your related field in safe rule

   [['careira'],'safe'],

  

  }

 public function search($params)

    {

     //join your query dataProvider with your relatedfield

      $query = yourQuery->joinWith('careira'); 

   }


  //Sort your field

   $dataProvider->setSort([

            'attributes'=>[

                'careira'=>[

                    'asc'    =>['careira_type'=>SORT_ASC],

                    'desc'   =>['careira_type'=>SORT_DESC],

                    'default'=>SORT_ASC

                ],

            ]    

        ]);

  // add your related field to the search fields

  $query->andFilterWhere([['careira'=>$this->careira]);


}






this link explain all i wrote above:

http://www.yiiframework.com/wiki/621/filter-sort-by-calculated-related-fields-in-gridview-yii-2-0/

Grischer,

Thank you a lot.

I solve the problems with the filtering. Now I’m still having some problems with sortering but they are not critical.

Once again, thank you for your help!

What kind of problem with sorting?

It’s solved.

Thanks a lot for your help.

This is my final code. It might be useful to someone!

VIEW




    <?= GridView::widget([

        'dataProvider' => $dataProvider,

        'filterModel' => $searchModel,

        'columns' => [

            ['class' => 'yii\grid\SerialColumn'],

                 [

        		'label' => 'Carreira',

        		'attribute' => 'carreira_fk',

	       		'value' => 'carreiraFk.nome',

        		'filter'=> carreira::get_nomes(),

        	],

...



MODEL




class Colaborador extends \yii\db\ActiveRecord

{




    public $carreira_fk;




     

     public function getCarreiraFk()

    {

    	return $this->hasOne(Carreira::className(), ['idcarreira' =>'carreira_fk'])

    	->via('categoriaprofissionalFk');

    }

  }



MODELSEARCH





class ColaboradorSearch extends Colaborador

{

    public $carreira_fk;


    public function rules()

    {

        return [[['carreira_fk'], 'integer']];

    }


    public function search($params)

    {

        $query = Colaborador::find()->joinWith('carreiraFk');

    	//$query = Colaborador::find();


        $dataProvider = new ActiveDataProvider([

            'query' => $query,

        ]);

        

        $dataProvider->setSort([

        		'attributes' => [

        			'carreira_fk' => [

        				'asc' => ['carreira.idcarreira' => SORT_ASC],

        				'desc' => ['carreira.idcarreira' => SORT_DESC],

        				'label' => 'Carreira'

        			],

        				

        		]

        ]);




        $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;

        }


        $query->andFilterWhere([

            'idcolaborador' => $this->idcolaborador,


        ]);


        $query->andFilterWhere(['carreira_fk'=>$this->carreira_fk]);




        return $dataProvider;

    }




Now I have one more problem :)

The filters of "Carreira" and "Categoria Profissional" are dropDownList.

When I select a value (filter) in the "Carreira" column I want that the options shown in the second dropDownList "Categoria Profissional" to be the ones that are related with the value in "Carreira"!

I usually use ajax to to do what you need, but if you want to use Yii2 widget,

at this link you should fiind what you need: