maestrodimateo/simple-word

A simple, dev-friendly Laravel wrapper for PHPWord

Maintainers

Package info

github.com/maestrodimateo/simple-word

pkg:composer/maestrodimateo/simple-word

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.2.3 2026-04-30 23:19 UTC

This package is auto-updated.

Last update: 2026-04-30 23:20:42 UTC


README

Latest Version on Packagist License

A simple, dev-friendly Laravel wrapper for PHPWord.

Built on top of PHPWord's TemplateProcessor, this package provides a fluent API for generating Word documents from .docx templates — with variables, tables, images, conditional blocks, PDF conversion, and Mailable-style template classes out of the box.

Installation

composer require maestrodimateo/simple-word

Publish the config:

php artisan vendor:publish --tag=word-config

Add to your .env (optional):

WORD_TEMPLATES_PATH=/path/to/your/templates

By default, templates are resolved from resources/templates/word.

Quick Start

Replace placeholders

In your .docx template, use ${variable} placeholders:

Hello ${name}, your order ${ref} was placed on ${date}.

word()->template('order.docx')
    ->set('name', 'John Doe')
    ->set('ref', 'CMD-001')
    ->set('date', now()->format('d/m/Y'))
    ->save(storage_path('app/orders/order-john.docx'));

Bulk replace

word()->template('order.docx')
    ->set([
        'name' => 'John Doe',
        'ref'  => 'CMD-001',
        'date' => now()->format('d/m/Y'),
    ])
    ->download('order.docx');

Nested arrays (dot notation)

Nested associative arrays are automatically flattened with dot notation. This is useful for grouping related data:

word()->template('contract.docx')
    ->set([
        'client' => [
            'name'  => 'John Doe',
            'email' => 'john@example.com',
            'address' => [
                'city' => 'Paris',
                'zip'  => '75001',
            ],
        ],
        'date' => now()->format('d/m/Y'),
    ])
    ->download('contract.docx');

This replaces ${client.name}, ${client.email}, ${client.address.city}, ${client.address.zip}, and ${date} in the template.

Tables

In your template, prefix table cell placeholders with a group name. See PHPWord cloneRowAndSetValues:

${items.produit} ${items.prix}
word()->template('invoice.docx')
    ->table('items', [
        ['produit' => 'Widget A', 'prix' => '100 EUR'],
        ['produit' => 'Widget B', 'prix' => '200 EUR'],
        ['produit' => 'Widget C', 'prix' => '50 EUR'],
    ])
    ->download('invoice.docx');

The table row is cloned once for each entry and placeholders are filled automatically.

Images

Use ${placeholder} in your template where an image should appear. See PHPWord setImageValue:

Local file

word()->template('report.docx')
    ->set('company', 'Acme Corp')
    ->image('logo', storage_path('app/logo.png'), width: 150, height: 50)
    ->image('signature', storage_path('app/signature.png'))
    ->save(storage_path('app/report.docx'));

Remote URL (GED, CDN, Alfresco, etc.)

image() accepts HTTP/HTTPS URLs. The image is downloaded automatically to a temp file:

word()->template('report.docx')
    ->set('company', 'Acme Corp')
    ->image('logo', 'https://alfresco.example.com/api/-default-/public/alfresco/versions/1/nodes/abc-123/content', width: 150)
    ->download('report.docx');

From a Laravel filesystem disk

Use imageFromDisk() to load images from any configured Laravel filesystem disk (S3, SFTP, custom Alfresco adapter, etc.):

word()->template('report.docx')
    ->set('company', 'Acme Corp')
    ->imageFromDisk('logo', 'alfresco', 'logos/company.png', width: 150, height: 50)
    ->imageFromDisk('signature', 's3', 'signatures/ceo.png')
    ->download('report.docx');

Conditional Blocks

In your template, wrap conditional content with block markers:

${has_discount} You benefit from a 10% loyalty discount! ${/has_discount}

word()->template('offer.docx')
    ->set('client', 'Jane Doe')
    ->when('has_discount', $client->is_vip)
    ->when('has_warranty', $product->warranty_months > 0)
    ->download('offer.docx');

When the condition is true, the content is kept (markers removed). When false, the entire block is removed.

Template Classes (Mailable-style)

For reusable, testable templates, generate a dedicated class:

php artisan make:word-template InvoiceDocument

