friendsofcake / cakephp-csvview
CakePHP 的 CSV 视图类
Requires
- cakephp/cakephp: ^5.0
Requires (Dev)
- cakephp/cakephp-codesniffer: ^5.0
- phpunit/phpunit: ^10.1
README
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 视图选项中指定,与 JsonView 或 XmlView 的工作方式完全相同。
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 输出中包含标题或页脚,可以指定 header 或 footer 视图选项。两者都是完全可选的
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, ]); }
您还可以使用 delimiter、eol、newline、enclosure 和 bom 分别指定分隔符、换行符、换行、转义字符和字节顺序标记(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:\neol:\nbom: falsesetSeparator: 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 选项,就像使用 delimiter、eol 和 enclosure 一样,来设置 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 文件的编码。这可以通过使用 dataEncoding 和 csvEncoding 来完成
默认值如下
dataEncoding:UTF-8csvEncoding:UTF-8
** 只有当这两个变量不同时,您的数据才会转换为另一种编码。
CsvView 默认使用 iconv 扩展来编码数据。您可以通过设置 transcodingExtension 选项来更改用于编码数据的 php 扩展
$this->viewBuilder()->setOption('transcodingExtension', 'mbstring');
目前支持的编码扩展如下
iconvmbstring
设置下载文件的名称
默认情况下,下载的文件将使用生成它的 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());