Yii 1.1: exportablegridbehavior

Export filtered GridView as CSV
26 followers

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'));

Resources

Total 20 comments

#19481 report it
Niral Shakya at 2015/07/23 03:21am
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'];
....
#19124 report it
Hipogea at 2015/03/24 10:33am
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?

#19116 report it
Todd Anstis at 2015/03/20 06:40pm
Got it

Nevermind, I got it. Thanks!

#19115 report it
Todd Anstis at 2015/03/20 12:12pm
PHP version

I'm using PHP 5.5

#19114 report it
Clem at 2015/03/20 11:59am
PHP > 5.3

Todd Anstis, you need PHP 5.3 or above to use anonymous functions. Hope it helps you.

#19113 report it
Todd Anstis at 2015/03/20 11:44am
Export related field data?

Clem, I implemented your adjustments so that I could export related data but I'm getting an error:

Undefined variable: data

How are you resolving the $data variable in your controller?

#19108 report it
Clem at 2015/03/19 09:13am
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);
        }
    }
#18029 report it
Maxxer at 2014/08/28 04:01am
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);
         }
     }
#17878 report it
vijay p s at 2014/08/05 06:44am
Need more !

How can we work foreign key values..

#16462 report it
abundance at 2014/02/26 07:04am
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'],
 
      ));
#16116 report it
acy at 2014/01/21 09:04am
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

#14993 report it
stromgol at 2013/09/26 09:06pm
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!

#14992 report it
Gero at 2013/09/26 05:15pm
Re: Eport has_many relation

stromgol

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'));
#14989 report it
stromgol at 2013/09/26 04:14pm
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!

#14838 report it
stromgol at 2013/09/13 03:02pm
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!

#14623 report it
abundance at 2013/08/29 10:03am
Error

I am getting this error:

MyController and its behaviors do not have a method or closure named "renderExportGridButton".

#13813 report it
Gustavo Andrade at 2013/06/27 09:18pm
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'));

#13456 report it
deez at 2013/05/29 12:03pm
2 Grids in same controller?

Will this work if the same controller is responsible for multiple grids from multiple models?

#13349 report it
danschmidt5189 at 2013/05/23 05:34pm
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.

#12774 report it
Gustavo Andrade at 2013/04/11 09:37am
I Like

It worked great! Very simple to use

CONGRATULATIONS! :D

Leave a comment

Please to leave your comment.

Create extension