DEV Community

Cover image for Building Dynamic Documents Content with Placeholdify
Nasrul Hazim Bin Mohamad
Nasrul Hazim Bin Mohamad

Posted on

Building Dynamic Documents Content with Placeholdify

Building dynamic documents in Laravel applications has always been a challenge. Whether you're generating invoices, certificates, official letters, or personalized emails, managing template placeholders can quickly become a nightmare of string concatenation and conditional logic.

Today, I'm excited to introduce Placeholdify - a powerful Laravel package that transforms how you work with dynamic templates, making placeholder replacement elegant, maintainable, and developer-friendly.

The Problem with Traditional Template Systems

Before diving into what makes Placeholdify special, let's acknowledge the pain points developers face with traditional approaches:

// The old way - messy and error-prone
$template = "Dear {{name}}, your invoice #{{invoice_no}} for {{amount}} is due on {{due_date}}";
$content = str_replace(['{{name}}', '{{invoice_no}}', '{{amount}}', '{{due_date}}'], 
                      [$user->name, $invoice->number, '$' . number_format($invoice->total, 2), $invoice->due_date->format('F j, Y')], 
                      $template);
Enter fullscreen mode Exit fullscreen mode

This approach quickly becomes unwieldy when you need:

  • Date formatting
  • Currency conversion
  • Conditional content
  • Nested object properties
  • Fallback values
  • Reusable template logic

Enter Placeholdify: Template Replacement Reimagined

Placeholdify is a zero-dependency Laravel package that provides a fluent, powerful API for managing dynamic templates. Here's the same example, reimagined:

use CleaniqueCoders\Placeholdify\PlaceholderHandler;

$template = "Dear {name}, your invoice #{invoice_no} for {amount} is due on {due_date}";

$content = (new PlaceholderHandler())
    ->add('name', $user->name)
    ->add('invoice_no', $invoice->number)
    ->addFormatted('amount', $invoice->total, 'currency', 'USD')
    ->addDate('due_date', $invoice->due_date, 'F j, Y')
    ->replace($template);
Enter fullscreen mode Exit fullscreen mode

Clean, readable, and maintainable. But this is just the beginning.

Key Features That Set Placeholdify Apart

1. Context-Aware Mapping

One of Placeholdify's most powerful features is context mapping. Instead of manually extracting properties from objects every time, you can register reusable mappings:

$handler = new PlaceholderHandler();

// Register once
$handler->registerContextMapping('user', [
    'name' => 'full_name',
    'email' => 'email_address',
    'role' => 'roles.0.name', // Supports dot notation
    'company' => fn($user) => $user->company->name ?? 'Freelancer',
]);

// Use anywhere
$handler->useContext('user', $currentUser, 'user');
// This automatically maps: {user_name}, {user_email}, {user_role}, {user_company}
Enter fullscreen mode Exit fullscreen mode

2. Built-in Formatters with Custom Support

Placeholdify comes with powerful built-in formatters for common scenarios:

$handler
    ->addFormatted('price', 1234.56, 'currency', 'MYR') // RM1,234.56
    ->addFormatted('size', 1048576, 'filesize') // 1.00 MB
    ->addFormatted('count', 5, 'number', 2) // 5.00
    ->addDate('created', $model->created_at, 'd/m/Y') // 15/10/2024
    ->addFormatted('status', 'active', 'upper'); // ACTIVE
Enter fullscreen mode Exit fullscreen mode

Need custom formatting? Create your own formatter:

use CleaniqueCoders\Placeholdify\Contracts\FormatterInterface;

class PhoneFormatter implements FormatterInterface
{
    public function getName(): string
    {
        return 'phone';
    }

    public function format($value, ...$options): string
    {
        // Format Malaysian phone numbers
        $cleaned = preg_replace('/\D/', '', $value);
        return preg_replace('/(\d{3})(\d{3})(\d{4})/', '$1-$2-$3', $cleaned);
    }
}

$handler->registerFormatter(new PhoneFormatter());
$handler->addFormatted('contact', '0123456789', 'phone'); // 012-345-6789
Enter fullscreen mode Exit fullscreen mode

3. Lazy Evaluation for Performance

For expensive operations, Placeholdify supports lazy evaluation:

$handler->addLazy('expensive_calculation', function() use ($model) {
    // This only runs if the placeholder is actually used in the template
    return $model->complexCalculation();
});
Enter fullscreen mode Exit fullscreen mode

4. Template Modifiers (Inline Formatting)

Use inline modifiers for quick formatting without pre-processing:

$template = "Hello {name|upper}, your balance is {amount|currency:USD}";
// Works directly without additional setup
Enter fullscreen mode Exit fullscreen mode

5. Dedicated Template Classes

For complex scenarios, create dedicated template classes:

namespace App\Services\Templates;

use CleaniqueCoders\Placeholdify\PlaceholdifyBase;

class InvoiceTemplate extends PlaceholdifyBase
{
    protected function configure(): void
    {
        $this->handler->setFallback('N/A');

        $this->handler->registerContextMapping('customer', [
            'name' => 'company_name',
            'address' => 'billing_address',
            'tax_id' => 'tax_identification',
        ]);
    }

