This behaviour allows you to add an 'Export' button near your gridview, that will download the grid filtered data as CSV.
Changelog ¶
- v2.0
- Totally rewritten code
- Allows export of arrays, single model and CActiveDataProvider ("search" result)
- Allows export of multiple models on same csv
- New method (better and cleaner) for generation of export url with grid search filters
- v1.1
- Cleaner method of file generation allows to handle huge datasets.
- Export data is now generated on php output. No more session temporal storage.
- CSV downloaded is now done without redirections.
- GridView id automatically fetch from widget
Requirements ¶
- Yii Framework v1.1.13 or greater (Dependecy with system.web.CDataProviderIterator)
- An explicit rule matching export action on urlManger like:
'<controller:\w+>/<action:\w+>' => '<controller>/<action>'
Available behavior params ¶
- csvDelimiter Delimiter used on the csv generated rows. Default ','
- csvEnclosure Enclosure used on the csv generated rows. Default '"'
- filename Filename poposed to the browser when downloading the file. Default 'export.csv'
- buttonId Id for the export button. Default 'export-button'
- exportParam GET param added to the grid url, representing the 'export' command. Default 'exportCSV'
Available behavior methods ¶
- public exportCSV(mixed $data, $attributes = array(), $endApplication = true, $endLineCount = 0)
mixed $data Data to be exported. CModel, CDataProvider or Array.
Array $attributes Attributes of $data to be exported.
boolean $endApplication True to end CApplication excecution.
integer $endLineCount Number of empty rows to add after this data. - public isExportRequest()
returns boolean True if the request have exportParam, False otherwise. - public renderExportGridButton(CGridView $gridWidget, $label = 'Export', $htmlOptions = array())
CGridView $gridWidget CGridView widget.
String $label Export button label.
Array $htmlOptions Export button additional HTML options.
Usage ¶
Step 1: Place file ¶
Place ExportableGridBehavior.php file on application components directory.
Step 2: Add the behavior to the controller ¶
Add the behavior ExportableGridBehavior to your Controller
public function behaviors() {
return array(
'exportableGrid' => array(
'class' => 'application.components.ExportableGridBehavior',
'filename' => 'PostsWithUsers.csv',
'csvDelimiter' => ';', //i.e. Excel friendly csv delimiter
));
}
Step 3: Some magic ¶
Call the exportCSV() method on your 'admin' Action, just before the 'render' method call. Like This:
class PostController extends CController {
. . .
public function actionAdmin() {
$model = new Post('search');
$model->unsetAttributes();
if (isset($_GET['Post']))
$model->attributes = $_GET['Post'];
if ($this->isExportRequest()) { //<==== [[ADD THIS BLOCK BEFORE RENDER]]
//set_time_limit(0); //Uncomment to export lage datasets
//Add to the csv a single line of text
$this->exportCSV(array('POSTS WITH FILTER:'), null, false);
//Add to the csv a single model data with 3 empty rows after the data
$this->exportCSV($model, array_keys($model->attributeLabels()), false, 3);
//Add to the csv a lot of models from a CDataProvider
$this->exportCSV($model->search(), array('id', 'title', 'user.id', 'user.name'));
}
$this->render('admin', array(
'model' => $model,
));
}
. . .
}
Step 4: Render export button ¶
Add the export button to the View that renders the CGridView.
//Capture your CGridView widget on a variable
//$gridWidget=$this->widget('bootstrap.widgets.TbGridView', array( . . .
$this->renderExportGridButton($gridWidget,'Export Grid Results',array('class'=>'btn btn-info pull-right'));
erro
passei o id da minha grid e o seguinte erro:
include(CDataProviderIterator.php) [function.include]: failed to open stream: No such file or directory (D:\wamp\www\framework\YiiBase.php:418)
RE: erro
marcoaurelio:
This extension needs Yii 1.1.13 or higher... because it uses CDataProviderIterator.php included in Yii 1.1.13.
If you still want to use this behavior with Yii 1.1.12 or less, please download CDataProviderIterator.php and save it in components folder of your application.
Remember that you also need to import the component like:
Yii::import('application.components.CDataProviderIterator');
I like this, but...
I do like your work as it is easy and just what I was looking for.
Beware however, the extension assumes the grid is populated using $model->search(). As one must provide input such as the grid-id, I incorrectly assumed it would magically figure use the same dataprovider as the one actually used by the grid.
It would be awesome if this could be overwritten through parameter.
Thanks
I Like
It worked great! Very simple to use
CONGRATULATIONS! :D
Detect loading class & customize pagination
Hi there,
Nice behavior! I'd just add two features and one modification:
1) Auto-detect the loading CSS class that's added / removed from the gridView. You can get the CSS class by modifying the code in renderExportGridButton():
var grid = $('#" . $this->gridId . "'), loadingClass = grid.yiiGridView.settings.loadingClass, dataVar = grid.yiiGridView('getUrl'), idx = dataVar.indexOf('?'); ... more javascript... success: function() { grid.removeClass(loadingClass); window.location = '".$fileDownloadUrl."'; },
2) Add an onclick prompt that asks users whether they want to export all records, just the current page, or just the selected rows. This would require a bit more customization but all in all isn't hard.
3) Change exportGrid() and generateFileOnMem() to use a dataProvider instance of a model instance. You can always extract the model's attribute info from the dataProvider, anyway, and doing this reduces coupling with the model's search() implementation.
2 Grids in same controller?
Will this work if the same controller is responsible for multiple grids from multiple models?
Replace the button for a link?
How to replace the button for a link?
$this->renderExportGridButton('Link here',array('class'=>'btn btn-info pull-right'));
Error
I am getting this error:
MyController and its behaviors do not have a method or closure named "renderExportGridButton".
Works like a charm
I just installed the new version (1.1) and I've been able to export huge csv files (50 000 rows). For the benefits of newbies like me, I want to add that to make it work, you have to add the $gridWidget variable at the beginning of the cgridview line in the admin page, as well as in the export button line. I mention it because it wasn't like that in the previous version and this option is greyed out in the above example(step 4, render export).
Thanks a lot!
Eport has_many relation
I just installed the last version (2.0) and it's a great extension, really easy to use. I modified it a little bit to show the date at the end of the filename. I have one question though. I have some classes with has_many relations and to access the related products in cgridview, I have to use a line like this 'value'=>'$data->getproducts()'. getproducts() is declared in my model. It's a loop with an implode function. The products are shown in cgridview, separated by a comma, but I can't find a way to export them with this behavior. Is there a way? thank you!
Re: Eport has_many relation
If your model has a getProducts() method, im pretty sure you should be able to get the method return value by adding its name without the 'get' part to the attributes array of exportCSV..
Somethig like this:
$this->exportCSV($model->search(), array('id', 'products'));
Re: Eport has_many relation
Thank you Gero!! I tried like you said and it worked. I just have a lot to learn in programming, particulary OOP in PHP. But Yii is great for that. Thanks again!
404 on export
Hey there! I get "File not found" when I click on the export button.
Does it have to do with my url structure?
/user/admin?exportCSV=1
Please assist
Using CArrayDataProvider as data source
Is it possible to use this extension with a CArrayDataProvider such as below. What should my syntax in the Controller look like? Below is the definition of my data provider. But my Controller code is not giving me a valid CSV - it provides blank data.
Model Code: searchProfitCurrency method
$rawData=Yii::app()->db->createCommand($selectStatment)->queryAll(); return new CArrayDataProvider($rawData, array( 'id'=>'id', 'sort'=>array( 'attributes'=>array( 'Currency', 'profitMarginCurrency' ), ), 'pagination'=>array( 'pageSize'=>50, ), ));
Controller Code:
$search_dataProvider = $model->searchProfitCurrency(); $this->exportCSV($search_dataProvider, array( $search_dataProvider['Currency'], $search_dataProvider['profitMarginCurrency'], ));
Need more !
How can we work foreign key values..
Problems when search attributes is an array
In my CGridView I have an advanced search function which can filter for more than one value (checkboxes). The view is filtered correctly but when you try to export with the filter values
$this->exportCSV($model, $model->attributeNames(), false, 3);
this resulted in an error:
~~~
Array to string conversion (protected/components/Export
ableGridBehavior.php:102)
~~~
I don't know if there's a better solution but I solved with:
@@ -98,7 +98,8 @@ class ExportableGridBehavior extends CBehavior { foreach ($attributes as $attr) { $row = array(); $row[] = $cModel->getAttributeLabel($attr); - $row[] = CHtml::value($cModel, $attr); + $value = CHtml::value($cModel, $attr); + $row[] = is_array($value) ? implode(", ",$value) : $value; fputcsv($fileHandle, $row, $this->csvDelimiter, $this->csvEnclosure); } }
Some modification
I have modified this great extension to display different label as declarated in Model file. I can further personnalize values as in CGridView Or CDetailView.
Example in your Controller Action:
$this->exportCSV($model->search(), array( array( 'label' => 'ID Column', 'name' => 'id', ), 'name', array( 'label' => Yii::t('app', "Date"), 'value' => function ($data) { return Yii::app()->dateFormatter->formatDateTime($data->date,'medium',null); } ), ... ));
The modification:
private function csvRowHeaders($fileHandle, $attributes, CModel $model) { $row = array(); foreach ($attributes as $attr) { if (is_array($attr)) { if (isset($attr['label'])) { $row[] = $attr['label']; } elseif (isset($attr['name'])) { $row[] = $model->getAttributeLabel($attr['name']); } else { $row[] = ''; } } else { $row[] = $model->getAttributeLabel($attr); } } fputcsv($fileHandle, $row, $this->csvDelimiter, $this->csvEnclosure); } private function csvRowModels($fileHandle, Traversable $cModels, $attributes) { foreach ($cModels as $cModel) { $row = array(); foreach ($attributes as $attr) { if (is_array($attr)) { if (isset($attr['name'])) { $row[] = CHtml::value($cModel, $attr['name']); } elseif (isset($attr['value'])) { $row[] = is_object($attr['value']) && get_class($attr['value']) === 'Closure' ? call_user_func($attr['value'], $cModel) : $attr['value']; } else { $row[] = ''; } } else { $row[] = CHtml::value($cModel, $attr); } } fputcsv($fileHandle, $row, $this->csvDelimiter, $this->csvEnclosure); } }
Export related field data?
Clem, I implemented your adjustments so that I could export related data but I'm getting an error:
How are you resolving the $data variable in your controller?
PHP > 5.3
Todd Anstis, you need PHP 5.3 or above to use anonymous functions. Hope it helps you.
PHP version
I'm using PHP 5.5
Got it
Nevermind, I got it. Thanks!
Doesn't work , since 10,000 rows
Please, I need some Help;
I worked fine, with this extension but , when the CDataProvider , returns up to 10 000 records, the extension does'n t work, Only ignore the function. What I doing wrong?, Could be a bad Configuration PHP ini?
Modification of content value in csv
It generate csv with data values same as field in table. But I want modified value.
Like table field have value 1,2,3 and I want FIRST, SECOND, THIRD instead of table value.
I have created modification function in model
How can i use it so csv generate with modified value.
Thanks.
@Clem
Thanks your function was really helpful.
.... if (isset($attr['value'])) { $row[] = is_object($attr['value']) && get_class($attr['value']) === 'Closure' ? call_user_func($attr['value'], $cModel) : $attr['value']; ....
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.