dutchcodingcompany/csv-collection
Easily read CSV files into Laravel Collections. Configure delimiters, enclosures, headers, and encoding, then use familiar Collection methods to filter, map, and transform rows—ideal for imports, reports, and data processing workflows. A simple, fluent API for CSV data in Laravel.
dutchcodingcompany/csv-collection adds a CsvCollection — a Laravel Collection that knows how to read from and write to CSV files, built on top of league/csv. The release of 1.4.0 is a compatibility bump adding Laravel 12 support (no new API surface, no removal of methods), so onboarding is unchanged from 1.3.x.
Install:
composer require dutchcodingcompany/csv-collection
There is no service provider, no config file and no migrations to publish — the class is usable immediately. The first place to look is the package README plus the CsvCollection class itself; almost everything you need is inherited from Illuminate\Support\Collection or is a thin wrapper around League\Csv\Reader / Writer.
The single most common first use case is a controller action that streams a CSV download without loading the whole dataset into memory:
use DutchCodingCompany\CsvCollection\CsvCollection;
public function export()
{
return CsvCollection::fromFile(storage_path('app/export.csv'))
->toCsv()
->download('export.csv');
}
Swap the source for a query and you have the day-to-day workhorse for reporting endpoints.
Export large query results without blowing memory. The package's value is in streaming, so feed it lazily. Use cursor() (or lazy()) rather than get(), map rows to flat arrays, and let the writer flush row by row:
$rows = Order::query()
->where('created_at', '>=', $since)
->cursor()
->map(fn (Order $o) => [$o->id, $o->reference, $o->total, $o->created_at->toIso8601String()]);
return CsvCollection::make($rows)->toCsv()->download('orders.csv');
Read incoming CSVs as a collection. For imports, fromFile() gives you a Collection you can map, filter, chunk and each over using the Collection API you already know. Map header keys to values once (->skip(1) for the header row, or use the reader's header mapping) and then work with associative arrays.
Transform before writing. Because it is a Collection, all normal pipeline methods are available — map, filter, sortBy, values — so formatting/validation logic lives in the same fluent chain that produces the file. This is what makes the package pleasant compared to raw fputcsv loops.
Queued exports. For anything slow, do the work in a job that writes to a disk (->toCsv(storage_path(...)) or a Storage::disk() path) and then notify/download via a signed URL. Keep the streaming response in the HTTP layer only; jobs should write files, not return responses.
Testing. Seed a small fixture CSV in tests/Fixtures and assert on the parsed collection (assertCount, assertSee on the response body, or $response->streamedContent()). The Laravel 12 bump changes nothing about how you write these tests, but if your test matrix uses orchestra/testbench or laravel/framework constraints, widen them to ^12.0 so the new version can be installed.
composer update. Run composer why-not dutchcodingcompany/csv-collection 1.4.0 if the upgrade is blocked — it is usually another package holding back laravel/framework or illuminate/support."dutchcodingcompany/csv-collection": "^1.3 || ^1.4" in your own library, or just ^1.4 in an application. 1.4.0 has no breaking changes, so ^1.3 already resolves to it unless you have a hard constraint.map() ((int), Carbon::parse(), (float) with a locale-aware note). Money columns in particular will silently become floats with float rounding if you are not careful.skip(1) or header-row config; forgetting it is the classic "first data row is missing / first row is garbage" bug. Conversely, when writing, make sure you prepend (or include) the header row once — some APIs write it per chunk if you loop manually."\xEF\xBB\xBF" to the output stream. Also beware of the ; vs , delimiter depending on regional settings — expose a delimiter option if exports are opened in Excel directly.=, +, - or @ can execute as a formula when opened in a spreadsheet. Sanitise user-supplied strings before writing; this is not handled by the package.download() uses a streamed response, which bypasses Content-Length. Middleware that buffers or transforms output (compression, debugbars, some profilers) can break it — test in a production-like config, and remember $response->streamedContent() is how you assert on it in tests.League\Csv reader/writer — the package does not hide it from you, and that is the right escape hatch rather than reimplementing CSV parsing.How can I help you explore Laravel packages today?