Search by

plin-code / laravel-name-fixer

danielebarbaro

Clean, normalize and properly case personal names before validation in Laravel

Package info

github.com/plin-code/laravel-name-fixer

pkg:composer/plin-code/laravel-name-fixer

Fund package maintenance!

Plin Code

Statistics

Installs: 13

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-09-24 10:12 UTC

This package is not auto-updated.

Last update: 2026-09-24 19:48:27 UTC


README

Laravel Name Fixer

Laravel Name Fixer

Packagist PHP from Packagist Laravel versions GitHub Workflow Status (main) Total Downloads

Clean, normalize and properly case personal names before validation in Laravel. Handles the mess that comes from web forms, CSV imports and legacy systems, including Italian quirks like NICOLO' stored instead of Nicolò.

The Problem

Str::title() was built for titles, not for people. It turns D'ANGELO into D'angelo, keeps invisible characters pasted from a spreadsheet, and happily title cases de Rossi into De Rossi even when the user typed it that way on purpose.

Laravel Name Fixer repairs names once, before they reach your database, and leaves alone what it cannot know for sure.

What It Fixes

Input Output Fixer
Rossi\u{200B}, Mario\u{00A0}Rossi Rossi, Mario Rossi StripInvisible
D’Angelo, Dall`Oglio D'Angelo, Dall'Oglio NormalizeApostrophe
Rossi., Rossi, Rossi StripTrailingPunctuation
Mario Rossi Mario Rossi SquishWhitespace
Maria - Luisa, Maria – Luisa Maria-Luisa HyphenSpacing
LUCA D'ANGELO, sant'elia Luca D'Angelo, Sant'Elia TitleCase
NICOLO' CANTU' Nicolò Cantù AccentFromApostrophe (locale: it)
de Rossi de Rossi left alone (mixed case)

What It Does Not Do

Italian surnames are registered exactly as written: De Rossi and de Rossi are both legal, and so are Lo Presti and Lopresti. From DE ROSSI in capitals there is no way to tell which one is right. The package title cases it to De Rossi and, by default, never touches a name that already comes in mixed case. If the exact spelling matters, let users correct the field.

Installation

composer require plin-code/laravel-name-fixer

Optionally publish the config file:

php artisan vendor:publish --tag="name-fixer-config"

Quick Start

Using the Facade

use PlinCode\LaravelNameFixer\Facades\NameFixer;

NameFixer::fix("  LUCA D’ANGELO. ");
// "Luca D'Angelo"

// Get a detailed report
$report = NameFixer::diagnose('MARIO ROSSI');
// $report->original       → "MARIO ROSSI"
// $report->fixed          → "Mario Rossi"
// $report->appliedFixers  → ["title_case"]
// $report->wasModified    → true

// Batch processing
$reports = NameFixer::fixMany(['MARIO ROSSI', "d'angelo"]);

Using the String Macro

A drop in replacement for Str::title() when the string is a person's name.

use Illuminate\Support\Str;

Str::fixName("luca d'angelo");          // "Luca D'Angelo"
Str::of(' MARIO ROSSI ')->fixName();    // "Mario Rossi"

Using the Cast

Fix names every time they are written to a model.

use PlinCode\LaravelNameFixer\Casts\FixedName;

class Customer extends Model
{
    protected function casts(): array
    {
        return [
            'first_name' => FixedName::class,
            'last_name' => FixedName::class,
        ];
    }
}

Using the Middleware

Register the SanitizeNames middleware to fix name fields in incoming requests before they reach validation.

use PlinCode\LaravelNameFixer\Middleware\SanitizeNames;

Route::middleware(SanitizeNames::class)->group(function () {
    Route::post('/register', [RegisterController::class, 'store']);
});

By default the middleware targets first_name, last_name, surname, nome, cognome, *_first_name and *_last_name. A plain name field is left out on purpose, because it often holds a company or product name. You can change the list in the config file.

Using the Validation Rule

ValidName accepts letters (accented ones included) joined by single spaces, apostrophes or hyphens. It rejects digits, emoji, symbols and doubled separators. It only validates: combine it with the middleware or the cast to also fix the value.

use PlinCode\LaravelNameFixer\Rules\ValidName;

public function rules(): array
{
    return [
        'first_name' => ['required', 'string', 'max:100', new ValidName],
    ];
}

The message is The :attribute field must be a valid name. and can be translated through your JSON language files.

Use Cases

Legacy Italian Data

Old management systems and tax code records store names in uppercase ASCII: NICOLO', FORLI', CANTU'. Enable the Italian locale to turn the trailing apostrophe back into an accent.

// Via config (config/name-fixer.php)
'locale' => 'it',

// Or at runtime
NameFixer::locale('it')->fix("NICOLO' CANTU'");
// "Nicolò Cantù"

Only a vowel at the end of a word, preceded by at least two letters, is converted. Elisions like D'Angelo and Dell'Acqua and short particles like de' Medici stay as they are.

A trailing E' becomes è (Mosè, Noè, Giosuè). Switch to é with the locale options:

'locale_options' => ['accentedE' => 'é'],

Cleaning Existing Records

The name-fixer:scan command finds badly formatted names already in your database. It only lists the changes unless you pass --fix.

php artisan name-fixer:scan "App\Models\Customer" --column=first_name --column=last_name --locale=it

php artisan name-fixer:scan "App\Models\Customer" --column=first_name --column=last_name --locale=it --fix

Rows are read in chunks (--chunk=500 by default) and updated with the query builder, so model events, casts and updated_at are not touched. Run it on a backup or a staging copy first.

CSV Imports

$reports = NameFixer::fixMany(array_column($rows, 'last_name'));

foreach ($reports as $index => $report) {
    $rows[$index]['last_name'] = $report->fixed;
}

Configuration

return [
    // Locale preset: 'it' or null
    'locale' => null,

    // Named arguments for the locale preset
    'locale_options' => [],

    // Leave mixed case names ("de Rossi") untouched
    'lazy' => true,

    // Middleware field patterns
    'middleware' => [
        'fields' => ['first_name', 'last_name', 'surname', 'nome', 'cognome', '*_first_name', '*_last_name'],
    ],
];

Custom Fixers

Build your own pipeline with any class that implements PlinCode\LaravelNameFixer\Contracts\FixerInterface:

use PlinCode\LaravelNameFixer\Contracts\FixerInterface;
use PlinCode\LaravelNameFixer\Fixers\SquishWhitespace;
use PlinCode\LaravelNameFixer\Fixers\TitleCase;
use PlinCode\LaravelNameFixer\NameFixer;

class StripHonorifics implements FixerInterface
{
    public function fix(string $name): string
    {
        return preg_replace('/^(dott|sig|ing)\.?\s+/iu', '', $name);
    }

    public function name(): string
    {
        return 'strip_honorifics';
    }
}

$fixer = new NameFixer([
    new SquishWhitespace,
    new StripHonorifics,
    new TitleCase,
]);

Standalone Usage

The fixers and the pipeline do not depend on Laravel:

use PlinCode\LaravelNameFixer\NameFixer;

NameFixer::defaults()->locale('it')->fix("NICOLO' ROSSI");
// "Nicolò Rossi"

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.