    public function build($invoice): PlaceholderHandler
    {
        return $this->handler
            ->add('invoice_no', $invoice->number)
            ->addDate('date', $invoice->created_at, 'F j, Y')
            ->addDate('due_date', $invoice->due_date, 'F j, Y')
            ->addFormatted('subtotal', $invoice->subtotal, 'currency', 'USD')
            ->addFormatted('tax', $invoice->tax_amount, 'currency', 'USD')
            ->addFormatted('total', $invoice->total, 'currency', 'USD')
            ->useContext('customer', $invoice->customer, 'customer');
    }
}

// Usage
$template = new InvoiceTemplate();
$content = $template->build($invoice)->replace($invoiceTemplate);
Enter fullscreen mode Exit fullscreen mode

Real-World Use Cases

Academic Institution Management

Placeholdify shines in educational institutions where various documents need dynamic generation:

// Student permit system
class PermitTemplate extends PlaceholdifyBase
{
    public function build($application): PlaceholderHandler
    {
        return $this->handler
            ->add('permit_no', $this->generatePermitNumber($application))
            ->addDate('issued_date', now(), 'F j, Y')
            ->addDate('expiry_date', now()->addYear(), 'F j, Y')
            ->useContext('student', $application->student, 'student')
            ->useContext('appliance', $application->appliance, 'appliance')
            ->add('room_no', $application->room_number)
            ->add('approved_by', $application->approvedBy->name);
    }
}
Enter fullscreen mode Exit fullscreen mode

E-commerce and Invoice Generation

Perfect for generating dynamic invoices, receipts, and order confirmations:

$handler = new PlaceholderHandler();
$content = $handler
    ->add('order_id', $order->id)
    ->addDate('order_date', $order->created_at, 'd/m/Y')
    ->addFormatted('total', $order->total, 'currency', 'USD')
    ->useContext('customer', $order->customer, 'customer')
    ->useContext('shipping', $order->shippingAddress, 'shipping')
    ->replace($orderTemplate);
Enter fullscreen mode Exit fullscreen mode

Legal Document Generation

Generate contracts, agreements, and legal notices:

class ContractTemplate extends PlaceholdifyBase
{
    public function build($contract): PlaceholderHandler
    {
        return $this->handler
            ->add('contract_no', $contract->number)
            ->addDate('start_date', $contract->start_date, 'F j, Y')
            ->addDate('end_date', $contract->end_date, 'F j, Y')
            ->addFormatted('monthly_rent', $contract->monthly_rent, 'currency', 'USD')
            ->useContext('tenant', $contract->tenant, 'tenant')
            ->useContext('landlord', $contract->landlord, 'landlord')
            ->useContext('property', $contract->property, 'property');
    }
}
Enter fullscreen mode Exit fullscreen mode

Installation and Getting Started

Getting started with Placeholdify is incredibly simple:

composer require cleaniquecoders/placeholdify
Enter fullscreen mode Exit fullscreen mode

Optionally, publish the configuration file:

php artisan vendor:publish --tag=placeholdify-config
Enter fullscreen mode Exit fullscreen mode

The basic usage couldn't be simpler:

use CleaniqueCoders\Placeholdify\PlaceholderHandler;

// Quick static method
$content = PlaceholderHandler::process($template, [
    'name' => 'John Doe',
    'amount' => '$99.99'
]);

// Or use the fluent API for more control
$handler = new PlaceholderHandler();
$content = $handler
    ->add('name', 'John Doe')
    ->addFormatted('amount', 99.99, 'currency', 'USD')
    ->replace($template);
Enter fullscreen mode Exit fullscreen mode

Advanced Configuration

Placeholdify offers extensive configuration options:

// config/placeholdify.php
return [
    'delimiter' => [
        'start' => '{',
        'end' => '}',
    ],
    'fallback' => 'N/A',
    'formatters' => [
        // Register global formatters
    ],
    'contexts' => [
        // Register global contexts
    ],
];
Enter fullscreen mode Exit fullscreen mode

You can also customize delimiters per instance:

$handler->setDelimiter('{{', '}}'); // Use {{ }} instead of { }
$handler->setDelimiter('[[]]'); // Use [[ ]] for both start and end
Enter fullscreen mode Exit fullscreen mode

Artisan Commands

Placeholdify includes a helpful Artisan command to boost your productivity:

# Generate a new template class
php artisan make:placeholder InvoiceTemplate template

# Generate a new context class
php artisan make:placeholder UserContext context

# Generate a new formatter class
php artisan make:placeholder PhoneFormatter formatter

# List all available component types
php artisan make:placeholder --list
Enter fullscreen mode Exit fullscreen mode

Conclusion

Placeholdify represents a significant evolution in how Laravel developers can handle dynamic template generation. By providing a fluent API, powerful formatting options, context mapping, and extensible architecture, it eliminates the friction and complexity traditionally associated with placeholder replacement.

Whether you're building a simple email system or a complex document generation platform, Placeholdify scales to meet your needs while keeping your code clean, maintainable, and testable.

The package is production-ready, well-tested, and designed to integrate seamlessly with existing Laravel applications. With zero dependencies and comprehensive documentation, there's never been a better time to upgrade your template handling.

Get Started Today

Ready to transform your template handling? Install Placeholdify today:

composer require cleaniquecoders/placeholdify
Enter fullscreen mode Exit fullscreen mode

Visit the GitHub repository for documentation, examples, and community support.


Photo by Luke Chesser on Unsplash

Top comments (0)