May 25, 2013

Enable CSV Import for all controllers/models in a CakePHP 2.x project

CakePHP LogoI often use phpMyAdmin to import csv data into projects I am building, this can be tedious as phpMyAdmin requires the number of fields and field order to match exactly for the csv import to work.
This frustration prompted me to explore the Utils plugin by CakeDC which contains a CsvImport behavior. I wanted this functionality for all of my controllers/models during the development process. Import functionality will eventually end up under the admin interface of my project. If you use admin routing and would like the import functionality only available through an admin url simply change the method name below to admin_import().

Install the CakeDC Utils plugin

Using git, clone the Utils Plugin Repo and checkout the 2.0 branch.
sudo git clone https://github.com/CakeDC/utils.git app/Plugin/Utils
cd app/Plugin/Utils
sudo git checkout 2.0

Enable the Utils plugin

Add the following line to app/Config/bootstrap.php to enable the plugin.
1
CakePlugin::load('Utils');

Enable the CsvImport behavior in AppModel

Add the following code to app/Model/AppModel.php. If you only want csv import capability for a specific controller, add it there.
1
2
3
4
5
public $actsAs = array(
    'Utils.CsvImport' => array(
        'delimiter'  => ','
    )
);

Create a shared View called Common

Create the directory app/View/Common and add the shared import.ctp view
    
mkdir app/View/Common
touch app/View/Common/import.csv
1
2
3
4
5
6
<h3>Import <?php echo Inflector::pluralize($modelClass);?> from CSV data file</h3>
<?php
    echo $this->Form->create($modelClass, array('action' => 'import', 'type' => 'file') );
    echo $this->Form->input('CsvFile', array('label'=>'','type'=>'file') );
    echo $this->Form->end('Submit');
?>

Add the import action to AppController

Paste the following code into app/Controller/AppController.php.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* CSV Import functionality for all controllers
*    
*/
function import() {
    $modelClass = $this->modelClass;
    if ( $this->request->is('POST') ) {
        $records_count = $this->$modelClass->find( 'count' );
        try {
            $this->$modelClass->importCSV( $this->request->data[$modelClass]['CsvFile']['tmp_name']  );
        } catch (Exception $e) {
            $import_errors = $this->$modelClass->getImportErrors();
            $this->set( 'import_errors', $import_errors );
            $this->Session->setFlash( __('Error Importing') . ' ' . $this->request->data[$modelClass]['CsvFile']['name'] . ', ' . __('column name mismatch.')  );
            $this->redirect( array('action'=>'import') );
        }
         
        $new_records_count = $this->$modelClass->find( 'count' ) - $records_count;
        $this->Session->setFlash(__('Successfully imported') . ' ' . $new_records_count ' records from ' . $this->request->data[$modelClass]['CsvFile']['name'] );
        $this->redirect( array('action'=>'index') );
    }
    $this->set('modelClass', $modelClass );
    $this->render('../Common/import');
} //end import()

Add an Import link to the Scaffolding View

If you are using scaffoling in your project and would like to add a link to the
import view, copy lib/Cake/View/Scaffold/index.ctp to app/View/Scaffold/index.ctp
and add the following after line 98.
1
2
3
4
echo "<li>" . $this->Html->link(
    __d('cake', 'New %s', Inflector::humanize(Inflector::underscore($_alias))),
    array('controller' => $_details['controller'], 'action' => 'add')
) . "</li>";
If you choose not to customize the scaffolding view then you can point your browser directly to the controller action, http://domain.com/controller_name/import

1 comment:

  1. http://www.pronique.com/blog/enable-csv-import-all-controllers-models-cakephp-2

    ReplyDelete