prodevcon/data-exporter-bundle

Symfony 框架的数据导出库捆绑包

安装: 9

依赖项: 0

建议者: 0

安全: 0

类型:symfony-bundle

v0.2.0 2021-01-19 15:58 UTC

This package is not auto-updated.

Last update: 2024-09-26 07:36:16 UTC


README

注意:API 不稳定,可能随时更改。

Symfony 的数据导出捆绑包。

假设以下表,包含2个用户资料对象

idfirstNamelastNameage
1FooChan20
2BarWong17

在代码中数据结构看起来像这样

$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()) 来代替类型名称,结果是一样的。
  • 第三个参数是选项。

选项参数允许您进一步配置类型的行性行为。允许的选项在不同字段类型之间有所不同,但有几个例外,如 labelproperty_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,您需要自己实现迭代器包装类。