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

String Extra Laravel Package

twig/string-extra

Twig extension integrating Symfony String, adding u, slug, singular, and plural filters for Unicode-aware text manipulation, URL-friendly slugs, and simple word inflection directly in your templates.

View on GitHub
Deep Wiki
Context7

Getting Started

Installation

composer require twig/string-extra

You almost certainly already have symfony/string — it is a transitive dependency of symfony/console, which Laravel ships with. Verify before adding it explicitly:

composer show symfony/string

Do not pin a fresh ^6 or ^7 unless you know your Laravel version’s transitive constraint; let the lock file resolve it.

Where to Look First

  1. The README of this package — it documents only four filters: u, slug, singular, plural. That is the entire public surface. If you are looking for truncate, sanitize, or titlecase, they are not here — those come from UnicodeString methods accessed via the u filter.
  2. The Symfony String docs — this is where 90% of the real value lives. The u filter is your entry point into UnicodeString, and its method list (truncate, title, camel, snake, folded, trimSuffix, replace, …) is the actual API you will use day-to-day.
  3. The extension source file (src/StringExtension.php) — worth reading once. It is ~80 lines and tells you the exact argument order for each filter, which the README does not.

First Use Case

If your Laravel app uses Twig (via twigbridge / laravel-twigbridge), register the extension and use it immediately:

