Reader Stacks

Custom Helper Functions in Laravel 11 and 12

How to register global helper functions in Laravel 11 and 12, including the composer.json autoload change from older versions.

Custom Helper Functions in Laravel 11 and 12

Laravel doesn't ship a dedicated helpers file, but adding your own global functions — a currency formatter, a slug generator, a shortcut you call in dozens of Blade views — is a common and supported pattern. The setup is the same in Laravel 11 and 12; only the surrounding app-structure files have changed from older tutorials, so version mismatches are the most common source of confusion here.

1. Create the helpers file

Create app/helpers.php (some projects use app/Support/helpers.php — either works, as long as the path below matches):

<?php

if (! function_exists('money')) {
    function money(int $cents, string $currency = 'USD'): string
    {
        return number_format($cents / 100, 2).' '.$currency;
    }
}

The function_exists guard matters more than it looks: without it, running the same helper file twice in a test suite or a package that also defines money() throws a fatal "cannot redeclare function" error.

2. Autoload it via composer.json

Add the file to the autoload.files array in composer.json:

"autoload": {
    "files": [
        "app/helpers.php"
    ],
    "psr-4": {
        "App\\": "app/"
    }
}

Then regenerate the autoloader:

composer dump-autoload

What changed from Laravel 8/9 tutorials

Older guides sometimes have you load the helper file from AppServiceProvider::register() with a manual require. That still works, but it's unnecessary — the Composer autoload.files approach is simpler, doesn't run on every service provider boot, and is what Laravel's own first-party packages use internally.

Using it

Once autoloaded, money() is available anywhere — controllers, Blade templates, Artisan commands, without an import:

{{ money($order->total_cents) }}

Common mistake

If the function isn't found after adding it, the fix is usually composer dump-autoload. The generated Composer autoloader has to know that the new file belongs in autoload.files; changing APP_DEBUG does not change that requirement.

Global helpers need globally unique names

The function_exists() guard prevents a fatal redeclaration, but it can also hide a naming collision: if a dependency already defines money(), your implementation is silently skipped and callers receive the other function. For application-specific helpers, a distinctive name or a namespaced class is safer than a very generic global function name.

Do not put stateful dependencies inside a helper function

A helper is strongest when its inputs are visible in its parameters. Once a function reaches into the service container, database, authenticated user, or remote API, it becomes harder to test and its cost becomes invisible at each call site. At that point an injectable service is the clearer abstraction even if the call syntax is slightly longer.

Formatting money has edge cases beyond number_format()

The example deliberately demonstrates registration, not a complete international money formatter. Dividing integer cents is a good way to avoid storing binary floating-point currency values, but real currencies do not all share the same number of minor units and locale-specific grouping/decimal rules vary. If the helper becomes user-facing international formatting, move those rules into a dedicated formatter rather than extending one global function with a growing list of exceptions.

Autoload changes belong in deployment steps

Because composer.json changed, the generated autoloader needs to be rebuilt anywhere the new release is installed. Normal Composer install/deploy workflows do this for you; copying only app/helpers.php onto an already-built production release does not. That distinction explains why a helper can work on one machine and be "undefined" on another even though the PHP file itself is present.

Topics: Developer Productivity