This creates app/Word/InvoiceDocument.php:

namespace App\Word;

use Maestrodimateo\SimpleWord\DocumentBuilder;
use Maestrodimateo\SimpleWord\WordTemplate;

class InvoiceDocument extends WordTemplate
{
    protected string $template = 'invoice.docx';

    public function __construct(
        private Invoice $invoice,
        private Collection $lines,
    ) {}

    public function build(DocumentBuilder $builder): void
    {
        $builder
            ->set([
                'client' => [
                    'name'  => $this->invoice->client->name,
                    'email' => $this->invoice->client->email,
                ],
                'date'  => $this->invoice->date->format('d/m/Y'),
                'total' => number_format($this->invoice->total, 2) . ' EUR',
            ])
            ->table('lines', $this->lines->map(fn ($l) => [
                'description' => $l->description,
                'quantity'    => $l->quantity,
                'price'       => number_format($l->price, 2) . ' EUR',
            ])->all())
            ->image('logo', storage_path('app/logo.png'), width: 120, height: 40)
            ->when('has_notes', $this->invoice->notes !== null);
    }
}

Use it anywhere:

// In a controller
return word()->generate(new InvoiceDocument($invoice, $lines))
    ->download('invoice.docx');

// Save to disk
word()->generate(new InvoiceDocument($invoice, $lines))
    ->saveTo('s3', "invoices/{$invoice->id}.docx");

PDF Conversion

Convert any document to PDF with toPdf():

word()->template('contract.docx')
    ->set('name', 'John Doe')
    ->toPdf()
    ->download('contract.pdf');

Supported drivers

Driver Requires Quality
libreoffice (default) LibreOffice installed on server Excellent
gotenberg Gotenberg Docker container Excellent

Configuration

WORD_PDF_DRIVER=libreoffice
LIBREOFFICE_PATH=/usr/bin/libreoffice

# Or with Gotenberg
WORD_PDF_DRIVER=gotenberg
GOTENBERG_URL=http://localhost:3000

Custom converter

Implement the PdfConverter interface for any other conversion service:

use Maestrodimateo\SimpleWord\Contracts\PdfConverter;

class CloudConvertPdf implements PdfConverter
{
    public function convert(string $docxPath, string $pdfPath): void
    {
        // Your conversion logic
    }
}

// Use it directly
word()->template('doc.docx')
    ->set('key', 'value')
    ->toPdf(new CloudConvertPdf())
    ->download('doc.pdf');

Output Methods

All output methods are available on both DocumentBuilder (docx) and PdfBuilder (after toPdf()):

Method Description
save($path) Save to a local file path
saveTo($disk, $path) Save to a Laravel filesystem disk (S3, local, etc.)
download($filename) Return a download HTTP response
stream($filename) Return an inline HTTP response (preview in browser)
// Save locally
->save(storage_path('app/document.docx'));

// Save to S3
->saveTo('s3', 'documents/2026/contract.docx');

// Download
return ->download('contract.docx');

// Preview in browser
return ->stream('contract.docx');

Advanced Usage

Access the underlying PHPWord TemplateProcessor for features not covered by the fluent API:

$builder = word()->template('complex.docx');

$processor = $builder->getProcessor();
$processor->cloneBlock('repeating_section', 3);

$builder->save('output.docx');

Facade

use Maestrodimateo\SimpleWord\Facades\Word;

Word::template('doc.docx')->set('key', 'value')->save('output.docx');
Word::generate(new InvoiceDocument($invoice, $lines))->download('invoice.docx');

Helper

word()->template('doc.docx')->set('key', 'value')->save('output.docx');

Configuration

// config/word.php
return [
    // Default directory for .docx templates
    'templates_path' => env('WORD_TEMPLATES_PATH', resource_path('templates/word')),

    // PDF conversion
    'pdf' => [
        'driver'           => env('WORD_PDF_DRIVER', 'libreoffice'),
        'libreoffice_path' => env('LIBREOFFICE_PATH', '/usr/bin/libreoffice'),
        'gotenberg_url'    => env('GOTENBERG_URL', 'http://localhost:3000'),
    ],
];

Artisan Commands

Command Description
make:word-template Generate a new WordTemplate class

Testing

./vendor/bin/pest

Useful PHPWord Links

License

MIT

Credits