PHP Laravel package to export and import CSV files in a memory-optimized way.
Export CSV files from PHP arrays, Laravel Collections or Laravel Queries and choose to prompt the user to download the file, store it in a Laravel disk or create the file in background as a Laravel Job.
Import CSV files from Laravel Disks, local files, strings or resources and choose to retrieve the full content or in small chunks.
The memory usage is optimized in this project by using PHP streams, which places the content in a temporary file (rather than PHP thread memory) and reads/writes content one line at a time.
NOTE: This project was inspired on https://github.com/maatwebsite/Laravel-Excel which is a great project and can handle many formats (Excel, PDF, OpenOffice and CSV). But since it uses PhpSpreadsheet, it is not optimized for handling large CSV files (thousands of records) causing the PHP memory exhaustion.
Version 2.0 adds the importing feature so the only required action is to change the importing namespace:
// 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
Step 1) Add composer dependency
composer require vitorccs/laravel-csv
Step 2) Publish the config file
php artisan vendor:publish --provider="Vitorccs\LaravelCsv\ServiceProviders\CsvServiceProvider" --tag=config
Step 3) Edit your local config\csv.php
file per your project preferences
Step 1) Create an Export class file as shown below
Note: you may implement FromArray, FromCollection or FromQuery
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');
}
}
Step 2) The file can now be generated by using a single line:
# prompt the client browser to download the file
return (new UsersExport)->download('users.csv');
In case you want the file to be stored in the disk:
# will save the file in 's3' disk
return (new UsersExport)->store('users.csv', 's3');
You may also get the content as stream for better control over the output:
# will get the content in a stream (content placed in a temporary file)
return (new UsersExport)->stream();
For larger files, you may want to generate the file in background as a 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)
]);
Note: Only FromQuery
can chunk results per chunk_size
parameter from config file.
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();
}
}
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');
}
}
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']
]);
}
}
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();
}
}
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']
];
}
}
Step 1) Create an Import class file as shown below
Note: you may implement FromDisk, FromFile, FromResource or FromContents
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';
}
}
Step 2) The content can now be retrieved by using a single line:
# 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;
}
}
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';
}
}
The implementations below work with both Export and Import mode.
Implement WithHeadings
for setting a heading to the CSV file.
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'];
}
}
Implement WithMapping
if you either need to set the value of each column or apply some custom formatting.
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'
];
}
}
Implement WithColumnFormatting
to format date and numeric fields.
In export mode, the Date must be either a Carbon or a Datetime object, and the number must be any kind of numeric data (numeric string, integer or float).
In import mode, the string content must match with the formatting set (e.g: yyyy-mm-dd for dates).
The formatting preferences are set in the config file 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,
];
}
}
Implement the method below if you need to limit the quantity of results to be exported/imported.
use Vitorccs\LaravelCsv\Concerns\Exportables\Exportable;
use Vitorccs\LaravelCsv\Concerns\Exportables\FromQuery;
class UsersExport implements FromQuery
{
use Exportable;
public function limit(): ?int
{
return 5000;
}
}
Released under the MIT License.