Cgridview With Custom Dataprovider That Doesn't Load All Records From Source (Lazy Loading)

I’m a 5-day Yii fan trying to learn the ropes of this great framework. I would really appreciate it if you could bear with me reading through my problem and help me out.

So here goes, I’m retrieving a list of products sold by a particular merchant of an online marketplace through its web service. When I query the web service like this


api.marketplace.com?merchant_id=1234

I’ll get back the response in JSON which consists of two main pieces of data

  1. The number of products sold by Merchant 1234 (total_products)

  2. The list (array) of the products sold (list_product). Each element in this array represents one distinct Product object with two fields: product_id and product_name respectively.

So if total_products = 934, there’ll be 934 elements in the array.

When I put it in codes, my derived DataProvider looks like this




class ProductDataProvider extends CDataProvider /* or should I extend other better class? */ {

    public function __construct($config = array()) {

	foreach($config as $key=>$value)

	    $this->$key=$value;

    }    


    protected function fetchData() {

        $request = "api.marketplace.com?merchant_id=1234";

        $response = retrieveProductsAndProcessJSONResponseIntoAppropriateDataStructure($request);

        $this->setTotalItemCount($response->total_products);


        if(($pagination=$this->getPagination())!==false)

            $pagination->setItemCount($this->getTotalItemCount());


        return $response->list_products;

    }


    protected function calculateTotalItemCount() {

        return $this->getTotalItemCount();

    }

}



My model




class Product extends CModel {

    public $product_id;

    public $product_name;


    public function display() {

        return new ProductDataProvider(array(

            'pagination'=>array(

		'pageSize'=>10,

        ));

    }

}



and my View




$this->widget('zii.widgets.grid.CGridView', array(

    'id'=>'product-grid',

    'dataProvider'=>$model->display(),

    'columns'=>array(

        'product_id',

        'product_name',

    ),

));



This works well with pagination and all that. However retrieving all 934 products is taking a bit too long. We can do this to the web service.


api.marketplace.com?merchant_id=1234&page_size=10&page_no=1

This will only retrieve the first 10 records. We can play around with the query string variable page_no to retrieve subsequent pages of products in groups of 10. The total_products returned from the web service will still be 934.

So my question is, how do I put this into play so that the ProductDataProvider only retrieves 10 product at any one time and load it into CGridView? The CGridView will have to know that there are a total of 934 products to enable pagination. User browsing to other pages of the CGridView will trigger the ProductDataProvider to retrieve the next 10 records corresponding to the page number being viewed. I hope that my long winded question is comprehensible.

Hi Twisted Whisper, welcome to the forum.

This is from the source code of CArrayDataProvider:

So, isn’t it really easy to implement pagination in your data provider too?

Hi softark, thanks for taking the time reading and replying.

But this means that I’ll have to load all 934 records into $this->rawData. My requirement is only to load a page of data from the web service (10 records). Do I create an array of 934 empty elements, and only populate 10 of them in the appropriate order in the array and then assign that array to $this->rawData to give the illusion to CGridView that there are indeed 934 elements (most of them empty but it doesn’t matter since the CGridView is only displaying 10 visible records while the empty ones are “invisible” hidden in other pages)

Scratch my #3 post. I think I get what you mean. I’ll try it out first and will update you on the outcome.

Yeah, I’m sure you got it.

I hope $pagination->getOffset() and $pagination->getLimit() will provide the necessary api parameters.