Quantcast
Channel: Live News for Yii Framework
Viewing all articles
Browse latest Browse all 3375

[Wiki] Filter & Sort by calculated/related fields in GridView Yii 2.0

$
0
0

This wiki explains how to add calculated fields into your Yii Framework 2.0 gridview with filtering and sorting.

Example Structure

Let's say you have the following tables:

/* Countries */
CREATE TABLE `tbl_country` (
    `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'Unique country identifier',
    `country_name` VARCHAR(150) NOT NULL COMMENT 'Country name',
     PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Country master table';
 
/* Persons */
CREATE TABLE `tbl_person` (
    `id` INT(11) NOT NULL AUTO_INCREMENT COMMENT 'Unique person identifier',
    `first_name` VARCHAR(60) NOT NULL COMMENT 'First name',
    `last_name` VARCHAR(60) NOT NULL COMMENT 'Last name',
    `country_id` INT(11) COMMENT 'Residing Country',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Person master table';
 
/* Foreign Key */
ALTER TABLE `tbl_person`
ADD CONSTRAINT `tbl_person_FK1` 
FOREIGN KEY (`country_id`) 
REFERENCES `tbl_country` (`id`) 
, ADD INDEX `tbl_person_FK1` (`country_id` ASC);

Prerequisites

Generate your models and CRUD via Gii. You should now have the following model classes generated:

  1. Person: The base model for tbl_person
  2. PersonSearch: The search and filtering model for Person within gridview.
  3. Country: The search and filtering model for tbl_country.
  4. CountrySearch: The search and filtering model for Country within gridview.

Gridview Scenarios

Let's consider 2 scenarios you want to display in the GridView within the index view generated for Person.

Scenario 1: Calculated field from same table

An example describing how to add a fullName column within the Person grid with sorting and filtering. The field fullName will be concatenation of first_name and last_name separated by space.

Scenario 2: Calculated field from related table

An example describing how to add a countryName column within the Person grid with sorting and filtering. The field countryName will be generated based on country_id using foreign key relation with the tbl_country.

Scenario 1 Steps

STEP 1: Add a getter function to your base Person model:

Setup base model

/* Getter for person full name */
public function getFullName() {
    return $this->first_name . ' ' . $this->last_name;
}
 
/* Your model attribute labels */
public function attributeLabels() {
    return [
        /* Your other attribute labels */
        'fullName' => Yii::t('app', 'Full Name')
    ];
}

STEP 2: Add an attribute fullName to your model PersonSearch and configure your rules.

Setup search model

/* your calculated attribute */
public $fullName;
 
/* setup rules */
public function rules() {
   return [
    /* your other rules */
    [['fullName'], 'safe']
   ];
}
 
/**
 * setup search function for filtering and sorting 
 * based on fullName field
 */
public function search($params) {
    $query = Person::find();
    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);
 
    /**
     * Setup your sorting attributes
     * Note: This is setup before the $this->load($params) 
     * statement below
     */
    $dataProvider->setSort([
        'attributes' => [
            'id',
            'fullName' => [
                'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
                'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
                'label' => 'Full Name',
                'default' => SORT_ASC
            ],
            'country_id'
        ]
    ]);
 
    if (!($this->load($params) && $this->validate())) {
        return $dataProvider;
    }
 
    $this->addCondition($query, 'id');
    $this->addCondition($query, 'first_name', true);
    $this->addCondition($query, 'last_name', true);
    $this->addCondition($query, 'country_id');
 
    /* Setup your custom filtering criteria */
 
    // filter by person full name
    $query->andWhere('first_name LIKE "%' . $this->fullName . '%" ' .
        'OR last_name LIKE "%' . $this->fullName . '%"'
    );
 
    return $dataProvider;
}

STEP 3: Configure your gridview columns in your view index file

Setup view file

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        'id',
        'fullName',
        ['class' => 'yii\grid\ActionColumn'],
    ]
]);

Voila, your fullName column in the grid view should be available for sort and filtering.

Scenario 2 Steps

STEP 1: Ensure your Person model has a relation defined to the Country model. You can also implement a getter for CountryName.

Setup base model

/* ActiveRelation */
public function getCountry()
{
    return $this->hasOne(Country::className(), ['id' => 'country_id']);
}
 
/* Getter for country name */
public function getCountryName() {
    return $this->country->country_name;
}
 
/* Your model attribute labels */
public function attributeLabels() {
    return [
        /* Your other attribute labels */
        'fullName' => Yii::t('app', 'Full Name'),
        'countryName' => Yii::t('app', 'Country Name')
    ];
}

STEP 2: Add an attribute countryName to your model PersonSearch and configure your rules.

Setup search model

/* your calculated attribute */
public $countryName;
 
/* setup rules */
public function rules() {
   return [
    /* your other rules */
    [['countryName'], 'safe']
   ];
}
 
/**
 * setup search function for filtering and sorting 
 * based on fullName field
 */
public function search($params) {
    $query = Person::find();
    $dataProvider = new ActiveDataProvider([
        'query' => $query,
    ]);
 
    /**
     * Setup your sorting attributes
     * Note: This is setup before the $this->load($params) 
     * statement below
     */
     $dataProvider->setSort([
        'attributes' => [
            'id',
            'fullName' => [
                'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
                'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
                'label' => 'Full Name',
                'default' => SORT_ASC
            ],
            'countryName' => [
                'asc' => ['tbl_country.country_name' => SORT_ASC],
                'desc' => ['tbl_country.country_name' => SORT_DESC],
                'label' => 'Country Name'
            ]
        ]
    ]);
 
    if (!($this->load($params) && $this->validate())) {
        return $dataProvider;
    }
 
    $this->addCondition($query, 'id');
    $this->addCondition($query, 'first_name', true);
    $this->addCondition($query, 'last_name', true);
    $this->addCondition($query, 'country_id');
 
    /* Add your filtering criteria */
 
    // filter by person full name
    $query->andWhere('first_name LIKE "%' . $this->fullName . '%" ' .
        'OR last_name LIKE "%' . $this->fullName . '%"'
    );
 
    // filter by country name
    $query->joinWith(['country' => function ($q) {
        $q->where('tbl_country.country_name LIKE "%' . $this->countryName . '%"');
    }]);
 
    return $dataProvider;
}

STEP 3: Configure your gridview columns in your view index file

Setup view file

echo GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],
        'id',
        'fullName',
        'countryName',
        ['class' => 'yii\grid\ActionColumn'],
    ]
]);

Voila, your countryName column in the grid view should be available for sort and filtering.


Viewing all articles
Browse latest Browse all 3375

Trending Articles