Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Spintax Laravel Package

madeitbelgium/spintax

Process spintax templates in Laravel with madeitbelgium/spintax. Expand {a|b|c} syntax into random variations for emails, copy, and SEO content, with a simple API that integrates cleanly into any PHP or Laravel app.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require madeitbelgium/spintax

Laravel wiring — the package ships a service provider with package auto-discovery, so on Laravel 5.5+ there is nothing to register. The facade is aliased as Spintax. On older apps, add to config/app.php:

'providers' => [
    MadeITBelgium\Spintax\SpintaxServiceProvider::class,
],
'aliases' => [
    'Spintax' => MadeITBelgium\Spintax\Facades\Spintax::class,
],

First use case — pick one variant out of a template:

use MadeITBelgium\Spintax\Facades\Spintax;

Spintax::parse('{Hello|Hi|Hey} there, {friend|mate}');
// => "Hi there, mate"  (re-randomised on every call)

Where to look first

  1. src/Spintax.php — the actual parser. Read this before anything else; it is small, and the public method name (parse() / spin()) has drifted across madeITBelgium packages, so confirm it there rather than trusting a blog post.
  2. src/SpintaxServiceProvider.php — confirms the facade alias and whether a config file is publishable (php artisan vendor:publish --provider="MadeITBelgium\Spintax\SpintaxServiceProvider").
  3. The README example — enough to cover 90% of usage; the API surface is one method.

Framework-agnostic use

$spintax = new \MadeITBelgium\Spintax\Spintax();
echo $spintax->parse('{Red|Green|Blue}');

Use this form inside Artisan commands, queue workers, or plain PHP scripts where you don't want the container involved.

Implementation Patterns

Wrap it behind your own class. The package is a single static-ish call; injecting the concrete package class all over your app makes it painful to swap later. Bind a thin wrapper:

// app/Support/TextSpinner.php
namespace App\Support;

class TextSpinner
{
    public function spin(string $template): string
    {
        return app('spintax')->parse($template);
    }
}
// AppServiceProvider::register()
$this->app->bind('spintax', fn () => new \MadeITBelgium\Spintax\Spintax());

Now tests can swap the binding for a deterministic stub.

Mail and notifications. Spintax shines in subject lines, where the variation is invisible to the user but affects open rates:

public function envelope(): Envelope
{
    return new Envelope(
        subject: Spintax::parse('{Your|Quick} {invoice|receipt} is {ready|available}'),
    );
}

Generate the subject inside envelope() and not in the constructor — queued mails are serialised and rebuilt, so computing it at send time keeps each delivery distinct.

Blade helper. Register a directive once and use it in views:

Blade::directive('spin', fn ($expr) => "<?php echo e(app('spintax')->parse($expr)); ?>");
@spin('{Welcome|Good to see you}, {{ $user->name }}')

Note the escaped {{ }} — Blade compiles its own braces first, so any spintax containing {{ needs care (see Gotchas).

Seeding test data. This is the highest-value day-to-day use: varied but plausible fixture text without a faker library.

User::factory()->create(['bio' => Spintax::parse(
    '{Software|Backend|Full-stack} developer who {loves|enjoys} {PHP|Laravel|clean code}.'
)]);

Unique-variant batching. When you need N distinct outputs (subject line A/B lists, sitemap descriptions), loop with a guard:

$seen = [];
$attempts = 0;

while (count($seen) < $count && $attempts++ < $count * 10) {
    $seen[Spintax::parse($template)] = true;
}

if (count($seen) < $count) {
    // The template has fewer combinations than you asked for — log it.
}

Bound the loop. A template with three binary choices yields eight outcomes; asking for fifty will spin forever otherwise.

Config-driven templates. Keep spintax strings in config/ or the database rather than inline in PHP, so marketers can edit copy without a deploy, and you can preview the variant space in a Nova/Filament panel.

Gotchas and Tips

Every call re-randomises — there is no seed. You cannot reproduce the same output twice, and there is no parse($template, $seed) equivalent. Tests must mock the facade, and anything requiring a stable string (URLs, cache keys, stored snapshots) must be generated once and persisted, never regenerated on read.

Spintax::shouldReceive('parse')->once()->andReturn('Hello there');

It expands one variant, not all of them. If you need the full permutation set for a preview UI, this package won't give it to you — you'll have to write a recursive expander yourself. Budget for that early; teams routinely assume "spintax library" means "enumerate".

Nested braces are supported, but depth isn't documented. {Hello|Hi {there|friend}} works, but deeply nested or unbalanced braces ({a|{b|c}) can silently drop text or consume more than intended. If the templates are user-supplied, validate balanced braces before parsing, and cap nesting depth.

Literal braces are a real problem. Spintax has no escape character, and Blade's {{ }} collides directly with it. If your copy contains CSS, JSON, or Blade expressions, either parse before handing the string to Blade, or use @verbatim blocks and a placeholder token that you swap in afterwards.

Empty options. Check what {Yes|} produces — depending on the regex version, you may get an empty string or a stray pipe. Filter templates on save: reject || and leading/trailing pipes.

Whitespace is preserved verbatim. {Hello | Hi} yields "Hello " or " Hi" with the trailing/leading space baked in. Always write {Hello|Hi} with no padding, or trim the final result if your template formatting demands readable source.

PHP version drift. Last release was July 2022, before PHP 8.3/8.4 became common. The parser is regex-based and framework-agnostic, so it almost certainly runs fine, but if you hit deprecation notices (dynamic properties, ${var} interpolation), check composer.json constraints and consider pinning the package with a dev-master requirement if a fix lands upstream. With zero dependents listed, don't expect upstream urgency.

Extension points are minimal. There is no driver interface — if you need weighted randomness, or deterministic output by seed, you cannot configure it. Fork or wrap: subclass the parser and override the random-selection step, or pre-process weighted syntax into plain spintax by repeating options ({A|A|A|B} gives 75/25).

Multibyte content. The regex operates on bytes; accented and CJK text inside options generally survives, but if you see truncated characters, add the u modifier in your fork.

Debugging tip. When output looks wrong, dump the template with var_dump() first — most "parser bugs" turn out to be Blade eating the braces, an env var interpolating, or a . in the string being concatenated before it reaches the parser.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin