adamczykpiotr/laravel-ultimate-formatter
A Laravel formatting package that bundles common presentation helpers into one toolkit, so you can consistently format values such as dates, numbers, and strings across your application. Useful for standardizing output in views and APIs with less repetitive code.
The package gives Laravel a single, config-driven entry point for turning raw values (phone numbers, currency, names, dates, identifiers) into display- or storage-ready strings.
composer require adamczykpiotr/laravel-ultimate-formatter
php artisan vendor:publish --tag=ultimate-formatter-config
config/ultimate-formatter.php first — this is the source of truth for defaults and, as of v1.1.0, the new pre_steps array. Read it before reading the README; the config documents the intended order of operations better than prose does.src/Steps or src/Formatters) to see which transformations ship out of the box.// app/Http/Controllers/InvoiceController.php
return view('invoice.show', [
'total' => formatter()->format($invoice->total, 'currency'),
'phone' => formatter()->format($customer->phone, 'phone'),
]);
If formatter() isn't your style, resolve the class from the container (app(Formatter::class)) or use the facade — both are wired to the same config, so behaviour stays consistent.
Config-first pre-steps (the v1.1.0 headline). Register global normalisation that should run before every format call:
// config/ultimate-formatter.php
'pre_steps' => [
\App\Support\Formatting\TrimWhitespace::class,
\App\Support\Formatting\StripInvisibleCharacters::class,
],
This is the right home for cross-cutting cleanup (trimming, stripping zero-width spaces, collapsing unicode spaces, casting numerics out of strings). Keep the list short and deterministic — it runs on every call.
Per-call pre-steps. For one-off cases, pass steps at the call site so the global config stays clean:
formatter()
->withPreSteps([NormaliseCountryCode::class])
->format($request->input('phone'), 'phone');
Prefer this over adding a step to config "just for one form" — global steps are effectively a hidden dependency of every formatted value in the app.
Form Requests and validation boundaries. Normalise before validation, not after, so your rules see canonical input:
protected function prepareForValidation(): void
{
$this->merge([
'phone' => formatter()->withPreSteps([StripFormatting::class])->format($this->phone, 'raw'),
]);
}
Custom steps as small, single-purpose classes. Each step should do one thing, accept a value, and return a value. Compose several rather than writing one CleanUpEverything step — it keeps ordering debuggable and lets you reuse steps across formats.
API resources / Blade. Format at the presentation boundary. Formatting inside models or accessors couples storage and display and makes it awkward when the same attribute needs two different renderings (table vs. PDF vs. JSON export).
// app/Http/Resources/CustomerResource.php
'phone' => formatter()->format($this->phone, 'phone'),
'balance' => formatter()->format($this->balance, 'currency'),
Testing. Assert on formatted output, not on intermediate step behaviour, and add one test per configured global pre-step that proves it is actually reachable. Config changes are silent otherwise.
TrimWhitespace before CollapseSpaces behaves differently than the reverse; document the intended order in a comment above the array.null passing through a chain of steps is the usual source of surprise. Confirm whether steps are skipped for null/'' and make your own steps null-safe regardless.pre_steps referencing class names means php artisan config:cache is fine, but renaming a step class without clearing the cache yields confusing container errors. Clear config cache after refactors.try/catch (or a validation rule) if the format is not guaranteed.withPreSteps([]) first to isolate whether the global list or the format definition is responsible. Adding a temporary ray()/logger() inside a step is faster than reasoning about it.pre_steps is new in 1.1.0; on projects pinned to 1.0.x the key is silently ignored. If you're upgrading, add the config key in the same PR as the code that relies on it, and check that published config files haven't drifted from the package default.pre_steps contents, read config/ultimate-formatter.php and the step base class directly.How can I help you explore Laravel packages today?