sparkson / data-exporter-bundle
Symfony2 框架的数据导出库捆绑包
Requires
- symfony/options-resolver: ~2.7|~3.0
- symfony/property-access: ~2.7|~3.0
Requires (Dev)
- asimlqt/php-google-spreadsheet-client: ^2.3
- google/apiclient: ^1.1
- phpoffice/phpexcel: 1.8
- symfony/framework-bundle: ~2.7|~3.0
- twig/twig: ^1.18
Suggests
- asimlqt/php-google-spreadsheet-client: To use the Google Spreadsheet output adapter
- phpoffice/phpexcel: To use the PHPExcel output adapter
This package is not auto-updated.
Last update: 2022-10-05 00:00:56 UTC
README
注意:API 不稳定,可能会更改。
Symfony2 的数据导出捆绑包。
假设以下表,其中包含 2 个用户配置文件对象
id | firstName | lastName | age |
---|---|---|---|
1 | Foo | Chan | 20 |
2 | Bar | Wong | 17 |
代码中的数据结构看起来是这样的
$items = [ ['id' => 1, 'firstName' => 'Foo', 'lastName' => 'Chan', 'age' => 20], ['id' => 2, 'firstName' => 'Bar', 'lastName' => 'Wong', 'age' => 17], ];
使用此捆绑包,您可以将数据集($items
)转换为其他格式,例如 CSV
Id,FirstName,LastName,Age
1,Foo,Chan,20
2,Bar,Wong,17
基本用法
首先需要在 AppKernel.php
中启用捆绑包
$bundles = array( // .... new Sparkson\DataExporterBundle\SparksonDataExporterBundle(), );
现在假设您在控制器操作中。以下是使用导出器的方法
public function exportAction(Request $request) { // First, retrieve the ExporterFactory service. $exporterFactory = $this->get('sparkson.data_exporter.factory'); // Then create a new exporter builder instance. $builder = $exporterFactory->createBuilder(); // We'll use the builder to build an exporter. // Let's define the column structure. $builder ->add('id', 'string') ->add('firstName', 'string') ->add('lastName', 'string') ->add('age', 'string'); // Build the exporter. $exporter = $builder->getExporter(); // Assigns an output adapter. Here we use the CSV adapter that utilizes PHP's fputcsv() function. $exporter->setOutputAdapter(new \Sparkson\DataExporterBundle\Exporter\OutputAdapter\CSVAdapter()); // Finally, sets the variable containing the data set to export ($items is assumed here). $exporter->setDataSet($items); // Run! $exporter->execute(); // By default, the output adapter will write the result to a variable which can be retrieved via getResult(). $result = $exporter->getResult(); // Feed the result to the response object return new Response($result); }
$result
变量将如下所示
Id,FirstName,LastName,Age
1,Foo,Chan,20
2,Bar,Wong,17
这说明了导出器的基本用法。请注意,方法可以按以下方式链接
$result = $builder ->add('id', 'string') ->add('firstName', 'string') ->add('lastName', 'string') ->add('age', 'string') ->getExporter() // this returns the Exporter instance ->setOutputAdapter(new \Sparkson\DataExporterBundle\Exporter\OutputAdapter\CSVAdapter()); ->setDataSet($items) ->execute() ->getResult();
定义列结构
导出器构建器允许您使用 $builder->add()
定义要导出的列集。例如
$builder->add('id', 'string', []);
使用伟大的 Symfony Form 组件的用户会发现这种语法很熟悉。这是故意的。
- 第一个参数是字段名称,它必须在相同的列集中是唯一的。
- 第二个参数是字段类型。它可以是一个在类型注册表中注册的类型名称(通常通过服务标记),或者是一个实现
Sparkson\DataExporterBundle\Exporter\Type\ExporterTypeInterface
的对象。这意味着,除了传递类型名称之外,您也可以编写$builder->add('id', new \Sparkson\DataExporterBundle\Exporter\Core\Type\StringType())
,结果将是相同的。 - 第三个参数是选项。
options
参数允许您进一步配置类型的操作。允许的选项在字段类型中不同,但有少数例外,如 label
和 property_path
。
label
每个列都有一个标签,它用于标题行中的标题/标题。如果未指定,则标签将生成自列名。
property_path
property_path
属性覆盖了默认行为,即使用字段名作为属性路径来检索列值。例如,当记录是数组时,您可能希望这样做
$builder->add('id', 'string', ['property_path' => '[id]'])
字段类型本身不会检索列值。相反,它将 property_path
值传递给值解析组件,而不关心值是如何检索的。默认情况下,使用 \Sparkson\DataExporterBundle\Exporter\ValueResolver\DefaultValueResolver
,该解析器通过 Symfony 的 PropertyAccess 组件获取记录值。这意味着您可以编写以下内容
$builder->add('author_name', 'string', ['property_path' => 'author.name'])
尽管看起来您可能不想自己编写值解析器,但可以通过调用 Exporter::setValueResolver()
来告诉导出器使用您自己的值解析器。
resolver_options
可以通过 resolver_options
键将附加选项传递给每个列的活动值解析器。
DefaultValueResolver
支持一个 filters
选项,该选项接受一个数组,可以是以下类型:
- 一个字符串,它是一个简单的 PHP 函数,接受导出值作为第一个参数;或者
- 一个实现了
Sparkson\DataExporterBundle\Exporter\ValueResolver\Filter\FilterInterface
的实例。本库中的一个例子是CustomFilter
类。
过滤器类似于值预处理程序,主要用于在值检索后和传递到字段类型进行最终输出之前,使用如 trim()
、ltrim()
等函数对值进行清理。
$builder->add('description', 'string', ['resolver_options' => ['filters' => ['trim']]]); // is equivalent to $builder->add('description', 'string', ['resolver_options' => ['filters' => [ new CustomFilter(function($value) { return trim($value); })]]]);
字段类型
以下是本扩展包中提供的字段类型。类定义在 Sparkson\DataExporterBundle\Exporter\Core\Type
命名空间下。
类名 | 类型 |
---|---|
StringType | string |
BooleanType | boolean |
CallbackType | callback |
DateTimeType | datetime |
MapType | map |
NumberType | number |
RawType | raw |
以下展示了 MapType
的用法
$builder->add('user_type', 'map', ['map' => [ 'U' => 'User', 'A' => 'Administrator', 'M' => 'Moderator', ]]);
换句话说,字段类型类似于值转换器
MapType
期望一个字符串,它应该是提供映射的键。在导出过程中,MapType
将列值转换为映射值。类似地,BooleanType
期望一个布尔值,并将其转换为如 "Yes/No"、"Enabled/Disabled" 等字符串,具体取决于配置。DateTimeType
将原始字段值转换为格式化的日期时间。StringType
将值转换为字符串。此外,它还提供了一个可选的format
配置,当设置时,将传递给 PHP 的sprintf()
函数。
请检查源文件以获取有关特定类型的工作方式及其可用选项的详细信息。
最后,您可以将自己的字段类型添加到类型注册表中。只需参考现有类型的源代码以了解如何实现一个,并参考 services.yml
了解如何将类型注册到类型注册表中,以便您可以使用其名称来使用它。
在单独的类中定义导出字段
就像 Symfony 的表单组件一样,您也可以在单独的类中定义导出字段
<?php namespace AppBundle\Exporter\Type; use Sparkson\DataExporterBundle\Exporter\ExporterBuilder; use Sparkson\DataExporterBundle\Exporter\Type\AbstractType; class ProfileType extends AbstractType { public function buildExporter(ExporterBuilder $builder) { $builder ->add('firstName', 'string') ->add('lastName', 'string') ->add('age', 'string'); } public function getName() { return 'user_profile'; } }
然后可以通过将类实例传递给工厂来创建导出器
$profileExporter = $factory->createExporter(new \AppBundle\Exporter\Type\ProfileType()); // OR, when the class is registered in the type registry: $profileExporter = $factory->createExporter('user_profile');
输出适配器
当调用 execute()
时,导出器实例逐个迭代数据集,每个都有定义的列集。但导出器实例本身不处理提取值的写入。这项工作委托给一个 输出适配器。
您可以在本库中找到以下输出适配器
CSVAdapter
,它使用 PHP 的内置fputcsv()
函数来写入数据。GoogleSpreadsheetAdapter
,它使用 asimlqt/php-google-spreadsheet-client 将数据写入 Google 表格。PHPExcelAdapter
,它利用 PHPExcel 库来写入数据。TwigAdapter
,它使用可定制的 twig 模板来渲染结果。
与字段类型不同,输出适配器不是服务,因此您需要手动创建它们。请参考它们的源代码以获取有关构造函数参数和可用选项的详细信息。以下是一些简要的使用示例
默认情况下,CSVAdapter
在$exporter->execute()
期间将数据写入内存,可以通过getResult()
方法检索。在下面的示例中,CSVAdapter
被配置为将导出结果写入文件,并且不会保留结果(即不会读取数据以供进一步通过getResult()
检索)。
$exporter->setOutputAdapter(new CSVAdapter([ 'filename' => __DIR__.'/output.csv', // sets output filename 'keep_result' => false, // do not keep result for getResult() ]));
使用TwigAdapter
,您可以传递导出数据到Twig模板以进行进一步处理。此类需要将Twig_Environment
实例作为第一个构造函数参数。
$twig = $this->get('twig'); // retrieve the Twig_Environment instance from the service container $exporter->setOutputAdapter(new TwigAdapter($twig, [ 'template' => '@AppBundle/exporter/my_exporter_template.html.twig', ]));
注意,如果未提供模板,将使用位于Resources/view/exporter/template.html.twig
的默认模板。您可以使用此文件来学习编写自己的模板。
提示:您可以定义自己的输出适配器服务。例如,您可以定义一个用于TwigAdapter
的服务,该服务使用您自己的模板。之后,上面的代码示例可以简化如下
$exporter->setOutputAdapter($this->get('app.exporter.output_adapter.twig'));
以下是在services.yml
中定义服务的方式
app.exporter.output_adapter.twig: class: Sparkson\DataExporterBundle\Exporter\OutputAdapter\TwigAdapter arguments: - @twig - { template: "@AppBundle/exporter/my_exporter_template.html.twig" }
杂项说明
修改列属性
默认情况下,列以它们被添加的顺序导出。但是,您可能希望在构建导出器之后修改列顺序。以下是方法
$columnSet = $exporter->getColumns(); $columnSet->getChild('lastName')->setPosition(2);
注意,position
仅是排序提示。库不会修改其他列的位置以确保唯一性。
或者,您可以通过setColumnOrders()
重新排序列
$columnSet->setColumnOrders(['age', 'lastName', 'firstName', 'id']); // Columns re-arranged in the following order: "age", "last name", "first name", "id"
您还可以从导出器中禁用列
$columnSet->getChild('id')->setEnabled(false);
您甚至可以通过将true
作为第二个参数传递来禁用setColumnOrders
中未指定的列
// Fields in the column set: id, age, firstName, lastName $columnSet->setColumnOrders(['lastName', 'firstName'], true); // Columns re-arranged in the following order: "last name", "first name". // "id" and "age" are disabled.
这在您为用户提供选择要导出哪些列的用户界面时特别有用。不需要在构建器的添加字段语句周围添加if语句,只需先添加所有字段,然后在构建器构建后禁用不需要的字段即可。
重要:一旦调用$exporter->execute()
,就无法更改列属性。
在Doctrine中处理大数据集
要使用Doctrine导出数据库记录,通常编写如下
// $em is the Entity Manager $items = $em->getRepository('AppBundle:EntityToExport')->findAll(); $exporter->setDataSet($items);
但是,这对于大数据集来说效果不好,因为内存消耗可能非常高。幸运的是,Doctrine提供了一种迭代大型结果集的方法。以下是略微修改自Doctrine文档的代码
$q = $em->createQuery('SELECT e FROM AppBundle:EntityToExport e'); $iterableResult = $q->iterate(); foreach ($iterableResult as $row) { // do stuff with the data in the row, $row[0] is always the object // detach from Doctrine, so that it can be Garbage-Collected immediately $em->detach($row[0]); }
我们可以使用PHP的生成器采用此技术,如下所示
$getDataSetIterator = function() { $q = $em->createQuery('SELECT e FROM AppBundle:EntityToExport e'); $iterableResult = $q->iterate(); foreach ($iterableResult as $row) { yield $row[0]; // detach from Doctrine, so that it can be Garbage-Collected immediately $em->detach($row[0]); } }; $exporter->setDataSet($getDataSetIterator());
就是这样。对于PHP < 5.5,您需要自己创建迭代包装类。