ecourty/doctrine-export-bundle
Composer 安装命令:
composer require ecourty/doctrine-export-bundle
包简介
A Symfony bundle for exporting Doctrine entities to various formats (CSV, JSON, XML)
README 文档
README
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
- Installation
- Quick Start
- Key Features
- Performance
- Usage
- Advanced Options
- Supported Formats
- Development
- Requirements
- License
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
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
- 🎯 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 (< 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
Benchmarked with realistic dataset (10,000 entities, 15 fields):
| Format | Time (10k entities) | Memory Usage | Throughput |
|---|---|---|---|
| CSV | 0.274s | 2.00 MB | 36,496 ent/s |
| JSON | 0.238s | < 0.1 MB | 42,017 ent/s |
| XML | 0.314s | < 0.1 MB | 31,847 ent/s |
| Generator | 0.305s | < 0.1 MB | 32,787 ent/s |
Tested: 10,000 entities × 15 fields = 150,000 data points
Linear scaling: tested with 100,000 entities in ~2.5s with < 5 MB memory 🚀
Usage
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)
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
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
PreExportEvent- Dispatched before export beginsPostExportEvent- Dispatched after export completes (includes count and duration)
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
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
PreExportEvent:
getEntityClass()- Entity class being exportedgetFormat()- Export format (CSV, JSON, XML)getCriteria()- Filter criteriagetLimit()- Result limitgetOffset()- Result offsetgetOrderBy()- Sort ordergetFields()- Selected fieldsgetOptions()- Export options
PostExportEvent:
- All PreExportEvent properties
getExportedCount()- Number of entities exportedgetDurationInSeconds()- Export duration measured with microsecond precision (float)
Supported Formats
| Format | Extension | Description | Use Case |
|---|---|---|---|
| CSV | .csv |
Comma-separated values | Spreadsheets, Excel |
| JSON | .json |
JSON format | APIs |
| XML | .xml |
XML with configurable structure | Legacy enterprise systems |
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
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
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('/(?<=.).(?=.*@)/', '*', $data['email']); } return $data; } }
Using Processors
$exporter->exportToFile( entityClass: User::class, format: ExportFormat::CSV, filePath: '/tmp/users.csv', processors: [new EmailMaskingProcessor()] );
Adding Virtual Fields
class UserVirtualFieldsProcessor implements EntityProcessorInterface { public function process(object $entity, array $data, array $options): array { assert($entity instanceof User); // Add computed fields $data['displayName'] = $entity->getFirstName() . ' ' . $entity->getLastName(); $data['ageCategory'] = $entity->getAge() >= 30 ? 'senior' : 'junior'; return $data; } } // Export with virtual fields $exporter->exportToFile( entityClass: User::class, format: ExportFormat::JSON, filePath: '/tmp/users.json', fields: ['firstName', 'displayName', 'ageCategory'], // Include virtual fields processors: [new UserVirtualFieldsProcessor()] );
Chaining Multiple Processors
Processors are executed in order, allowing you to compose transformations:
$exporter->exportToFile( entityClass: User::class, format: ExportFormat::CSV, filePath: '/tmp/users.csv', processors: [ new EmailMaskingProcessor(), // First: mask emails new UppercaseProcessor(), // Then: uppercase all strings new UserVirtualFieldsProcessor(), // Finally: add virtual fields ] );
Performance Optimization: Disable Default Processor
When using a fully custom processor that handles all data extraction, disable the default processor for better performance:
class FullyCustomProcessor implements EntityProcessorInterface { public function process(object $entity, array $data, array $options): array { // Handle ALL field extraction yourself $data['id'] = $entity->getId(); $data['email'] = $entity->getEmail(); // ... handle all fields return $data; } } $exporter->exportToFile( entityClass: User::class, format: ExportFormat::JSON, filePath: '/tmp/users.json', fields: ['id', 'email'], options: [ // Skip default processor - custom processor handles everything DoctrineExporterInterface::OPTION_DISABLE_DEFAULT_PROCESSOR => true, ], processors: [new FullyCustomProcessor()] );
Note: The default processor handles property access, associations, and data normalization. Only disable it when your custom processor fully replaces this functionality.
Field Selection
You can specify which fields to export. If not specified, all entity fields are exported:
// Export only specific fields $exporter->exportToFile( entityClass: User::class, format: ExportFormat::CSV, filePath: '/tmp/users_minimal.csv', fields: ['id', 'email', 'firstName', 'lastName'] // Only these fields ); // Export all fields (default) $exporter->exportToFile( entityClass: User::class, format: ExportFormat::CSV, filePath: '/tmp/users_full.csv', fields: [] // Empty = all fields );
Note: Field names are validated against the entity metadata. If you specify a field that doesn't exist, an InvalidCriteriaException will be thrown.
Export Options
use Ecourty\DoctrineExportBundle\Contract\DoctrineExporterInterface; $exporter->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
| Option | Type | Default | Description |
|---|---|---|---|
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
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
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(), noflush(), 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
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
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
- PHP 8.3 or higher
- Symfony 7.0 or 8.0
- Doctrine ORM 3.0 or 4.0
License
MIT
ecourty/doctrine-export-bundle 适用场景与选型建议
ecourty/doctrine-export-bundle 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 203 次下载、GitHub Stars 达 4, 最近一次更新时间为 2025 年 12 月 10 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「symfony」 「json」 「xml」 「csv」 「bundle」 「doctrine」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 ecourty/doctrine-export-bundle 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 ecourty/doctrine-export-bundle 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 ecourty/doctrine-export-bundle 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Kinikit - PHP Application development framework MVC component
The bundle for easy using json-rpc api on your project
ext-json wrapper with sane defaults
A package to cast json fields, each sub-keys is castable
Load DOM document safety
Bundle Symfony DaplosBundle
统计信息
- 总下载量: 203
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 4
- 点击次数: 10
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-12-10