friendsofcake/cakephp-csvview

CakePHP 的 CSV 视图类

安装量: 1,928,440

依赖项: 3

建议者: 0

安全: 0

星星: 175

观察者: 14

分支: 64

类型:cakephp-plugin

5.0.0 2023-10-13 18:16 UTC

README

CI Coverage Status Total Downloads Latest Stable Version Software License

CsvView 插件

快速启用模型数据的 CSV 输出。

此分支适用于 CakePHP 5.x。有关详细信息,请参阅 版本映射

背景

我需要快速导出数据库中东西的 CSV 文件。使用视图类手动迭代将是复制每个导出方法的繁琐工作,所以我想用自定义视图类,比如 JsonView 或 XmlView,来做这件事会更容易。

安装

composer require friendsofcake/cakephp-csvview

启用插件

运行以下命令加载插件

bin/cake plugin load CsvView

用法

要将扁平数组导出为 CSV,可以编写以下代码

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

要包含在 CSV 中的所有变量都必须在 serialize 视图选项中指定,与 JsonViewXmlView 的工作方式完全相同。

CSV 输出中可以有多变量

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

如果要在 CSV 输出中包含标题或页脚,可以指定 headerfooter 视图选项。两者都是完全可选的

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,
        ]);
}

您还可以使用 delimitereolnewlineenclosurebom 分别指定分隔符、换行符、换行、转义字符和字节顺序标记(BOM)序列

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,
        ]);
}

这些选项的默认值如下

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

eol 选项用于生成输出中的换行符。然而,newline 是应该替换实际数据中换行符的字符。建议使用换行符的字符串表示,以避免生成无效的输出。

一些阅读软件错误地渲染不包含字节顺序标记(BOM)序列的 UTF-8 编码文件。bom 选项用于在生成的 CSV 输出流的开始添加字节顺序标记(BOM)序列。有关更多信息,请参阅 维基百科关于字节顺序标记的文章

可以使用 setSeparator 选项在 CSV 的第一行中显式设置分隔符。某些阅读器需要这样做才能正确显示 CSV。

如果您的模型数据复杂,可以使用 extract 视图选项指定每个记录的单独 Hash::extract()-兼容 路径或可调用对象

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,
        ]);
}

如果您的模型数据中包含一些空值或缺失键,可以使用 null 选项,就像使用 delimitereolenclosure 一样,来设置 CSV 中空值应如何显示

null 默认为 ''

自动视图类切换

您可以使用控制器的内容协商功能自动切换 CsvView 类,如下所示。

在您的应用的 routes.php 中启用 csv 扩展解析,使用 $routes->addExtensions(['csv']) 在所需的范围内。

// PostsController.php

// Add the CsvView class for content type negotiation
public function initialize(): void
{
    parent::initialize();

    $this->addViewClasses(['csv' => 'CsvView.Csv']);
}

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

使用上述控制器,您现在可以访问 /posts.csv 或使用 Accept 报头 text/csv 来获取 CSV 数据,并使用 /posts 来获取正常的 HTML 页面。

对于非常复杂的 CSV,您还可以使用自己的视图文件。为此,您可以选择不指定 serialize 或将其设置为 null。视图文件将位于您当前控制器的 csv 子目录中

// 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);
}

设置文件的编码

如果您需要 CSV 文件有不同的编码,您必须设置传递给视图的数据的编码,以及您想要的 CSV 文件的编码。这可以通过使用 dataEncodingcsvEncoding 来完成

默认值如下

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

** 只有当这两个变量不同时,您的数据才会转换为另一种编码。

CsvView 默认使用 iconv 扩展来编码数据。您可以通过设置 transcodingExtension 选项来更改用于编码数据的 php 扩展

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

目前支持的编码扩展如下

  • iconv
  • mbstring

设置下载文件的名称

默认情况下,下载的文件将使用生成它的 URL 的最后一个段来命名。例如:使用 example.com/my-controller/my-action 会下载 my-action.csv,而使用 example.com/my-controller/my-action/first-param 会下载 first-param.csv

在 IE 中,您需要设置文件名,否则它将作为文本文件下载。

要设置自定义文件名,请使用 Response::withDownload() 方法。以下片段可以将下载的文件从 export.csv 更改为 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');
}

使用特定的视图构建器

在某些情况下,最好不使用当前控制器的视图构建器 $this->viewBuilder(),因为任何对 $this->render() 的调用都将损害后续的渲染。

例如,在您当前控制器的动作过程中,如果您需要将某些数据作为 CSV 渲染出来,以便简单地将其保存到服务器上的文件中。

不要忘记将以下内容添加到您的控制器中

use Cake\View\ViewBuilder;

这样您就可以创建一个特定的视图构建器

// 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());