composer require webklex/laravel-pdfmerger and add the service provider to config/app.php:
'providers' => [
Webklex\PDFMerger\Providers\PDFMergerServiceProvider::class,
],
'aliases' => [
'PDFMerger' => Webklex\PDFMerger\Facades\PDFMergerFacade::class,
]
use PDFMerger;
$merger = PDFMerger::init();
$merger->addPDF(public_path('file1.pdf'), [1, 2]); // Add specific pages
$merger->addPDF(public_path('file2.pdf'), 'all'); // Add all pages
$merger->merge();
$merger->save(storage_path('merged.pdf'));
PDFMerger facade for fluent syntax.examples/ directory in the package source for use cases.Dynamic PDF Merging:
$merger = PDFMerger::init();
foreach ($pdfFiles as $file) {
$merger->addPDF($file, ['all' | [1, 2, 3]]);
}
$merger->merge()->save($outputPath);
Streaming PDFs from Storage:
$merger = PDFMerger::init();
$merger->addString(Storage::disk('s3')->get('file.pdf'), [1]);
$merger->merge();
return response($merger->output(), 200, ['Content-Type' => 'application/pdf']);
Conditional Page Selection:
$pages = request()->input('pages', 'all');
$merger->addPDF($file, $pages === 'all' ? 'all' : explode(',', $pages));
Batch Processing:
$merger = PDFMerger::init();
foreach ($batch as $item) {
$merger->addPDF($item->path, $item->pages);
}
$merger->merge()->save("batch_{$batchId}.pdf");
MergePdfJob::dispatch($files, $outputPath);
event(new PdfMerged($outputPath));
$this->mock(PDFMerger::class)->shouldReceive('init')->andReturnSelf();
Memory Limits:
memory_limit. Use ini_set('memory_limit', '512M') or chunk processing.Path Handling:
/var/www/file.pdf) work, but relative paths may fail in shared hosting.public_path(), storage_path(), or realpath().Page Indexing:
addPDF($file, [0]) will silently skip the page.$pages = array_filter($pages, fn($p) => $p > 0);.Facade vs. Class:
PDFMerger::init()) and class (new PDFMerger) are interchangeable, but the facade auto-resolves the service container.Output Handling:
merge() returns the instance, but output() returns raw bytes. Forgetting to call output() after merge() may leave the merger in an invalid state.$merger->merge()->output().try {
$merger->merge();
} catch (\Exception $e) {
Log::error("PDF Merge Failed: " . $e->getMessage());
return back()->withError($e->getMessage());
}
if (!file_exists($file)) {
throw new \InvalidArgumentException("File not found: {$file}");
}
Custom Merge Logic:
Override the merge() method in a service class:
class CustomMerger extends \Webklex\PDFMerger\PDFMerger {
public function merge() {
// Add pre-merge logic (e.g., watermarking)
parent::merge();
// Add post-merge logic
}
}
Register the service in config/app.php:
'PDFMerger' => App\Services\CustomMerger::class,
Add Metadata:
Use the underlying setPdf() method to inject custom data:
$merger->addString($pdfData, [1]);
$merger->setPdf(0, ['Title' => 'Custom Title']); // Set metadata for the first PDF
Hooks: Extend the package by publishing config and adding listeners:
php artisan vendor:publish --provider="Webklex\PDFMerger\Providers\PDFMergerServiceProvider"
Then bind events in EventServiceProvider:
protected $listen = [
'pdf.merging' => [YourListener::class],
];
addString() for Dynamic Content: If generating PDFs on-the-fly (e.g., with Dompdf), merge them as strings to avoid temporary files:
$pdfContent = (new Dompdf)->loadHtml($html)->output();
$merger->addString($pdfContent, [1]);
How can I help you explore Laravel packages today?