PHPackages                             ecourty/doctrine-export-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. ecourty/doctrine-export-bundle

ActiveSymfony-bundle[Database &amp; ORM](/categories/database)

ecourty/doctrine-export-bundle
==============================

A Symfony bundle for exporting Doctrine entities to various formats (CSV, JSON, XML)

1.2.1(3mo ago)477↓50%[1 PRs](https://github.com/EdouardCourty/doctrine-export-bundle/pulls)MITPHPPHP &gt;=8.3CI passing

Since Dec 10Pushed 3mo agoCompare

[ Source](https://github.com/EdouardCourty/doctrine-export-bundle)[ Packagist](https://packagist.org/packages/ecourty/doctrine-export-bundle)[ RSS](/packages/ecourty-doctrine-export-bundle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (4)Dependencies (17)Versions (11)Used By (0)

Doctrine Export Bundle
======================

[](#doctrine-export-bundle)

[![CI](https://github.com/EdouardCourty/doctrine-export-bundle/actions/workflows/ci.yml/badge.svg)](https://github.com/EdouardCourty/doctrine-export-bundle/actions/workflows/ci.yml)

A flexible and extensible Symfony bundle for exporting Doctrine entities to various formats (CSV, JSON, XML).

**Compatible with PHP 8.3+, Symfony 7.x/8.x, and Doctrine ORM 3.x/4.x** 🎉

📖 Table of Contents
-------------------

[](#-table-of-contents)

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Key Features](#-key-features)
- [Performance](#performance)
- [Usage](#usage)
    - [Basic Export to File](#basic-export-to-file)
    - [Streaming Export (Binary Response)](#streaming-export-binary-response)
- [Advanced Options](#advanced-options)
    - [Custom Entity Processors](#custom-entity-processors)
    - [Field Selection](#field-selection)
    - [Export Options](#export-options)
    - [Field Validation](#field-validation)
    - [Memory Management](#memory-management)
    - [Association Handling](#association-handling)
    - [Events](#events)
- [Supported Formats](#supported-formats)
- [Development](#development)
- [Requirements](#requirements)
- [License](#license)

Installation
------------

[](#installation)

```
composer require ecourty/doctrine-export-bundle
```

If you're not using Symfony Flex, enable the bundle manually:

```
// config/bundles.php
return [
    // ...
    Ecourty\DoctrineExportBundle\DoctrineExportBundle::class => ['all' => true],
];
```

Quick Start
-----------

[](#quick-start)

Export entities in a few lines:

```
use Ecourty\DoctrineExportBundle\Contract\DoctrineExporterInterface;
use Ecourty\DoctrineExportBundle\Enum\ExportFormat;

// Inject the service
public function __construct(
    private DoctrineExporterInterface $exporter
) {}

// Export to CSV
$this->exporter->exportToFile(
    entityClass: User::class,
    format: ExportFormat::CSV,
    filePath: '/tmp/users.csv'
);

// Stream to browser
return new StreamedResponse(
    $this->exporter->exportToGenerator(User::class, ExportFormat::JSON),
    Response::HTTP_OK,
    ['Content-Type' => 'application/json']
);
```

That's it! 🚀

✨ Key Features
--------------

[](#-key-features)

- **🎯 Field Selection** - Export only the fields you need
- **🔍 Advanced Filtering** - Filter by criteria, pagination, ordering
- **📦 Multiple Formats** - CSV, JSON, XML out of the box
- **💾 Memory Efficient** - Streaming support via generators (&lt; 5 MB for 100k entities)
- **⚡ High Performance** - 42,000 entities/second (JSON), linear O(n) scaling
- **🔌 Extensible** - Add custom formats with the Strategy pattern
- **🎯 Type-Safe** - PHP 8.1+ enums for format specification
- **🛡️ XML Native** - Uses XMLWriter for guaranteed valid XML

Performance
-----------

[](#performance)

Benchmarked with realistic dataset (10,000 entities, 15 fields):

FormatTime (10k entities)Memory UsageThroughputCSV0.274s2.00 MB36,496 ent/sJSON0.238s&lt; 0.1 MB42,017 ent/sXML0.314s&lt; 0.1 MB31,847 ent/sGenerator0.305s&lt; 0.1 MB32,787 ent/s*Tested: 10,000 entities × 15 fields = 150,000 data points*

**Linear scaling**: tested with 100,000 entities in ~2.5s with &lt; 5 MB memory 🚀

Usage
-----

[](#usage)

### Basic Export to File

[](#basic-export-to-file)

```
use Ecourty\DoctrineExportBundle\Contract\DoctrineExporterInterface;
use Ecourty\DoctrineExportBundle\Enum\ExportFormat;
use App\Entity\User;

class UserExportService
{
    public function __construct(
        private DoctrineExporterInterface $exporter
    ) {}

    public function exportActiveUsers(): void
    {
        $this->exporter->exportToFile(
            entityClass: User::class,
            format: ExportFormat::CSV,
            filePath: '/tmp/active_users.csv',
            criteria: ['isActive' => true],
            limit: 1000,
            orderBy: ['createdAt' => 'DESC']
        );
    }
}
```

### Streaming Export (Binary Response)

[](#streaming-export-binary-response)

Simply pass the generator to a `StreamedResponse`:

```
use Symfony\Component\HttpFoundation\StreamedResponse;

#[Route('/export/users')]
public function export(DoctrineExporterInterface $exporter): StreamedResponse
{
    $format = ExportFormat::CSV;

    return new StreamedResponse(
        $exporter->exportToGenerator(
            entityClass: User::class,
            format: $format,
            criteria: ['isActive' => true]
        ),
        200,
        [
            'Content-Type' => $format->getMimeType(),
            'Content-Disposition' => 'attachment; filename="users.' . $format->getExtension() . '"'
        ]
    );
}
```

### Events

[](#events)

The bundle dispatches events before and after each export, allowing you to hook into the export lifecycle for logging, monitoring, or custom logic.

**Events are optional** - if no event dispatcher is configured, exports work normally without events.

The bundle uses the **PSR-14 EventDispatcherInterface** (`Psr\EventDispatcher\EventDispatcherInterface`), making it compatible with any PSR-14 compliant event dispatcher, not just Symfony's.

#### Available Events

[](#available-events)

- **`PreExportEvent`** - Dispatched before export begins
- **`PostExportEvent`** - Dispatched after export completes (includes count and duration)

#### Example: Logging Exports

[](#example-logging-exports)

```
use Ecourty\DoctrineExportBundle\Event\PostExportEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;

#[AsEventListener]
public function onPostExport(PostExportEvent $event): void
{
    $this->logger->info('Export completed', [
        'entity' => $event->getEntityClass(),
        'count' => $event->getExportedCount(),
    ]);
}
```

#### Example: Performance Monitoring

[](#example-performance-monitoring)

```
use Ecourty\DoctrineExportBundle\Event\PostExportEvent;

#[AsEventListener]
public function onPostExport(PostExportEvent $event): void
{
    $duration = $event->getDurationInSeconds();
    $throughput = $event->getExportedCount() / $duration;

    $this->metrics->gauge('export.duration', $duration);
    $this->metrics->gauge('export.throughput', $throughput);
}
```

#### Event Properties

[](#event-properties)

**PreExportEvent:**

- `getEntityClass()` - Entity class being exported
- `getFormat()` - Export format (CSV, JSON, XML)
- `getCriteria()` - Filter criteria
- `getLimit()` - Result limit
- `getOffset()` - Result offset
- `getOrderBy()` - Sort order
- `getFields()` - Selected fields
- `getOptions()` - Export options

**PostExportEvent:**

- All PreExportEvent properties
- `getExportedCount()` - Number of entities exported
- `getDurationInSeconds()` - Export duration measured with microsecond precision (float)

Supported Formats
-----------------

[](#supported-formats)

FormatExtensionDescriptionUse CaseCSV`.csv`Comma-separated valuesSpreadsheets, ExcelJSON`.json`JSON formatAPIsXML`.xml`XML with configurable structureLegacy enterprise systems### Format Examples

[](#format-examples)

```
// CSV
$exporter->exportToFile(User::class, ExportFormat::CSV, '/tmp/users.csv');

// JSON
$exporter->exportToFile(User::class, ExportFormat::JSON, '/tmp/users.json');

// XML
$exporter->exportToFile(User::class, ExportFormat::XML, '/tmp/users.xml');
```

Advanced Options
----------------

[](#advanced-options)

### Custom Entity Processors

[](#custom-entity-processors)

Implement custom data transformations with entity processors. They allow you to modify exported data, add virtual fields, or apply business logic during export.

#### Creating a Custom Processor

[](#creating-a-custom-processor)

```
use Ecourty\DoctrineExportBundle\Contract\EntityProcessorInterface;

class EmailMaskingProcessor implements EntityProcessorInterface
{
    public function process(object $entity, array $data, array $options): array
    {
        // Mask email addresses
        if (isset($data['email'])) {
            $data['email'] = preg_replace('/(?exportToFile(
    entityClass: User::class,
    format: ExportFormat::CSV,
    filePath: '/tmp/users.csv',
    options: [
        // Boolean values as integers (default: true)
        DoctrineExporterInterface::OPTION_BOOLEAN_TO_INTEGER => false,

        // Custom datetime format (default: ATOM)
        DoctrineExporterInterface::OPTION_DATETIME_FORMAT => 'Y-m-d H:i:s',

        // Custom null value representation (default: null)
        DoctrineExporterInterface::OPTION_NULL_VALUE => '',

        // Strict field validation - throw exception if field doesn't exist (default: false)
        DoctrineExporterInterface::OPTION_STRICT_FIELDS => true,

        // Disable default processor for performance (default: false)
        // Only use when custom processor handles all data extraction
        DoctrineExporterInterface::OPTION_DISABLE_DEFAULT_PROCESSOR => true,
    ]
);
```

#### Available Options

[](#available-options)

OptionTypeDefaultDescription`OPTION_BOOLEAN_TO_INTEGER``bool``true`Convert boolean values to integers (1/0) instead of strings ('true'/'false')`OPTION_DATETIME_FORMAT``string``DateTimeInterface::ATOM`PHP date format for DateTime fields (e.g., 'Y-m-d H:i:s', 'c')`OPTION_NULL_VALUE``string|int|float``null`Custom representation for null values (e.g., 'NULL', 'N/A', '')`OPTION_STRICT_FIELDS``bool``false`Throw exception if a requested field doesn't exist on the entity`OPTION_DISABLE_DEFAULT_PROCESSOR``bool``false`Skip default processor when using custom processors that handle all processing### Field Validation

[](#field-validation)

The bundle automatically validates that fields in `criteria` and `orderBy` exist in the entity. If an invalid field is specified, an `InvalidCriteriaException` will be thrown with a helpful error message listing all available fields.

```
// This will throw InvalidCriteriaException if 'nonExistentField' doesn't exist
$exporter->exportToFile(
    entityClass: User::class,
    format: ExportFormat::CSV,
    filePath: '/tmp/users.csv',
    criteria: ['nonExistentField' => 'value'] // ❌ Throws exception
);
```

### Memory Management

[](#memory-management)

The bundle uses **streaming** and entity detachment for automatic memory efficiency:

```
// Streaming export - automatically memory efficient
foreach ($exporter->exportToGenerator(User::class, ExportFormat::CSV) as $line) {
    echo $line; // Each entity is processed and immediately detached
    flush();
}
```

**How it works:**

- Uses Doctrine's `toIterable()` for **true streaming** (one entity at a time)
- Each entity is **detached** immediately after processing
- Detachment is **safe**: only affects memory tracking, not your database
- No `clear()`, no `flush()`, no batch management needed
- PHP's garbage collector handles memory automatically with the streaming approach

**Why no batching?**This bundle doesn't need batch processing because:

- Entities are processed one-by-one (real streaming)
- Each entity is detached immediately (no accumulation)
- Generators ensure minimal memory footprint naturally
- No circular references are created

### Association Handling

[](#association-handling)

**Doctrine associations are automatically exported as primary keys:**

```
// Given entities:
// Article (id, title, author_id) -> ManyToOne -> User (id, name)
// Article (id, title) -> ManyToMany -> Tag (id, name)

$exporter->exportToFile(
    entityClass: Article::class,
    format: ExportFormat::JSON,
    filePath: '/tmp/articles.json',
    fields: ['title', 'author', 'tags']
);

// Output:
// [
//   {"title": "Article 1", "author": 42, "tags": [1, 2, 3]},
//   {"title": "Article 2", "author": 43, "tags": [2, 4]}
// ]
```

**How it works:**

- **ManyToOne / OneToOne**: Exported as the related entity's primary key (integer or string)
- **ManyToMany / OneToMany**: Exported as an array of primary keys `[1, 2, 3]`
- **Null associations**: Exported as `null`
- **Collections**: Empty collections exported as `[]`
- **No lazy loading issues**: Primary keys are extracted without triggering proxy initialization
- **Format-specific rendering**: JSON keeps arrays native, CSV/XML encode as JSON string

**Benefits:**

- Avoids N+1 queries and lazy loading issues
- Keeps export lightweight and predictable
- Easy to re-hydrate entities on import if needed
- Works seamlessly with all export formats

Development
-----------

[](#development)

### Quality Assurance

[](#quality-assurance)

Run all quality checks:

```
composer qa
```

Individual commands:

```
# Code style check
composer cs-check

# Fix code style
composer cs-fix

# Static analysis (PHPStan level 9)
composer phpstan

# Run tests
composer test

# Run all tests (including performance)
composer test:all

# Run only performance tests
composer test:performance
```

Requirements
------------

[](#requirements)

- PHP 8.3 or higher
- Symfony 7.0 or 8.0
- Doctrine ORM 3.0 or 4.0

License
-------

[](#license)

MIT

###  Health Score

42

—

FairBetter than 90% of packages

Maintenance78

Regular maintenance activity

Popularity16

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~10 days

Total

4

Last Release

118d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/3150ffb131124e5f03272d9ed8084c514f18fff6aafff1a5973c016993f6ef66?d=identicon)[ecourty](/maintainers/ecourty)

---

Top Contributors

[![EdouardCourty](https://avatars.githubusercontent.com/u/37371516?v=4)](https://github.com/EdouardCourty "EdouardCourty (16 commits)")

---

Tags

jsonsymfonybundlexmlexportdoctrinecsv

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ecourty-doctrine-export-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/ecourty-doctrine-export-bundle/health.svg)](https://phpackages.com/packages/ecourty-doctrine-export-bundle)
```

###  Alternatives

[sonata-project/doctrine-orm-admin-bundle

Integrate Doctrine ORM into the SonataAdminBundle

46117.7M155](/packages/sonata-project-doctrine-orm-admin-bundle)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[fresh/doctrine-enum-bundle

Provides support of ENUM type for Doctrine2 in Symfony applications.

4636.8M12](/packages/fresh-doctrine-enum-bundle)[onurb/doctrine-yuml-bundle

Symfony Bundle to visualize the mapping of your entities with Yuml

4198.6k](/packages/onurb-doctrine-yuml-bundle)[prezent/doctrine-translatable-bundle

Integrate the doctrine-translatable extension in Symfony

14698.4k5](/packages/prezent-doctrine-translatable-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
