kuborgh/csv-bundle
Kuborgh CSV Bundle integrates CSV handling into Symfony applications, providing configurable services for reading, writing, and processing CSV files. It simplifies import/export tasks and keeps CSV logic reusable across your project.
Install and wire up (slightly less friction than before).
composer require kuborgh/csv-bundle:^1.0 — the v1.0.1 tag gives you a clean semver constraint, so you should no longer need "minimum-stability" overrides or pin a dev-master reference. If Composer still complains, check the release's composer adaptation for the PHP constraint it declares; fall back to pinning the exact tag.new Kuborgh\CsvBundle\CsvBundle() to AppKernel::registerBundles() (Symfony 2/3) or to config/bundles.php (Symfony 4+, if you're lucky enough that it loads — see Gotchas).bin/console debug:container | grep -i csv) and confirm the service ID. The bundle ships a DI extension that registers a converter service; treat the exact ID as something to discover, not assume, since there's no maintained docs.Where to look first. In priority order:
Resources/config/services.yml — tells you the real service ID and constructor args (delimiter, enclosure, encoding are usually injected here).CsvConverter / Csv) — that's your real API surface. Read the public methods before writing any integration code; the readme (if present) is often stale relative to the code.DependencyInjection/CsvExtension.php — confirms what's configurable via config.yml.First use case — a quick smoke test in a controller:
$converter = $this->get('kuborgh_csv.converter'); // verify ID first
// array -> CSV string
$csv = $converter->arrayToCsv([
['id' => 1, 'name' => 'Alice'],
['id' => 2, 'name' => 'Bob'],
]);
// CSV string -> array
$rows = $converter->csvToArray($csv);
Run that before anything else. If both directions round-trip a couple of rows, the package is doing its one job and you can build on it.
Export endpoints (array → CSV). The natural fit is an admin "download report" action. Build your rows as associative arrays, let the converter emit the CSV string, then return a Response with Content-Type: text/csv and a Content-Disposition: attachment header:
public function exportAction()
{
$rows = $this->getDoctrine()->getRepository(User::class)->findAllAsArray();
$csv = $this->get('kuborgh_csv.converter')->arrayToCsv($rows);
$response = new Response($csv);
$response->headers->set('Content-Type', 'text/csv; charset=UTF-8');
$response->headers->set('Content-Disposition', 'attachment; filename="users.csv"');
return $response;
}
Keep the mapping (entity → plain array) in a separate service so the CSV library stays an implementation detail you can swap out later.
Import flows (CSV → array). Handle the uploaded file with Symfony's UploadedFile, read the contents, hand the string to csvToArray, then validate/normalise rows before persisting. Because the package just converts, all header mapping, type casting, and deduplication is on you:
$contents = file_get_contents($file->getPathname());
$rows = $converter->csvToArray($contents);
// First row is usually headers — decide explicitly whether to strip it
$headers = array_shift($rows);
$mapped = array_map(fn($r) => array_combine($headers, $r), $rows);
Wire it as a service, not a global grab. Inject the converter into your own import/export services via constructor argument (service ID from debug:container). This keeps controllers thin and makes the eventual migration to league/csv a one-line constructor change.
Console commands. Both directions work well inside a Command: read a file, csvToArray, process, arrayToCsv, write out. Because the converter is stateless, you can loop over many files in one command invocation without re-instantiating.
Config-driven formatting. If the bundle's extension exposes delimiter/enclosure/encoding, set them once in config.yml rather than hardcoding — especially encoding, since Excel users will otherwise complain about mojibake on accented characters.
Test harness. Since there are no dependents and no active maintenance, write a small integration test that round-trips your real data shapes (nested values, missing keys, commas-in-quotes, newlines-in-quotes). That test becomes your regression suite if you ever replace the package.
^1.0 constraint is nicer than a commit hash, but the maintenance posture hasn't changed. Note a fork-or-replace plan in your README; league/csv remains the safer default for anything long-lived.AppKernel, Resources/config/services.yml, possibly services.yml without autowiring). On Symfony 4+ this often still loads but won't autowire — you may need to declare services manually or use a compiler pass. Budget time for this before promising a feature.array() syntax in places, possibly deprecated calls). On PHP 8+ this can throw deprecations or fatals; run php -l against the vendor files and check your error log after first use. The Composer adaptation may have relaxed the PHP requirement constraint in composer.json, which means Composer will install it on newer runtimes — that's permission, not evidence it runs cleanly."\xEF\xBB\xBF" yourself on export, and strip it on import before parsing, or your first header will be \xEF\xBB\xBFid."Smith, John" and multi-line addresses. If the converter doesn't handle them, sanitise or reject those inputs explicitly.php://output, or switch to a streaming writer.var_dump the raw array before conversion and the string after — the bug is almost always in the array shape (nested arrays, missing keys) rather than the converter. Enable debug:container output to confirm you're actually hitting the bundle's service and not a similarly-named one.vendor under your namespace and treat it as your own code — with 0 dependents, nobody else will maintain it for you.How can I help you explore Laravel packages today?