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
:\n
eol
:\n
bom
: 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-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());