vitorccs/laravel-csv

PHP Laravel 包,以内存优化方式创建 CSV 文件

v2.0.0 2024-07-19 22:45 UTC

This package is not auto-updated.

Last update: 2024-09-24 18:20:47 UTC


README

PHP Laravel 包,以内存优化方式导出和导入 CSV 文件。

描述

PHP 数组Laravel 集合Laravel 查询 导出 CSV 文件,并可选择提示用户下载文件、将其存储在 Laravel 磁盘或作为 Laravel Job 在后台创建文件。

Laravel 磁盘本地文件字符串资源 导入 CSV 文件,并可选择检索完整内容或小部分内容。

该项目通过使用 PHP 流 优化内存使用,将内容放置在临时文件中(而不是 PHP 线程内存),并逐行读取/写入内容。

注意:此项目受到 https://github.com/maatwebsite/Laravel-Excel 的启发,这是一个伟大的项目,可以处理许多格式(Excel、PDF、OpenOffice 和 CSV)。但由于它使用 PhpSpreadsheet,因此不适合处理大型 CSV 文件(数千条记录),导致 PHP 内存耗尽。

从 v1.0 升级到 v2.0

版本 2.0 添加了导入功能,因此唯一需要执行的操作是更改导入命名空间。

// v1.0 (old)
use Vitorccs\LaravelCsv\Concerns\Exportable;
use Vitorccs\LaravelCsv\Concerns\FromArray;
use Vitorccs\LaravelCsv\Concerns\FromCollection;
use Vitorccs\LaravelCsv\Concerns\FromQuery;
// v2.0 (new)
use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromArray;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromCollection;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromQuery;

要求

  • PHP >= 8.0
  • Laravel >= 6.x

安装

步骤 1) 添加 composer 依赖项

composer require vitorccs/laravel-csv

步骤 2) 发布配置文件

php artisan vendor:publish --provider="Vitorccs\LaravelCsv\ServiceProviders\CsvServiceProvider" --tag=config

步骤 3) 根据项目偏好编辑本地 config\csv.php 文件

如何导出

步骤 1) 创建一个如下的导出类文件

注意:您可以实现 FromArrayFromCollectionFromQuery

namespace App\Exports;

use App\User;
use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromQuery;

class UsersExport implements FromQuery
{
    use Exportable;
    
    public function query()
    {
        return User::query()
            ->where('created_at', '>=', '2024-01-01 00:00:00');
    }
}

现在可以通过一行代码生成文件

# prompt the client browser to download the file 
return (new UsersExport)->download('users.csv');

如果要将文件存储在磁盘上

# will save the file in 's3' disk
return (new UsersExport)->store('users.csv', 's3');

您也可以获取内容流以更好地控制输出

# will get the content in a stream (content placed in a temporary file)
return (new UsersExport)->stream();

对于大型文件,您可能希望将其作为 Laravel Job 在后台生成

use App\Jobs\NotifyCsvCreated;

# generate a {uuid-v4}.csv filename
$filename = CsvHelper::filename();

# will create a job to create and store the file in disk
# and afterwards notify the user
(new BillsExport())
    ->queue($filename, 's3')
    ->allOnQueue('default')
    ->chain([
        // You must create the Laravel Job below
        new NotifyCsvCreated($filename)
    ]);

导出 - 数据源

注意:只有 FromQuery 可以从配置文件中的 chunk_size 参数按块大小分割结果。

Laravel Eloquent 查询构建器

namespace App\Exports;

use App\User;
use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromQuery;

class MyQueryExport implements FromQuery
{
    use Exportable;
    
    public function query()
    {
        return User::query();
    }
}

Laravel 数据库查询构建器

namespace App\Exports;

use App\User;
use Illuminate\Support\Facades\DB;
use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromQuery;

class MyQueryExport implements FromQuery
{
    use Exportable;
    
    public function query()
    {
        return DB::table('users');
    }
}

Laravel 集合

namespace App\Exports;

use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromCollection;

class MyCollectionExport implements FromCollection
{
    use Exportable;
    
    public function collection()
    {
        return collect([
            ['a1', 'b1', 'c1'],
            ['a2', 'b2', 'c2'],
            ['a3', 'b3', 'c3']
        ]);
    }
}

Laravel 懒集合

namespace App\Exports;

use App\User;
use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromCollection;

class MyQueryExport implements FromCollection
{
    use Exportable;
    
    public function collection()
    {
        return User::cursor();
    }
}

PHP 数组

namespace App\Exports;

use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromArray;

class MyArrayExport implements FromArray
{
    use Exportable;
    
    public function array(): array
    {
        return [
            ['a1', 'b1', 'c1'],
            ['a2', 'b2', 'c2'],
            ['a3', 'b3', 'c3']
        ];
    }
}

