• Stars
    star
    175
  • Rank 217,368 (Top 5 %)
  • Language
    PHP
  • License
    MIT License
  • Created about 12 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

CakePHP: A view class for generating CSV

CI Coverage Status Total Downloads Latest Stable Version Software License

CsvView Plugin

Quickly enable CSV output of your model data.

This branch is for CakePHP 4.x. For details see version map.

Background

I needed to quickly export CSVs of stuff in the database. Using a view class to iterate manually would be a chore to replicate for each export method, so I figured it would be much easier to do this with a custom view class, like JsonView or XmlView.

Installation

composer require friendsofcake/cakephp-csvview

Enable plugin

Load the plugin by running command

bin/cake plugin load CsvView

Usage

To export a flat array as a CSV, one could write the following code:

public function export()
{
    $data = [
        ['a', 'b', 'c'],
        [1, 2, 3],
        ['you', 'and', 'me'],
    ];

    $this->set(compact('data'));
    $this->viewBuilder()
        ->setClassName('CsvView.Csv')
        ->setOption('serialize', 'data');
}

All variables that are to be included in the csv must be specified in the serialize view option, exactly how JsonView or XmlView work.

It is possible to have multiple variables in the csv output:

public function export()
{
    $data = [['a', 'b', 'c']];
    $data_two = [[1, 2, 3]];
    $data_three = [['you', 'and', 'me']];

    $serialize = ['data', 'data_two', 'data_three'];

    $this->set(compact('data', 'data_two', 'data_three'));
    $this->viewBuilder()
        ->setClassName('CsvView.Csv')
        ->setOption('serialize', $serialize);
}

If you want headers or footers in your CSV output, you can specify either a header or footer view option. Both are completely optional:

public function export()
{
    $data = [
        ['a', 'b', 'c'],
        [1, 2, 3],
        ['you', 'and', 'me'],
    ];

    $header = ['Column 1', 'Column 2', 'Column 3'];
    $footer = ['Totals', '400', '$3000'];

    $this->set(compact('data'));
    $this->viewBuilder()
        ->setClassName('CsvView.Csv')
        ->setOptions([
            'serialize' => 'data',
            'header' => $header,
            'footer' => $footer,
        ]);
}

You can also specify the delimiter, end of line, newline, escape characters and byte order mark (BOM) sequence using delimiter, eol, newline, enclosure and bom respectively:

public function export()
{
    $data = [
        ['a', 'b', 'c'],
        [1, 2, 3],
        ['you', 'and', 'me'],
    ];

    $this->set(compact('data'));
    $this->viewBuilder()
        ->setClassName('CsvView.Csv')
        ->setOptions([
            'serialize' => 'data',
            'delimiter' => chr(9),
            'enclosure' => '"',
            'newline' => '\r\n',
            'eol' => '~',
            'bom' => true,
        ]);
}

The defaults for these options are:

  • delimiter: ,
  • enclosure: "
  • newline: \n
  • eol: \n
  • bom: false
  • setSeparator: false

The eol option is the one used to generate newlines in the output. newline, however, is the character that should replace the newline characters in the actual data. It is recommended to use the string representation of the newline character to avoid rendering invalid output.

Some reader software incorrectly renders UTF-8 encoded files which do not contain byte order mark (BOM) byte sequence. The bom option is the one used to add byte order mark (BOM) byte sequence beginning of the generated CSV output stream. See Wikipedia article about byte order mark for more information.

The setSeparator option can be used to set the separator explicitly in the first line of the CSV. Some readers need this in order to display the CSV correctly.

If you have complex model data, you can use the extract view option to specify the individual Hash::extract()-compatible paths or a callable for each record:

public function export()
{
    $posts = $this->Posts->find();
    $header = ['Post ID', 'Title', 'Created'];
    $extract = [
        'id',
        function (array $row) {
            return $row['title'];
        },
        'created'
    ];

    $this->set(compact('posts'));
    $this->viewBuilder()
        ->setClassName('CsvView.Csv')
        ->setOptions([
            'serialize' => 'posts',
            'header' => $header,
            'extract' => $extract,
        ]);
}

If your model data contains some null values or missing keys, you can use the null option, just like you'd use delimiter, eol, and enclosure, to set how null values should be displayed in the CSV.

null defaults to ''.

Automatic view class switching

You can use router's extension parsing feature and the RequestHandlerComponent to automatically have the CsvView class switched in as follows.

Enable csv extension parsing for all routes using Router::extensions('csv') in your app's routes.php or using $routes->addExtensions() within required scope.

// PostsController.php

// In your controller's initialize() method:
$this->loadComponent('RequestHandler');

// Controller action
public function index()
{
    $posts = $this->Posts->find();
    $this->set(compact('posts'));

    if ($this->request->is('csv')) {
        $serialize = 'posts';
        $header = array('Post ID', 'Title', 'Created');
        $extract = array('id', 'title', 'created');

        $this->viewBuilder()->setOptions(compact('serialize', 'header', 'extract'));
    }
}

