Take more control of CGridview and model searching (related model or another controller)

You are viewing revision #4 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.

« previous (#3)

Suppose you have an Application in Yii that manages hotels each one has many customers.

So we should have the below tables with attributes

hotel (id,name) customer (id,surname) customer_to_hotel (id, hotel_id,customer_id)

Suppose now you want to have a page that displays the info of the hotel and all customers of the specific hotel. Also you want all the links (sorter columns), search fields and the pagination of the CGridview work with another controller/action (suppose you want to have distinct control for the view and search of the customer into the Hotel Controller for reusability or/and other reasons)

How to achieve that ?

This Article explain how to do that

HotelController.php (controller)

//this action manage both view of the hotel and view search customer
    public function actionViewHotel($hotel_id=null) {
     
        $customerToHotel = new CustomerToHotel('search');
        $customerToHotel->unsetAttributes();

        if (isset($_GET['customerToHotel']))
            $customerToHotel->attributes = $_GET['customerToHotel'];

       $hotel = Hotel::model()->findByPk($hotel_id);
            if ($hotel===null)
                throw new CHttpException(404, 'The requested page does not exist.');
       
        $this->render('viewHotel', array('hotel' => $hotel,'customerToHotel'=>$customerToHotel));
    }
    
    //this action can be used by ajax (directly from CGridview)
    public function actionSearchCustomer() {
     
        $customerToHotel = new CustomerToHotel('search');
        $customerToHotel->unsetAttributes();

        if (isset($_GET['customerToHotel']))
            $customerToHotel->attributes = $_GET['customerToHotel'];
       
        $this->renderPartial('_searchCustomersByHotel', array('customerToHotel' => $customerToHotel));
    }

customerToHotel.php (model)

//an extra search for specific hotel and other parameters
  public function searchByHotel($hotel_id, $routeUrl = null, $templateParams = array()) {

        $criteria = new CDbCriteria;

        $criteria->compare('id', $this->id);
        $criteria->compare('hotel_id', $hotel_id);
        $criteria->compare('customer_id', $this->customer_id);
        
        $item_count = self::model()->count($criteria);

        $pages = new CPagination($item_count);
        $pages->setPageSize(10);
        
        $sort = new CSort();

        if (!empty($templateParams)) {
            $pages->params = $templateParams;
            $sort->params = $templateParams;
        }

        if ($routeUrl) {
            $pages->route = $routeUrl;
            $sort->route = $routeUrl;
        }
        
        return new CActiveDataProvider($this, array(
            'criteria' => $criteria,
            'pagination' => $pages,
            'sort' => $sort,
        ));
    }

_searchCustomersByHotel.php (view) //use a different action (or/and controller) and search method (only show customer filtered by specific hotel)

$this->widget('zii.widgets.grid.CGridView', array(
    'ajaxUrl' => $this->createUrl('searchCustomer', array('hotel_id' => $hotel->id)), // this takes care of the search using searchCustomer action and hodel id
    'dataProvider' => $customerToHotel->searchByHotel($hotel->id, 'searchCustomer', array('hotel_id' => $hotel->id)), //filter by hotel, routing to searchCustomer action, extra parameter of hotel id generated to the next request (like sort column links e.t.c)
    'filter' => $customerToHotel,

    'columns'=>array(
        'id',
        'hotel_id', //this should be the same for all rows (testing)
        'customer_id'
    ),
));

viewHotel.php (view) //display both hotel and customers in gridview

$this->widget('zii.widgets.CDetailView',
	'data'=>$hotel,
	'attributes'=>array(
		'id',
		'name',
	),
));

$this->renderPartial('_searchCustomersByHotel' , array('customerToHotel'=>$customerToHotel));