Using grids

Zii grids are very useful to quickly create efficient application admin pages or any pages on which you need to manage data.

Let's use Gii to generate a grid, see how it works, and how we can customize it.

Getting ready

Carry out the following steps:

  1. Create a new application using yiic webapp as described in the official guide.
  2. Download the Sakila database from http://dev.mysql.com/doc/index-other.html. Execute the downloaded SQLs (first schema then data).
  3. Configure the database connection in protected/config/main.php.
  4. Use Gii to create models for the customer, address, and city tables.

How to do it...

  1. Open Gii, select Crud Generator, and enter Customer into the Model Class field. Press Preview and then Generate.
  2. Gii will generate a controller in protected/controllers/CustomerController.php and a group of views under protected/views/customer/.
  3. Run the customer controller and go to the Manage Customer link. After logging in you should see the grid generated, as shown in the following screenshot:
    How to do it...

How it works...

Let's start with the admin action of our customer controller:

public function actionAdmin()
{
  $model=new Customer('search'),
  $model->unsetAttributes();  // clear any default values
  if(isset($_GET['Customer']))
    $model->attributes=$_GET['Customer'];

  $this->render('admin',array(
    'model'=>$model,
  ));
}

The Customer model is created with the search scenario, all attribute values are cleaned up, and then filled up with data from $_GET. On the first request, $_GET is empty but when you are changing the page, or filtering by the first_name attribute using the input field below the column name, the following GET parameters are passed to the same action via an AJAX request:

Customer[address_id] = 
Customer[customer_id] = 
Customer[email] = 
Customer[first_name] = alex
Customer[last_name] = 
Customer[store_id] = 
Customer_page = 2
ajax = customer-grid

Since the scenario is search, the corresponding validation rules from Customer::rules are applied. For the search scenario, Gii generates a safe rule that allows for mass assigning of all fields:

array('customer_id, store_id, first_name, last_name, email, address_id, active, create_date, last_update', 'safe', 'on'=>'search'),

Then the model is passed to a view, protected/views/customer/admin.php. It renders an advanced search form and then passes the model to the grid widget:

<?php $this->widget('zii.widgets.grid.CGridView', array(
  'id'=>'customer-grid',
  'dataProvider'=>$model->search(),
  'filter'=>$model,
  'columns'=>array(
    'customer_id',
    'store_id',
    'first_name',
    'last_name',
    'email',
    'address_id',
    /*
    'active',
    'create_date',
    'last_update',
    */
    array(
      'class'=>'CButtonColumn',
    ),
  ),
)); ?>

Columns used in the grid are passed to columns. When just a name is passed, the corresponding field from the data provider is used.

Also we can use a custom column represented by a class specified. In this case we are using CButtonColumn that renders the view, update, and delete buttons that are linked to the same named actions and pass the row ID to them, so action can be done to a model representing a specific row from the database.

The filter property accepts a model filled with data. If it's set, a grid will display multiple text fields at the top that the user can fill to filter the grid.

The dataProvider property takes an instance of data provider. In our case it's returned by the model's search method:

public function search()
{
  // Warning: Please modify the following code to remove attributes that
  // should not be searched.

  $criteria=new CDbCriteria;

  $criteria->compare('customer_id',$this->customer_id);
  $criteria->compare('store_id',$this->store_id);
  $criteria->compare('first_name',$this->first_name,true);
  $criteria->compare('last_name',$this->last_name,true);
  $criteria->compare('email',$this->email,true);
  $criteria->compare('address_id',$this->address_id);
  $criteria->compare('active',$this->active);
  $criteria->compare('create_date',$this->create_date,true);
  $criteria->compare('last_update',$this->last_update,true);

  return new CActiveDataProvider(get_class($this), array(
    'criteria'=>$criteria,
  ));
}

This method is called after the model was filled with the $_GET data from the filtering fields, so we can use field values to form the criteria for the data provider. In this case all numeric values are compared exactly while string values are compared using partial matches.

There's more...

Code generated by Gii can be useful in a lot of simple cases but often we need to customize it.

Using data from related Active Record models

In the code, the generated grid displays the store and address IDs instead of corresponding values. Let's fix the address and display the city, district, and address instead of just the ID.

  1. We have the following relations in the Customer model:
    public function relations()
    {
      // NOTE: you may need to adjust the relation name and the related
      // class name for the relations automatically generated below.
      return array(
        'address' => array(self::BELONGS_TO, 'Address', 'address_id'),
        'store' => array(self::BELONGS_TO, 'Store', 'store_id'),
        'payments' => array(self::HAS_MANY, 'Payment', 'customer_id'),
        'rentals' => array(self::HAS_MANY, 'Rental', 'customer_id'),
      );
    }
  2. We need to load the address data along with the model. That means we have to add these to the with part of the criteria passed to the data provider in Customer::search. Since the address includes the city ID, we need to load the city data using the city relation of the Address model.
    public function search()
    {
      // Warning: Please modify the following code to remove 
      //attributes that should not be searched.
    
      $criteria=new CDbCriteria;
    
      $criteria->with = array('address' => array(
        'with' => 'city'
      )); …

    Note

    Each relation used in the with part of the criteria can be specified in the way shown previously where the key is the relation name and the value is an array representing criteria. These criteria will be applied to the related model.

  3. Now let's modify the columns list passed to a grid in protected/views/customer/admin.php:
    'columns'=>array(
      'customer_id',
      'store_id',
      'first_name',
      'last_name',
      'email',
      array(
        'name'=>'address',
        'value'=>'$data->address->address.", ".$data->address->city->city.", ".$data->address->district',
    ),
  4. In the preceding code, we are using a relation name of name and setting value to a string consisting of address, city, and district.

    Now check the grid. It should now list the address, city, and district in the address field, as shown in the following screenshot:

    Using data from related Active Record models
  5. The only problems now are that addresses are not sortable and filtering using address does not work. Let's fix the sorting first. In the Customer::search method we need to create a CSort instance, configure it, and pass it to the data provider:
    $sort = new CSort;
    $sort->attributes = array(
      'address' => array(
        'asc' => 'address, city, district',
        'desc' => 'address DESC, city DESC, district DESC',
      ),
      '*',
    );
    return new CActiveDataProvider(get_class($this), array(
      'criteria'=>$criteria,
      'sort'=>$sort,
    ));

    CSort::attributes accepts a list of sortable attributes. We want all the Customer attributes to be sortable so we are adding * to the list. Additionally we are specifying SQL for both ascending and descending sorting of the address attribute.

    That's it! Sorting should work.

  6. Now let's fix filtering. First we need to add address to the safe attributes list in the model's rules method. Then we replace the comparison in Customer::search:
    $criteria->compare('address_id',$this->address_id);

    This should be replaced with:

    $criteria->compare('address',$this->address,true);
    $criteria->compare('district',$this->address,true,"OR");
    $criteria->compare('city',$this->address,true,"OR");
  7. When a user enters california in the address filter field, the three compare methods mentioned in the previous step will result in SQL, like the following:
    WHERE address LIKE '%california%'
    OR district LIKE '%california%'
    OR city LIKE '%california%'
    Using data from related Active Record models

Further reading

To learn more about grids and their properties, refer to the following resources:

See also

  • The Using data providers recipe
  • The Using lists recipe
  • The Creating custom grid columns recipe
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.109.30