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

Dataexporter Bundle Laravel Package

fungio/dataexporter-bundle

A Symfony bundle for exporting data to CSV, Excel, and other formats. Define exporters, map columns, and generate downloadable files from arrays or Doctrine queries through a configurable, extensible workflow—ideal for reports and admin panels.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register

composer require fungio/dataexporter-bundle
// config/bundles.php
return [
    // ...
    Fungio\DataExporterBundle\FungioDataExporterBundle::class => ['all' => true],
];

With Symfony Flex the bundle registration is usually automatic; without it you must add the entry by hand. There is no recipe for a 4-star package, so assume manual registration.

Where to look first (in this order)

  1. Resources/config/services.yml (or config/services.yaml) in the bundle — this is the source of truth for service IDs. Do not trust README examples, since they drift between tags; the YAML does not.
  2. DependencyInjection/Configuration.php — tells you whether the bundle exposes a fungio_data_exporter: config tree at all. If it's absent, everything is convention-based and there is nothing to configure.
  3. The Exporter/ (or Export/) directory — the ExporterInterface is what you should type-hint against, not concrete classes.

First use case: CSV download from a controller

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ReportController extends AbstractController
{
    public function export(ExporterInterface $exporter): Response
    {
        $rows = [
            ['id', 'name', 'total'],
            [1, 'Widget', '19.99'],
            [2, 'Gadget', '24.50'],
        ];

        $content = $exporter->export($rows);

        return new Response($content, 200, [
            'Content-Type'        => 'text/csv; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="report.csv"',
        ]);
    }
}

If autowiring fails, bind explicitly in config/services.yaml:

services:
    Fungio\DataExporterBundle\Exporter\ExporterInterface:
        alias: fungio.data_exporter.csv   # confirm exact ID via `debug:container`

Confirm the ID before you write the alias:

bin/console debug:container | grep -i fungio

Implementation Patterns

Format switching at runtime. The bundle's value proposition is one interface, five formats. Wrap the individual exporters in your own small registry rather than if/else-ing service IDs in every controller:

final class ExportRegistry
{
    /** @param array<string, ExporterInterface> $exporters */
    public function __construct(private array $exporters) {}

    public function forFormat(string $format): ExporterInterface
    {
        return $this->exporters[$format]
            ?? throw new \InvalidArgumentException("Unsupported format: $format");
    }
}
services:
    App\Export\ExportRegistry:
        arguments:
            $exporters:
                csv:  '@fungio.data_exporter.csv'
                json: '@fungio.data_exporter.json'
                xml:  '@fungio.data_exporter.xml'
                html: '@fungio.data_exporter.html'
                xls:  '@fungio.data_exporter.xls'

Use explicit keys instead of !tagged_iterator unless the bundle actually tags its services — tags are the first thing that goes missing in a small, low-traffic package.

Feeding it Doctrine results. Exporters almost certainly take a flat array of rows. For scalar exports use getArrayResult() on a Query, not getResult(), so you avoid hydrating entities you're about to flatten:

$rows = $this->getDoctrine()->getRepository(Order::class)
    ->createQueryBuilder('o')
    ->select('o.id', 'o.reference', 'o.total')
    ->where('o.createdAt >= :from')
    ->setParameter('from', $from)
    ->getQuery()
    ->getArrayResult();

Reusable export action. Keep the controller thin — put header generation (content type, filename, charset) in a single service or trait so all five formats share one code path and one set of headers.

Testing. Assert on Content-Type and Content-Disposition headers, and snapshot the body for a fixed fixture. This is the cheapest way to catch a bundle upgrade silently changing its output format (e.g. delimiter, escaping, XML root element).

Gotchas and Tips

Verify the API before trusting the README. With 4 stars and 0 dependents there is essentially no community proof-reading of the docs. The method signature may be export(array $data), export(array $data, array $options = []), or a fluent builder. Read the interface file in vendor/ first; it takes two minutes and saves an afternoon.

XLS is probably not a real XLS. Small exporters usually emit an HTML table with an .xls extension, or an XML Spreadsheet 2003 file. Excel will open it with a warning; LibreOffice and phpspreadsheet readers will not. If downstream users need real binary files, budget for PhpSpreadsheet instead.

Memory blows up on big exports. A string-returning export() builds the entire document in memory. That's fine to a few thousand rows and painful past ~50k. For large datasets, batch with ->toIterable() and stream with StreamedResponse + fputcsv, or move to league/csv's Writer and skip this package for the CSV path specifically.

CSV formula injection. Any cell beginning with =, +, -, or @ becomes executable in Excel/Sheets. If you export user-supplied strings, prefix with a single quote or a tab before handing rows to the exporter. This is a your-code problem, not a bundle problem — but nobody warns you.

UTF-8 in Excel. Excel misreads UTF-8 CSV without a BOM. Prepending "\xEF\xBB\xBF" to the CSV output fixes it and breaks nothing else.

XML escaping and shape. Expect no schema, no configurable root element, and no CDATA handling. Any &, <, or > in your data should be escaped by the exporter — verify it with a fixture containing &, otherwise you ship invalid XML to a consumer's parser.

Symfony version ceiling. Check composer.json for the symfony/* constraint. A bundle this small is often pinned to ^4.4|^5.0; on Symfony 6/7 the AbstractBundle/Extension signatures may break, and the fix is a fork. Decide up front whether you're comfortable maintaining that fork.

Swap-ability is the real design decision. Because maintenance is uncertain, type-hint against your own App\Export\ExporterInterface and adapt the bundle behind it. Then replacing the package with league/csv + symfony/serializer later is a one-file change, not a refactor of every controller.

Debugging. bin/console debug:container --show-private | grep fungio will surface services that autowiring hides; debug:container fungio.data_exporter.csv --show-arguments confirms which concrete class backs the ID when aliases point in circles.

Extension point. If the bundle tags its exporters, adding a format means implementing ExporterInterface and registering your class with that same tag — cleaner than duplicating format-switching logic. If there is no tag, skip the abstraction and register your exporter in the explicit registry map shown above.

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.
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
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core