misatotremor/csv-bundle
misatotremor/csv-bundle provides straightforward CSV handling for PHP and Symfony-style applications, helping developers read, write, import, export, and manipulate comma-separated data through reusable bundle components and simple configuration.
Reality check first. Despite the composer name misatotremor/csv-bundle, the upstream repository is MisatoTremor/AvroCsvBundle — it is a Symfony bundle that handles Avro as well as CSV, and it is not a Laravel package. There is no service provider, no facade, no config/csv-bundle.php. It is built around Symfony's DI container and, at least for Avro, a schema/record model. That shapes everything below: you can use it in Laravel, but only as a standalone library you wire up yourself, or by borrowing its pattern rather than its code.
Minimal path if you still want to try it:
composer require misatotremor/csv-bundle — check the resolved version and that it doesn't drag in a full Symfony framework you don't want. v1.2.2 is the current tag.src/ before the README. With a single star and zero dependents, the source is the documentation. Look for the importer/exporter service classes, the attribute (or annotation) used to mark fields as CSV columns / Avro fields, and any DependencyInjection/Configuration.php for the config tree.config/bundles.php, then look at config/packages/ for the expected config key.First use case: export a handful of models to CSV for a client download, or — if the Avro side is what you actually need — write a stream of records to an .avro container file for a downstream consumer (Kafka/Data Lake style ingestion). Do a CSV export first: it is read-only, has no schema or transaction semantics, and will tell you quickly whether the column-mapping model fits your domain. Only then try Avro, because Avro adds a schema contract on top of the same mapping layer.
If your goal is genuinely "CSV import/export in Laravel," the honest recommendation is still league/csv (low level) or spatie/simple-excel / maatwebsite/excel (higher level). Reach for this bundle only if you are maintaining a Symfony codebase, need Avro files from PHP, or specifically want its declarative mapping idea.
Borrow the pattern, not the dependency. The valuable idea here is declarative field mapping: attribute your model properties with the CSV header / Avro field they correspond to, and let one generic importer/exporter walk the metadata. You can reproduce the CSV half in ~80 lines of Laravel with PHP 8 attributes and reflection, and you keep full control over validation, casting and transactions. The Avro half is not worth reimplementing — use avro-php directly if that's the requirement.
Adapter service. If you do call into the bundle, isolate it behind one class so the rest of your app never sees Symfony types:
// app/Services/Csv/CsvBridge.php (class names illustrative — confirm against src/)
final class CsvBridge
{
public function export(iterable $records, string $path): void { /* bundle exporter */ }
public function import(string $path, string $class): iterable { /* bundle importer */ }
public function exportAvro(iterable $records, string $schemaPath, string $path): void { /* bundle avro writer */ }
}
Everything else — controllers, jobs, console commands — talks to CsvBridge. Note the constructor: since v1.2.2 the bundle's services receive their dependencies by constructor injection rather than reaching into the container, so if you instantiate anything by hand you must supply those arguments yourself (there is no setContainer() fallback any more).
Workflow for a CSV import:
file_get_contents.DB::transaction(), with an idempotency key (e.g. external SKU) so re-runs update rather than duplicate.Workflow for a CSV export: build a query with an explicit select(), chunk it (lazy() / chunkById), and write to a streamed response or a file on disk with a TTL. Avoid hydrating full Eloquent models for a 200k-row export — use toBase()/query builder and map manually.
Workflow for Avro: treat the .avsc schema as a versioned artefact checked into the repo, not something generated at runtime. Validate every record against the reader/writer schema before writing, because Avro fails late and opaquely (you get a truncated or unreadable container, not a row number). Keep the writer schema alongside the produced file so downstream consumers can deserialize without a Schema Registry lookup.
Integration tips:
Storage facade and hand the bundle an absolute local path (most such libraries expect fopen-able local files, not S3 streams). Stream to a temp disk path and upload on completion..avro fixture per schema version.Maintenance risk is the headline gotcha — but it just moved. v1.2.2 (fix for the removal of ContainerAwareInterface) shows the package is still being touched, which is better than the previous picture. It is still one star, zero dependents, and single-maintainer. Pin the version in composer.json, and be prepared to fork. Do not put this on a critical path without a fallback.
Symfony version coupling. ContainerAwareInterface was deprecated in Symfony 4.4 and removed in Symfony 6.0; this release is the maintainer catching up. Practical consequences:
$this->container or setContainer() will now break — dependencies must be injected. That is effectively a breaking change for subclasses even though the version bump is a patch.Framework mismatch. Symfony bundles assume a container, an event dispatcher and often Doctrine's EntityManager. In Laravel those services don't exist unless you pull in symfony/* pieces or run the bundle inside a Symfony kernel. Expect to construct dependencies by hand — and expect at least one class to type-hint an interface you now have to stub.
Entity coupling. The name says "entities," which usually means Doctrine. Eloquent models will not satisfy Doctrine\ORM type hints. Mapping Eloquent → plain DTOs → bundle is extra work that often erases the convenience you were chasing.
CSV itself is the trap, not the library.
\r\n endings, UTF-8 BOM.01234) and long numbers become floats. Force string casting on read.null vs 0 — decide explicitly, and test all three.01/02/2024. Require ISO-8601 in the source contract.=, +, -, @ become formulas in Excel. Prefix with ' or strip on export for user-supplied data.Avro-specific traps (new in scope with this package).
avro-php's reader or avro-tools tojson, never with a text editor.int/long since epoch, decimals as bytes+precision/scale. A PHP DateTime handed over naively will either throw or silently encode wrong. Convert explicitly.["null","string"]) in the schema; you cannot send null to a non-union field.default breaks backward compatibility for old readers.Memory. If the importer returns an array rather than a generator, a 50MB file will exhaust PHP memory. Check the return type before promising users "any size file." Set memory_limit guardrails and a max upload size.
Debugging. Start with var_dump of the parsed header and the first two rows from the bundle's own parse step — that separates "parsing is wrong" from "mapping is wrong" in one shot. Log the raw row alongside the mapped object when an exception fires. If construction itself fails after upgrading to 1.2.2, the error will be an unresolvable constructor argument, not a runtime exception — read the DI/instantiation trace first.
Extension points. Look for custom type converters, normalizers or event subscribers in src/ — those are usually where date/boolean/enum handling is customized, and they are the natural place to hang your own Avro logical-type converters. If none exist, don't fight it: pre-normalize values in your adapter before handing rows over.
Migration advice. The lowest-risk use is a one-off internal CSV export. If you find yourself writing adapters for Doctrine, container wiring and a bridge layer, stop — port the attribute-mapping idea to Laravel and use league/csv underneath. Keep the bundle only where the Avro writer is doing genuine work for you, since that is the part with no trivial Laravel equivalent.
How can I help you explore Laravel packages today?