// config/twigbridge.php or config/twig.php
'extensions' => [
    \Twig\Extra\String\StringExtension::class,
],
<a href="/blog/{{ post.title|slug }}">{{ post.title }}</a>
{# "Laravel 11 & You!" → /blog/laravel-11-you #}

If you are on Blade (the default and most common case), you will not use the extension’s Twig filters at all — you will call Symfony\Component\String\UnicodeString directly through a Blade directive or a Str macro. That is the realistic Laravel starting point, and it is covered in the next section.


Implementation Patterns

Pattern 1 — Blade directive (Blade-only apps)

Register once in a service provider (AppServiceProvider::boot() or a dedicated TemplatingServiceProvider):

use Illuminate\Support\Facades\Blade;
use Symfony\Component\String\UnicodeString;

Blade::directive('slug', function ($expression) {
    return "<?php echo \\Symfony\\Component\\String\\UnicodeString::from({$expression})->slug(); ?>";
});
<a href="/blog/{{ slug($post->title) }}">{{ $post->title }}</a>

This is the lowest-friction way to give Blade parity with the Twig slug filter. Do the same for singular, plural, and any u-chained method you use regularly.

Pattern 2 — Str macro (Blade + PHP consistency)

If your team already writes Str::of() in controllers, register matching macros so template and backend code look the same:

use Illuminate\Support\Str;

Str::macro('plural', fn ($value, $locale = 'en') => (string) \Symfony\Component\String\UnicodeString::from($value)->pluralize($locale));
Str::macro('singular', fn ($value, $locale = 'en') => (string) \Symfony\Component\String\UnicodeString::from($value)->singularize($locale));
{{ Str::plural($model->type) }}   {{-- "categories" --}}

Advantage: works in both Blade and PHP with no directive parsing quirks.

Pattern 3 — Native Twig filters (Twig-enabled apps)

Once StringExtension is registered, all four filters are available. The high-value use is chaining on u, because UnicodeString has no Twig-native equivalent:

{{ article.body|u.truncate(140, '…') }}
{{ user.name|u.title }}
{{ input|u.folded }}            {# accent-insensitive comparison #}
{{ 'foo_bar'|u.camel }}          {# "fooBar" #}

Note the ergonomics: u returns a UnicodeString, Twig then dispatches the trailing method and auto-casts to string on output.

Pattern 4 — Locale-aware slugs and labels

{{ page.title|slug('-', app.request.locale) }}
{{ count ~ ' ' ~ noun|plural(app.request.locale) }}

The slug filter wraps AsciiSlugger::slug(), so it shares Laravel’s Str::slug() behaviour for ASCII, but gives you per-call locale, which Laravel’s helper does not expose cleanly. This is the single strongest reason to prefer the filter over Str::slug() in a multilingual site.

Pattern 5 — Keep slugging in ONE place

Pick a side: either the model writes a slug column on save, or the template derives it on render. Do not do both. Template-side slugging is appropriate for transient URLs (preview links, share URLs on unsaved records); persistent, indexed URLs should be generated on save in PHP so they can be unique-constrained and changed deliberately.

Pattern 6 — Wrap for testability

Do not scatter UnicodeString::from(...) calls across dozens of directives. Create a small helper class (App\Support\Text) with static methods (Text::slug(), Text::initials()), and have Blade directives / Str macros / Twig extensions delegate to it. This is the seam you test.


Gotchas and Tips

  • The package does not give you Blade filters. There is no {{ $x | slug }} in Blade. You are always bridging via a directive, macro, or helper. Plan for a TemplatingServiceProvider rather than one-off Blade::directive calls sprinkled around.

  • u returns an object, not a string. If you pipe it into something expecting a string before the terminal method, Twig may attempt __toString() at the wrong time. Chain only UnicodeString methods after u — ByteString methods like slice() from CodePointString differ subtly from AbstractUnicodeString::slice().

  • UnicodeString::from(null) throws. Real data hits null. Guard at the call site:

    Blade::directive('slug', fn ($e) => "<?php echo \\Symfony\\Component\\String\\UnicodeString::from((string) {$e})->slug(); ?>");
    

    Note the explicit (string) cast — it makes the directive null-safe.

  • Blade::directive receives the raw expression, including quotes. slug($post->title) comes in as '$post->title'. {$expression} interpolates it back into the compiled PHP. If the expression itself contains <?php, compilation breaks; keep directive arguments simple variables/accessors rather than complex expressions.

  • View caching hides directive bugs. After registering a new directive, run php artisan view:clear before judging whether it worked. A stale compiled view is the #1 false alarm.

  • slug() separator and locale argument order is slug(string $string, string $separator = '-', ?string $locale = null). Passing a locale where you meant a separator (|slug('de')) silently produces de-separated slugs instead of a locale-specific slug. Double-check signatures in the source.

  • AsciiSlugger customisation is not exposed by this extension. If you need custom transliteration rules (e.g., currency symbols, CJK fallbacks), you must write your own StringExtension subclass or a separate service that injects a configured AsciiSlugger. Do not expect the filter to accept a slugger instance.

  • plural() on already-plural irregulars can be surprising. UnicodeString::pluralize() follows the Inflector rules; "data" → "datas" and "sheep" → "sheeps" happen. For user-facing copy, prefer explicit translation strings over inflection, or test with your actual vocabulary.

  • Non-ASCII and RTL. Test explicitly with Café, Ærøskøbing, 東京, and Arabic input. Symfony handles these well, but Blade directive outputs need to be wrapped in {{ }} (auto-escaped) — the directive itself must not escape, or you get double-encoding.

  • Watch for duplicate work in loops. @foreach ($posts as $post) {{ slug($post->title) }} runs the slugger N times per request. If your pages are list-heavy, precompute slugs into a view model or @php block. The slugger caches internally but the UnicodeString allocation does not.

  • Version alignment. twig/string-extra (v3.x) pairs with Twig 3 and symfony/string v6+. If your Laravel version still resolves symfony/string ^5.4, you will get Twig filter registration errors at boot. Run composer why symfony/string once, and keep twig/string-extra on a matching minor.

  • Extension points to remember: (a) register your own Twig\Extension\AbstractExtension subclasses alongside this one if you need extra filters; (b) Str::macro for a PHP-first API; (c) Blade::directive for zero-dependency template sugar. Pick the one that matches where your team already lives.

  • The u filter is the real workhorse, not slug. Most teams install this package expecting slug, then discover the value is in {{ x|u.title }}, {{ x|u.truncate(80) }}, and {{ x|u.camel }} — because those are the operations that previously leaked custom helpers or JS into templates. Document the u chaining pattern for the team, or the package looks thinner than it is.

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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin