Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Csv Laravel Package

portphp/csv

CSV reader and writer for PortPHP import/export workflows. Stream CSV files with configurable delimiters, enclosures, and headers, and plug into PortPHP pipelines for efficient data migration, ETL, and bulk imports in PHP applications.

Deep Wiki
Context7

Getting Started

portphp/csv is a thin adapter that plugs CSV files into Port's reader/writer pipeline. It ships two classes — Port\Csv\CsvReader and Port\Csv\CsvWriter — and nothing else. Everything interesting (mapping, validation, conversion, batching) lives in portphp/port and portphp/steps, so budget your reading time accordingly.

composer require portphp/csv

First place to look: the installed source, since the implementation is only a few hundred lines.

vendor/portphp/csv/src/Port/Csv/CsvReader.php
vendor/portphp/csv/src/Port/Csv/CsvWriter.php

Check the class ... extends line at the top of each file — older tags extend \SplFileObject directly, newer tags may wrap league/csv. That single detail determines several of the gotchas below.

Minimal end-to-end script:

<?php
use Port\Csv\CsvReader;
use Port\Csv\CsvWriter;
use Port\Workflow;

$reader = new CsvReader('orders.csv', ',');   // filename, delimiter, enclosure, escape
$reader->setHeaderRowNumber(0);               // row 0 becomes the array keys

$writer = new CsvWriter('orders-out.csv');
$writer->setDelimiter(',');
$writer->setEnclosure('"');

$workflow = new Workflow($reader);
$workflow->addWriter($writer);
$workflow->process();

That is the canonical first use case: a nightly CLI job that reshapes a supplier/export CSV into a canonical file. Once reading and writing work, drop a MappingStep into the workflow and you have the real 90% case.

Implementation Patterns

Header-driven rows. With setHeaderRowNumber(0), current() returns associative arrays (['sku' => 'A1', 'qty' => '3']) instead of numeric ones. That is what Port's mapping and converter steps expect. Use the second argument to force names when the file has no header or you want to rename on the way in:

$reader->setHeaderRowNumber(2, ['sku', 'qty', 'price']); // skip two junk rows, then assume names

Normalize column order on output. CsvWriter::writeItem() writes fputcsv($stream, $item), i.e. values in the current key order of the array. If a mapping step adds or removes keys, column order drifts between rows. Pin it with a template:

$template = array_fill_keys(['sku', 'qty', 'price'], null);

$workflow->addStep(new CallbackStep(function (array $item) use ($template) {
    return array_merge($template, $item);   // stable order, missing keys become null
}));

Read → transform → write with Port steps.

use Port\Steps\Step\MappingStep;
use Port\Steps\Step\ValueConverterStep;
use Port\Steps\Step\CallbackStep;

$workflow->addStep(new MappingStep([
    'product_code' => 'sku',
    'quantity'     => 'qty',
]));
$workflow->addStep(new ValueConverterStep([
    'qty'   => 'intval',
    'price' => fn ($v) => (float) str_replace(',', '.', $v),
]));
$workflow->addStep(new CallbackStep(function (array $item) {
    if ($item['qty'] <= 0) {
        throw new \Port\Exception\ValidationException(...); // skipped, workflow keeps going
    }
    return $item;
}));

Fan-out to several sinks. addWriter() is variadic in practice — CSV for humans, a DB writer for the real table, both fed from the same pass:

$workflow->addWriter(new CsvWriter('report.csv'));
$workflow->addWriter($doctrineWriter);

Encoding conversion without touching the data. If the source is Latin-1 (very common with exports from Windows tooling), attach a PHP stream filter instead of a converter per field:

$reader->setStreamFilter('convert.iconv.ISO-8859-1/UTF-8');

The writer has the same method for the opposite direction. This is the cheapest fix and keeps the CSV semantics (delimiters, quotes) intact.

Large files. The reader iterates lazily, so a workflow over a 2 GB file is fine provided you don't call steps that buffer. If you must batch (bulk inserts, chunked uploads), buffer inside a callback step and flush on count($buffer) === 500. Don't call count($reader) to size the batch — see gotchas.

Integrating into Laravel. Treat it as a console concern: a queued job or an artisan command that constructs the workflow and calls process(). There is no service provider and nothing to configure. Bind the reader/writer only if you test the pipeline in isolation; a plain new CsvReader(storage_path('app/in.csv')) is usually clearer.

Gotchas and Tips

The writer's default delimiter is ;, the reader's is ,. This asymmetry is the single most common "my output is broken" report. Always call $writer->setDelimiter(',') explicitly.

setHeaderRowNumber() must be called before iteration. It rewinds, reads the header, then advances. Calling it after the workflow has started iterating leaves the pointer in an inconsistent place, and you'll silently reprocess or skip row 0.

Whitespace and BOM end up in your keys. With an \SplFileObject-based reader nothing is trimmed: a UTF-8 BOM turns sku into \xEF\xBB\xBFsku, and "sku " becomes a key with a trailing space. Both break every mapping lookup. Defend with a one-off filter or override the headers:

$reader->setColumnHeaders(array_map('trim', $reader->getColumnHeaders()));

(Strip the BOM with setStreamFilter or a preg_replace on the first header only.)

Duplicate header names silently lose a column. Associative arrays keyed by header mean ["price", "price"] keeps only the second value. Rename duplicates before setHeaderRowNumber() by passing explicit headers.

count() on the reader is O(n) and moves the pointer. CsvReader implements CountableReader; counting walks the file. Never use it for progress bars on big files unless you accept a full extra pass, and always rewind() afterwards.

SplFileObject::SKIP_EMPTY is set on the reader. Blank lines are dropped, but a line consisting of a single empty field can also be treated as empty depending on flags. If empty fields are meaningful in your dataset, verify behavior on a 3-row sample before trusting it.

Type juggling happens in fputcsv, not in your step. null writes as an empty string, false as "", true as 1, floats with the current locale's decimal separator unless you cast to string first. Money and booleans are the usual casualties — format them in a converter step.

No header row is written for you. CsvWriter writes exactly the arrays it is given. Emit the header yourself as the first item (a callback step, or $writer->writeItem($headers) inside a small subclass that overrides prepare()), or your file will be headerless.

Excel compatibility. Excel prefers \r\n and a UTF-8 BOM. The writer opens the file with 'w' in its constructor and closes it in finish(), so subclass and write "\xEF\xBB\xBF" in prepare() if you need BOM. Line endings are not exposed — that's an extension point, not a config option.

The writer truncates the target file on construction. There is no append mode. For incremental runs, write to a per-run temp file and concatenate, or subclass and open with 'a'.

Escape character. If your tag delegates to league/csv, the escape argument defaults to \ and has been deprecated in recent league/csv releases because it deviates from RFC 4180. Pass an empty string / '' if you see doubled quotes or mangled backslashes in fields. If your tag extends \SplFileObject, control it via the fourth constructor argument.

Extending. The cleanest extension points are subclassing CsvWriter (override writeItem, prepare, finish — all protected) and implementing Port\Writer for a decorator that adds headers or filters rows. Avoid subclassing CsvReader if the installed version extends \SplFileObject; the iterator internals are fragile, and a CallbackStep upstream is almost always the better place to put the logic.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource