You are viewing revision #1 of this wiki article.
This is the latest version of this article.
How to create a simple CSV exporting for CGridView search results ¶
A few days ago, i worked for CGridView exporting functional for my client. He asked me to create a simple exporting to a CSV file a CGridView search results.
That's very easy. Let's start...
Create an export button in your template. ¶
I just created an Export Button above my grid.
<?php echo CHtml::button('Export', array('id'=>'export-button','class'=>'span-3 button')); ?>
Extending CGridView javascript code. ¶
This javascript code does:
- bind an .export() execution by clicking an export button, defined above.
- extinds an yiiGridView javascript class for function update.
[javascript] $('#export-button').on('click',function() { $.fn.yiiGridView.export(); }); $.fn.yiiGridView.export = function() { $.fn.yiiGridView.update('dates-grid',{ success: function() { $('#dates-grid').removeClass('grid-view-loading'); window.location = '". $this->createUrl('exportFile') . "'; }, data: $('.search-form form').serialize() + '&export=true' }); }
Add actions to your controller ¶
Because CGridView always uses for update the same action as initial render CGridView we need to use this action for our purpose in a next way: Add next peace of code at the top of action your CGridView render.
if(Yii::app()->request->getParam('export')) {
$this->actionExport();
Yii::app()->end();
}
It will run our actionExport if GET['export'] set.
Action Export (will use CGridView dataprovider defined in a model()->search()) ¶
public function actionExport()
{
$fp = fopen('php://temp', 'w');
/*
* Write a header of csv file
*/
$headers = array(
'd_date',
'client.clientFirstName',
'client.clientLastName',
'd_time',
);
$row = array();
foreach($headers as $header) {
$row[] = MODEL::model()->getAttributeLabel($header);
}
fputcsv($fp,$row);
/*
* Init dataProvider for first page
*/
$model=new MODEL('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['MODEL'])) {
$model->attributes=$_GET['MODEL'];
}
$dp = $model->search();
/*
* Get models, write to a file, then change page and re-init DataProvider
* with next page and repeat writing again
*/
while($models = $dp->getData()) {
foreach($models as $model) {
$row = array();
foreach($headers as $head) {
$row[] = CHtml::value($model,$head);
}
fputcsv($fp,$row);
}
unset($model,$dp,$pg);
$model=new MODEL('search');
$model->unsetAttributes(); // clear any default values
if(isset($_GET['MODEL']))
$model->attributes=$_GET['MODEL'];
$dp = $model->search();
$nextPage = $dp->getPagination()->getCurrentPage()+1;
$dp->getPagination()->setCurrentPage($nextPage);
}
/*
* save csv content to a Session
*/
rewind($fp);
Yii::app()->user->setState('export',stream_get_contents($fp));
fclose($fp);
}
Last action to download CSV file ¶
public function actionGetExportFile()
{
Yii::app()->request->sendFile('export.csv',Yii::app()->user->getState('export'));
Yii::app()->user->clearState('export');
}
..
i've built something similar in one of my yii-apps. it gets a bit annoying if you need lots of csv exports because there is lots of code to write and maintain which does almost exactly what the corresponding cgridview does only using another data export format. maybe an extension to cgridview would be the better way.
EExcelView
Well, I just use my extension EExcelView to do this :)
this article not how to write an extension
is about how to make csv exporting. it's for understanding how to create it fast. When someone will need alot of exporting, he will write a class for this.
Unnecessary pagination loop
When I tried this code I kept getting an infinite loop accessing the data. Then I realized that the entire outer loop that handles pagination isn't needed. Instead, just set the pagination of the CActiveDataProvider returned by the model search method to false.
In other words, insert this after the first call of the search function:
$dp->setPagination(false);
Then eliminate the outer loop and all the pagination stuff. So instead of this block of code:
$dp = $model->search(); /* * Get models, write to a file, then change page and re-init DataProvider * with next page and repeat writing again */ while($models = $dp->getData()) { foreach($models as $model) { $row = array(); foreach($headers as $head) { $row[] = CHtml::value($model,$head); } fputcsv($fp,$row); } unset($model,$dp,$pg); $model=new MODEL('search'); $model->unsetAttributes(); // clear any default values if(isset($_GET['MODEL'])) $model->attributes=$_GET['MODEL']; $dp = $model->search(); $nextPage = $dp->getPagination()->getCurrentPage()+1; $dp->getPagination()->setCurrentPage($nextPage); }
We're just left with this:
$dp = $model->search(); $dp->setPagination(false); /* * Get models, write to a file */ $models = $dp->getData(); foreach($models as $model) { $row = array(); foreach($headers as $head) { $row[] = CHtml::value($model,$head); } fputcsv($fp,$row); }
Thanks!
Instead of dropping an extension somewhere, this article shows me how the mechanisms work, so I can adapt it to my needs easily.
Just a small thing: the last action should be named 'actionExportFile' instead of 'actionGetExportFile' or else the javascript at the top should be adapted.
Where
Where did you add that javaScript ?
+
By the way, thanks for the how to !
The JavaScript goes...
@CTala: The JavaScript goes in the same file as the first code snippet (the one that makes the Export button). That is, in the same view file that contains the CGridView. It makes the button work.
Thanks Steve for the amswer.
I just got confused because we are not adding it ass a Yii Add Script function.
So this JS I just need to put in the javascript part of the main layout in my case.
Why two requests?
Why sending an ajax request and saving the filter in the setState and then doing another plain request to send the file? Can't we do this with only one request?
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.