ddeboer/document-manipulation-bundle
Install the Bundle
Add to composer.json:
"require": {
"ddeboer/document-manipulation-bundle": "^1.0"
}
Run composer update and enable the bundle in config/bundles.php:
return [
// ...
Ddeboer\DocumentManipulationBundle\DdeboerDocumentManipulationBundle::class => ['all' => true],
];
Configure Required Services For LiveDocx (Word):
composer.json (as per README).config/packages/ddeboer_document_manipulation.yaml:
ddeboer_document_manipulation:
livedocx:
username: "%env(LIVE_DOCX_USERNAME)%"
password: "%env(LIVE_DOCX_PASSWORD)%"
wsdl: "%env(LIVE_DOCX_WSDL)%" # Optional for premium
For PDF (pdftk):
pdftk via system package manager (e.g., apt-get install pdftk).config/packages/ddeboer_document_manipulation.yaml:
ddeboer_document_manipulation:
pdftk_path: "/usr/bin/pdftk" # Path to pdftk binary
First Use Case: Mail Merge
Inject the LiveDocxManipulator service and merge a template:
use Ddeboer\DocumentManipulationBundle\Service\LiveDocxManipulator;
class InvoiceController extends AbstractController {
public function generate(LiveDocxManipulator $manipulator) {
$data = ['client' => 'John Doe', 'amount' => 100.00];
$templatePath = $this->getParameter('kernel.project_dir').'/templates/invoice.docx';
$outputPath = $this->getParameter('kernel.project_dir').'/uploads/invoice_'.uniqid().'.docx';
$manipulator->merge($templatePath, $data, $outputPath);
return $this->json(['success' => true, 'path' => $outputPath]);
}
}
LiveDocxManipulator for Word documents with placeholders (e.g., {client_name}).
$manipulator->merge(
$templatePath,
['client_name' => 'Acme Corp', 'date' => now()->format('Y-m-d')],
$outputPath
);
PdfManipulator:
$pdfManipulator = $this->container->get('ddeboer_document_manipulation.pdf_manipulator');
$pdfManipulator->concatenate(
[$pdf1Path, $pdf2Path],
$outputPath
);
foreach ($invoices as $invoice) {
$outputPath = $this->generateOutputPath($invoice->id);
$manipulator->merge($template, $invoice->toArray(), $outputPath);
$this->queueEmailAttachment($outputPath, $invoice->email);
}
use League\Flysystem\Filesystem;
$filesystem = new Filesystem($adapter);
$manipulator->merge($template, $data, $tempPath);
$filesystem->put('invoices/invoice_'.$invoiceId.'.docx', file_get_contents($tempPath));
unlink($tempPath); // Cleanup
try {
$manipulator->merge($template, $data, $outputPath);
} catch (\Exception $e) {
$this->logger->error('Document merge failed', ['error' => $e->getMessage()]);
throw new \RuntimeException('Failed to generate document', 0, $e);
}
Service Container Access: Use dependency injection or resolve services manually:
$manipulator = app('ddeboer_document_manipulation.live_docx_manipulator');
Configuration Overrides:
Override bundle config in config/packages/override/ddeboer_document_manipulation.yaml:
ddeboer_document_manipulation:
pdftk_path: "%env(PDFTK_PATH)%"
livedocx:
timeout: 30 # Override default timeout
Event Listeners: Trigger actions post-generation (e.g., send email):
// config/services.yaml
App\Listener\DocumentGeneratedListener:
tags:
- { name: kernel.event_listener, event: app.document_generated, method: onDocumentGenerated }
LiveDocx API Limits:
pdftk Dependencies:
pdftk requires Java on some systems. Ensure it’s installed and in PATH.pdftk manually first:
pdftk input1.pdf input2.pdf cat output combined.pdf
File Permissions:
www-data) has write permissions for output directories.umask in your deployment script or use a dedicated uploads directory:
chmod -R 775 /path/to/uploads
Template Placeholder Syntax:
{{placeholder}}). Avoid conflicts with Twig templates by escaping or using unique prefixes (e.g., {{docx_client_name}}).Memory Limits:
memory_limit. Increase it temporarily:
ini_set('memory_limit', '512M');
Enable Verbose Logging: Configure Monolog to log bundle operations:
# config/packages/monolog.yaml
monolog:
handlers:
document_manipulation:
type: stream
path: "%kernel.logs_dir%/document_manipulation.log"
level: debug
channels: ["ddeboer_document_manipulation"]
Check LiveDocx Responses: Inspect raw API responses for errors:
try {
$manipulator->merge($template, $data, $outputPath);
} catch (\ZendService\LiveDocx\Exception $e) {
\Log::error('LiveDocx Error', [
'code' => $e->getCode(),
'message' => $e->getMessage(),
'raw_response' => $e->getRawResponse()
]);
}
Validate Input Files: Ensure templates are valid before merging:
if (!file_exists($templatePath) || !is_readable($templatePath)) {
throw new \InvalidArgumentException("Template file not found or unreadable");
}
Custom Manipulators: Extend the bundle by creating your own manipulator class:
use Ddeboer\DocumentManipulationBundle\Service\AbstractManipulator;
class CustomPdfManipulator extends AbstractManipulator {
public function watermark($inputPath, $outputPath, $watermarkText) {
// Implement custom logic (e.g., using Imagick)
}
}
Register it as a service:
services:
App\Service\CustomPdfManipulator:
tags: ['ddeboer_document_manipulation.manipulator']
Pre/Post-Processing Hooks: Use Symfony events to intercept document operations:
// src/EventListener/DocumentEventListener.php
class DocumentEventListener {
public function onDocumentMerge(DocumentMergeEvent $event) {
// Modify $event->getData() or $event->getOutputPath()
}
}
Dispatch events in the bundle’s manipulator classes (requires patching or extending).
Alternative Backends: Replace LiveDocx with PhpWord or **DocRapt
How can I help you explore Laravel packages today?