With the above controller you can now access /posts.csv or use Accept header text/csv to get the data as csv and use /posts to get normal HTML page.

For really complex CSVs, you can also use your own view files. To do so, either leave serialize unspecified or set it to null. The view files will be located in the csv subdirectory of your current controller:

// View used will be in templates/Posts/csv/export.php
public function export()
{
    $posts = $this->Posts->find();
    $this->set(compact('posts'));
    $this->viewBuilder()
        ->setClassName('CsvView.Csv')
        ->setOption('serialize', null);
}

Setting a different encoding to the file

If you need to have a different encoding in you csv file you have to set the encoding of your data you are passing to the view and also set the encoding you want for the csv file. This can be done by using dataEncoding and csvEncoding:

The defaults are:

  • dataEncoding: UTF-8
  • csvEncoding: UTF-8

** Only if those two variable are different your data will be converted to another encoding.

CsvView uses the iconv extension by default to encode your data. You can change the php extension used to encode your data by setting the transcodingExtension option:

$this->viewBuilder()->setOption('transcodingExtension', 'mbstring');

The currently supported encoding extensions are as follows:

  • iconv
  • mbstring

Setting the downloaded file name

By default, the downloaded file will be named after the last segment of the URL used to generate it. Eg: example.com/my-controller/my-action would download my-action.csv, while example.com/my-controller/my-action/first-param would download first-param.csv.

In IE you are required to set the filename, otherwise it will download as a text file.

To set a custom file name, use the Response::withDownload() method. The following snippet can be used to change the downloaded file from export.csv to my-file.csv:

public function export()
{
    $data = [
        ['a', 'b', 'c'],
        [1, 2, 3],
        ['you', 'and', 'me'],
    ];

    $this->setResponse($this->getResponse()->withDownload('my-file.csv'));
    $this->set(compact('data'));
    $this->viewBuilder()
        ->setClassName('CsvView.Csv')
        ->setOption('serialize', 'data');
}

Using a specific View Builder

In some cases, it is better not to use the current controller's View Builder $this->viewBuilder() as any call to $this->render() will compromise any subsequent rendering.

For example, in the course of your current controller's action, if you need to render some data as CSV in order to simply save it into a file on the server.

Do not forget to add to your controller:

use Cake\View\ViewBuilder;

So you can create a specific View Builder:

// Your data array
$data = [];

// Options
$serialize = 'data';
$delimiter = ',';
$enclosure = '"';
$newline = '\r\n';

// Create the builder
$builder = new ViewBuilder();
$builder
    ->setLayout(false)
    ->setClassName('CsvView.Csv')
    ->setOptions(compact('serialize', 'delimiter', 'enclosure', 'newline'));

// Then the view
$view = $builder->build($data);
$view->set(compact('data'));

// And Save the file
file_put_contents('/full/path/to/file.csv', $view->render());

License

The MIT License (MIT)

More Repositories

1

awesome-cakephp

A curated list of amazingly awesome CakePHP plugins, resources and shiny things.
908
star
2

cakephp-upload

CakePHP: Handle file uploading sans ridiculous automagic
PHP
551
star
3

CakePdf

CakePHP plugin for creating and/or rendering PDFs, supporting several popular PDF engines.
PHP
375
star
4

crud

Production-grade rapid controller development with built in love for API and Search
PHP
374
star
5

bootstrap-ui

CakePHP: Transparently use Bootstrap
PHP
338
star
6

search

CakePHP: Easy model searching
PHP
170
star
7

vagrant-chef

a vagrant installation with all the necessary chef cookbooks to run a basic cakephp application
HTML
119
star
8

Authenticate

Deprecated CakePHP3: plugin with authentication classes for AuthComponent
PHP
81
star
9

app-template

CakePHP2: An empty application template, for use with composer
PHP
71
star
10

crud-json-api

Build advanced JSON API Servers with almost no code.
PHP
55
star
11

crud-view

CakePHP: Automated admin backend based on your CRUD configuration
PHP
49
star
12

travis

Easy travis setup for CakePHP plugins
Shell
39
star
13

vagrant-ansible

a vagrant installation with all the necessary ansible roles to run a basic cakephp application
39
star
14

Authorize

CakePHP2: Authorize classes for AuthComponent
PHP
23
star
15

fixturize

CakePHP: Improve performance of your fixture based tests on MySQL.
PHP
23
star
16

crud-users

CakePHP: Users plugin based on the CRUD action classes
PHP
17
star
17

cakephp-test-utilities

CakePHP: Plugin with support classes for easing unit testing
PHP
12
star
18

crud-demo-app

Demo app for CRUD, CRUD-View and Search
PHP
9
star
19

FriendsOfCake.github.io

FriendsOfCake.com website
JavaScript
4
star
20

process-mq

CakePHP3: A RabbitMQ connection class and worker shell
PHP
3
star
21

process-manager

CakePHP3: A shell task class to help handling external signals gracefully
PHP
1
star
22

blog

The Awesome FriendsOfCake blog
Python
1
star