prodevcon / data-exporter-bundle
Symfony 框架的数据导出库捆绑包
Requires
- symfony/options-resolver: ^4.4
- symfony/property-access: ^4.4
Requires (Dev)
- asimlqt/php-google-spreadsheet-client: ^2.3
- google/apiclient: ^1.1
- phpoffice/phpexcel: 1.8
- symfony/framework-bundle: ^4.4
- twig/twig: ^2.12.1
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: 2024-09-26 07:36:16 UTC
README
注意:API 不稳定,可能随时更改。
Symfony 的数据导出捆绑包。
假设以下表,包含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())
来代替类型名称,结果是一样的。 - 第三个参数是选项。
选项参数允许您进一步配置类型的行性行为。允许的选项在不同字段类型之间有所不同,但有几个例外,如 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 | 字符串 |
BooleanType | 布尔值 |
CallbackType | 回调 |
DateTimeType | 日期时间 |
MapType | 映射 |
NumberType | 数字 |
RawType | 原始数据 |
这里展示了 MapType
的使用
$builder->add('user_type', 'map', ['map' => [
'U' => 'User',
'A' => 'Administrator',
'M' => 'Moderator',
]]);
换句话说,字段类型就像值转换器
MapType
预期一个字符串,这个字符串应该是提供映射的键。在导出过程中,MapType
将列值转换为映射值。同样地,BooleanType
预期一个布尔值,并且根据配置将其转换为 "是/否"、"启用/禁用" 等字符串。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,您需要自己实现迭代器包装类。