josegonzalez / cakephp-csvview
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) 字节序列。有关更多信息,请参阅 Wikipedia
关于字节顺序标记的文章。
可以使用 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());