如何导入

步骤 1) 创建一个如下的导入类文件

注意:您可以实现 FromDiskFromFileFromResourceFromContents

namespace App\Exports;

use Vitorccs\LaravelCsv\Concerns\Importables\Importable;
use Vitorccs\LaravelCsv\Concerns\Importables\FromDisk;

class UsersImport implements FromDisk
{
    use Importable;
    
    public function disk(): ?string 
    {
        return 's3'; 
    }

    public function filename(): string
    {
        return 'users.csv';
    }
}

现在可以通过一行代码检索内容

# get the records in array format
return (new UsersImport)->getArray();
# in case the result is too large, you may receive small chunk of results
# at a time in your callback function, preventing memory exhaustion.
(new UsersImport)->chunkArray(function(array $rows, int $index) {
    // do something with the rows
    echo "Chunk $index has the following records:";
    print_r($rows);
});

导入 - 数据源

字符串

namespace App\Imports;

use Vitorccs\LaravelCsv\Concerns\Importables\Importable;
use Vitorccs\LaravelCsv\Concerns\Importables\FromContents;

class MyContents implements FromContents
{
    use Importable;
    
    public function contents(): string
    {
        return "A1,B1,C1\nA2,B2,C2\n,A3,B3,C3";
    }
}

本地文件

namespace App\Imports;

use Vitorccs\LaravelCsv\Concerns\Importables\Importable;
use Vitorccs\LaravelCsv\Concerns\Importables\FromFile;

class MyFileImport implements FromFile
{
    use Importable;
    
    public function filename(): string;
    {
        return storage_path() . '/users.csv';
    }
}

资源

namespace App\Imports;

use Vitorccs\LaravelCsv\Concerns\Importables\Importable;
use Vitorccs\LaravelCsv\Concerns\Importables\FromResource;

class MyResourceImport implements FromResource
{
    use Importable;
    
    public function resource()
    {
        $contents = "A1,B1,C1\nA2,B2,C2\n,A3,B3,C3";
        $resource = fopen('php://memory', 'w+');
        
        fputs($resource, $contents);
        
        return $resource;
    }
}

Laravel 磁盘

namespace App\Exports;

use Vitorccs\LaravelCsv\Concerns\Importables\Importable;
use Vitorccs\LaravelCsv\Concerns\Importables\FromDisk;

class UsersImport implements FromDisk
{
    use Importable;
    
    public function disk(): ?string 
    {
        return 'local'; 
    }

    public function filename(): string
    {
        return 'my_imports/users.csv';
    }
}

实现

以下实现适用于导出和导入模式。

标题

实现 WithHeadings 为 CSV 文件设置标题。

use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromArray;
use Vitorccs\LaravelCsv\Concerns\WithHeadings;

class UsersExport implements FromArray, WithHeadings
{
    use Exportable;
    
    public function headings(): array
    {
        return ['ID', 'Name', 'Email'];
    }
}

映射行

实现 WithMapping 如果您需要设置每列的值或应用一些自定义格式化。

use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromArray;
use Vitorccs\LaravelCsv\Concerns\WithMapping;

class UsersExport implements FromArray, WithMapping
{
    use Exportable;
    
    public function map($user): array
    {
        return [
            $user->id,
            $user->name,
            $user->email ?: 'N/A'
        ];
    }
}

格式化列

实现 WithColumnFormatting 格式化日期和数字字段。

在导出模式下,日期必须是 Carbon 或 Datetime 对象,数字必须是任何类型的数字数据(数字字符串、整数或浮点数)。

在导入模式下,字符串内容必须与设置的格式匹配(例如:日期为 yyyy-mm-dd)。

格式化首选项在配置文件 csv.php 中设置。

use Carbon\Carbon;
use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromArray;
use Vitorccs\LaravelCsv\Concerns\WithColumnFormatting;
use Vitorccs\LaravelCsv\Enum\CellFormat;

class UsersExport implements FromArray, WithColumnFormatting
{
    use Exportable;
    
    public function array(): array
    {
        return [
            [ Carbon::now(), Carbon::now(), 2.50, 1.00 ],
            [ new DateTime(), new DateTime(), 3, 2.00 ]
        ];
    }
    
    public function columnFormats(): array
    {
        return [
            'A' => CellFormat::DATE,
            'B' => CellFormat::DATETIME,
            'C' => CellFormat::DECIMAL,
            'D' => CellFormat::INTEGER,
        ];
    }
}

限制结果

如果需要限制导出/导入的结果数量,请实现以下方法。

use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromQuery;

class UsersExport implements FromQuery
{
    use Exportable;
    
    public function limit(): ?int
    {
        return 5000;
    }
}

许可证

MIT许可证下发布。