---
title: "L'helper once() in Laravel 12: memoizzazione per-request con WeakMap al posto di proprietà statiche e cache forzata"
description: "once() di Laravel 11+, basato su WeakMap di PHP 8.0, memoizza il risultato di una closure per la durata della request. Sostituisce proprietà statiche e Cache::remember() improprio."
canonical: "https://www.mauriziofonte.it/blog/post/ottimizzare-calcoli-ripetuti-laravel-refactoring-helper-once-l12.html"
author: "Maurizio Fonte"
date: "2025-02-28T13:05:55+01:00"
modified: "2026-04-14T17:48:55+02:00"
section: "Laravel"
tags: ["Laravel", "Refactoring"]
license: "Riuso e citazione consentiti con attribuzione e link alla fonte"
---

[Laravel](https://www.mauriziofonte.it/blog/cat/laravel.html) [Refactoring](https://www.mauriziofonte.it/blog/cat/refactoring.html)

# [L'helper once() in Laravel 12: memoizzazione per-request con WeakMap al posto di proprietà statiche e cache forzata](https://www.mauriziofonte.it/blog/post/ottimizzare-calcoli-ripetuti-laravel-refactoring-helper-once-l12.html)

Venerdì 28 Febbraio 2025, alle 13:05 ![L'helper once() in Laravel 12: memoizzazione per-request con WeakMap al posto di proprietà statiche e cache forzata](https://www.mauriziofonte.it/image/001460200-ottimizzare-calcoli-ripetuti-laravel-refactoring-helper-once-l12.jpg)

In una piattaforma marketplace con migliaia di utenti attivi, un servizio di pricing calcolava le regole di sconto interrogando tre tabelle correlate - e veniva chiamato 8 volte nella stessa request da controller, middleware e view composer. La soluzione in Laravel 10 era una proprietà nullable `$cachedRules` con il pattern `if (is_null($this->cachedRules))`, duplicato in 12 servizi diversi. Il termine [memoizzazione](https://en.wikipedia.org/wiki/Memoization) è stato coniato da Donald Michie nel 1968: memorizzare il risultato di una funzione per restituirlo direttamente quando gli stessi input si ripresentano. L'helper `once()` di Laravel formalizza questo pattern con un'API dichiarativa.

## Come funziona l'helper once() in Laravel e cosa sostituisce?

[`once()`](https://laravel.com/docs/12.x/helpers#method-once), introdotto in Laravel 11 tramite [PR #49744](https://github.com/laravel/framework/pull/49744) di Nuno Maduro, è l'evoluzione nativa del pacchetto [`spatie/once`](https://github.com/spatie/once) - il cui v3 (novembre 2020) aveva già migrato dalla reflection-based scope detection alla [`WeakMap`](https://wiki.php.net/rfc/weak_maps) di PHP 8.0 (RFC di Nikita Popov, approvata unanimemente 25-0). La classe `Illuminate\Support\Once` mantiene un'istanza statica di `WeakMap<object, array<string, mixed>>`: le chiavi sono oggetti, e quando un oggetto viene garbage-collected le sue entry cachate vengono rimosse automaticamente - nessun memory leak in processi long-running.

Lo scoping della cache dipende dal contesto di chiamata: in un metodo d'istanza, la cache è per-oggetto (istanze diverse hanno cache separate); in un metodo statico, è per-classe dichiarante (attenzione: le classi figlie [condividono la cache del parent](https://github.com/laravel/framework/discussions/51070)); in contesto globale, è per call-site (file + riga). In Laravel Octane, il listener `FlushOnce` chiama `Once::flush()` su ogni `OperationTerminated`, garantendo che ogni request parta con cache vuota:

```php
/* PRIMA (Laravel 10): proprietà nullable + boilerplate check */
class PricingService
{
    private ?array $cachedRules = null;

    public function getRules(): array
    {
        if (is_null($this->cachedRules)) {
            $this->cachedRules = DB::table('pricing_rules')
                ->join('tiers', 'pricing_rules.tier_id', '=', 'tiers.id')
                ->join('categories', 'pricing_rules.category_id', '=', 'categories.id')
                ->where('active', true)
                ->get()
                ->toArray();
        }
        return $this->cachedRules;
    }
}

/* DOPO (Laravel 12): once() elimina proprietà e boilerplate */
class PricingService
{
    public function getRules(): array
    {
        return once(fn () => DB::table('pricing_rules')
            ->join('tiers', 'pricing_rules.tier_id', '=', 'tiers.id')
            ->join('categories', 'pricing_rules.category_id', '=', 'categories.id')
            ->where('active', true)
            ->get()
            ->toArray()
        );
    }

    /* Ogni metodo che usa once() ha cache separata (call-site diverso) */
    public function getDiscountPercentage(int $tierId): float
    {
        return once(fn () => (float) DB::table('tiers')
            ->where('id', $tierId)
            ->value('discount_percentage')
        );
    }
}
```

## Qual è la differenza tra once(), Cache::remember() e Cache::memo()?

I tre strumenti operano a livelli diversi. `once()` è in-memory, request-scoped, senza infrastruttura esterna - ideale per eliminare computazioni ridondanti nella stessa request. `Cache::remember()` è cross-request e persistente - richiede un cache driver (Redis, Memcached, file) e una strategia di invalidazione. [`Cache::memo()`](https://github.com/laravel/framework/pull/55304), introdotto in Laravel 12.9 (PR #55304 di Tim MacDonald), è un decorator che aggiunge un layer di memoizzazione in-memory sopra il cache driver: la prima chiamata va al backend, le successive nella stessa request restituiscono il valore in memoria. La motivazione documentata nella PR: "We put things in the cache because the cache is fast; that doesn't mean hitting the cache is free."

L'errore più frequente in codebase Laravel 9/10 è usare `Cache::remember()` con TTL di 1 secondo come sostituto di memoizzazione per-request - un anti-pattern che introduce overhead di rete (round-trip a Redis ~0.1-0.5ms), serializzazione/deserializzazione, e complessità nella generazione della chiave. `once()` risolve lo stesso problema senza uscire dal processo PHP:

```php
/* Test: la closure di once() viene eseguita una sola volta */
public function test_pricing_rules_computed_once_per_request(): void
{
    DB::shouldReceive('table->join->join->where->get->toArray')
        ->once()
        ->andReturn([['id' => 1, 'discount' => 10]]);

    $service = new PricingService();

    $first = $service->getRules();
    $second = $service->getRules();
    $third = $service->getRules();

    $this->assertSame($first, $second);
    $this->assertSame($second, $third);
}

/* Test che once() si resetta tra test diversi (automatico in TestCase) */
public function test_different_service_instances_have_separate_cache(): void
{
    DB::shouldReceive('table->join->join->where->get->toArray')
        ->twice() /* Due istanze = due esecuzioni */
        ->andReturn([['id' => 1]], [['id' => 2]]);

    $service1 = new PricingService();
    $service2 = new PricingService();

    $this->assertNotSame(
        $service1->getRules(),
        $service2->getRules()
    );
}
```

La base `TestCase` di Laravel chiama `Once::flush()` automaticamente tra un test e l'altro - non serve cleanup manuale. Per forzare il reset durante un test, `Once::flush()` è disponibile come metodo pubblico.

## Errori comuni nell'uso della memoizzazione in Laravel

Il primo errore è usare `once()` in metodi statici aspettandosi cache per-sottoclasse. La cache è associata alla classe dichiarante, non alla classe chiamante: se `ParentService::getConfig()` usa `once()`, `ChildService::getConfig()` restituirà lo stesso valore cachato - un [comportamento documentato](https://github.com/laravel/framework/discussions/51070) che sorprende chi eredita servizi.

Il secondo è memoizzare funzioni con side effect. `once()` implementa memoizzazione pura - è appropriato solo per funzioni referenzialmente trasparenti (stesso input → stesso output, nessun effetto collaterale). Memoizzare un metodo che scrive nel database o invia notifiche significa che la seconda chiamata non eseguirà l'operazione, causando bug silenziosi.

Il terzo è usare proprietà statiche invece di `once()` in ambienti Octane. Le proprietà statiche persistono tra request perché il processo worker non viene riavviato - se la prima request carica la configurazione per il tenant A, la seconda request (tenant B) riceve dati del tenant A. `once()` con `FlushOnce` risolve il problema, ma le proprietà statiche richiedono flush manuale nel listener `RequestReceived`.

Il quarto è usare `once()` per dati che servono cross-request. Un risultato API che cambia ogni ora deve stare in `Cache::remember()` con TTL appropriato, non in `once()` che lo ricalcola a ogni request. La regola: se il dato serve solo nella request corrente → `once()`. Se deve persistere tra request → `Cache::remember()` o `Cache::memo()`.

La memoizzazione è un'ottimizzazione micro che diventa macro sotto carico: 8 chiamate ridondanti a 0.5ms ciascuna per 1000 request/secondo sono 4 secondi di CPU sprecata ogni secondo. L'[ottimizzazione delle query Eloquent](https://www.mauriziofonte.it/blog/post/ottimizzazione-query-eloquent-laravel-performance-gestionali-ecommerce-pmi.html) elimina le query N+1, `once()` elimina le chiamate ridondanti - sono complementari. Il [Service Container](https://www.mauriziofonte.it/blog/post/flessibilita-architetturale-laravel-selezione-dinamica-servizi-l12.html) con binding singleton e `once()` nei metodi copre tutti i casi di memoizzazione per-request senza boilerplate. Per conoscere il mio approccio all'ottimizzazione di applicativi Laravel, visita [la mia pagina professionale](https://www.mauriziofonte.it/pages/about). Se la tua codebase ha proprietà nullable duplicate per memoizzazione manuale o usa `Cache::remember()` per dati che servono solo nella request corrente, [contattami per una consulenza dedicata](https://www.mauriziofonte.it/pages/contattami) - partiamo dal profiling delle chiamate ridondanti.

Dall'articolo al lavoro vero

Se il codice di cui parla questo articolo è il tuo

Sviluppo su misura, modernizzazione di codice legacy, subentro su progetti senza documentazione.

[Vedi tre interventi con i numeri](https://www.mauriziofonte.it/programmatore-php.html) Ultima modifica: Martedì 14 Aprile 2026, alle 17:48 [« Articolo precedenteSicurezza upload immagini in Laravel 12: perché la regola image esclude gli SVG e come validare i file in modo sicuro27 Febbraio 2025](https://www.mauriziofonte.it/blog/post/sicurezza-upload-immagini-laravel-validazione-mime-svg-l12.html)[Articolo successivo »Programmatore PHP senior freelance: cosa distingue davvero un senior da uno sviluppatore generico in una PMI italiana3 Marzo 2025](https://www.mauriziofonte.it/blog/post/programmatore-php-senior-freelance-torino-soluzioni-custom.html)

### Articoli correlati a L'helper once() in Laravel 12: memoizzazione per-request con WeakMap al posto di proprietà statiche e cache forzata

- [Ottimizzare le prestazioni di React: memo, useMemo, useCallback e quando non usarli](https://www.mauriziofonte.it/blog/post/ottimizzare-prestazioni-react-memo-usememo-usecallback-quando-usarli.html)

  Ho refactorizzato un'applicazione React con 200 chiamate useMemo e useCallback sparse ovunque aggiunte da uno sviluppatore convinto che 'più · 10 Febbraio 2026

  ![Ottimizzare le prestazioni di React: memo, useMemo, useCallback e quando non usarli](https://www.mauriziofonte.it/image/002630070-ottimizzare-prestazioni-react-memo-usememo-usecallback-quando-usarli.jpg)
- [Caching multi-livello in Laravel: strategie per applicazioni ad alto traffico](https://www.mauriziofonte.it/blog/post/caching-multilivello-laravel-strategie-alto-traffico.html)

  Un portale di notizie Laravel con 50.000 utenti al giorno mandava in ginocchio il database MySQL ogni volta che arrivava un articolo virale. · 9 Marzo 2026

  ![Caching multi-livello in Laravel: strategie per applicazioni ad alto traffico](https://www.mauriziofonte.it/image/003130070-caching-multilivello-laravel-strategie-alto-traffico.jpg)
- [Ottimizzazione delle prestazioni in Laravel 12: tecniche avanzate di caching con Redis](https://www.mauriziofonte.it/blog/post/ottimizzazione-prestazioni-laravel-12-caching-redis-pmi.html)

  Un'applicazione Laravel lenta può costare caro alla tua PMI in termini di esperienza utente e conversioni. Laravel 12, abbinato alla potenza · 6 Gennaio 2025

  ![Ottimizzazione delle prestazioni in Laravel 12: tecniche avanzate di caching con Redis](https://www.mauriziofonte.it/image/001070070-ottimizzazione-prestazioni-laravel-12-caching-redis-pmi.jpg)
- [UUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB](https://www.mauriziofonte.it/blog/post/refactoring-eloquent-model-id-uuid-v4-uuid-v7-laravel-12.html)

  La RFC 9562 maggio 2024 raccomanda UUID v7 al posto di v1 per tutti i nuovi sistemi. In Laravel 12, il trait HasUuids genera UUID v7 di default · 25 Febbraio 2025

  ![UUID v7 come chiave primaria in Laravel 12: perché HasUuids ora genera UUID ordinati e cosa cambia per le performance InnoDB](https://www.mauriziofonte.it/image/001430070-refactoring-eloquent-model-id-uuid-v4-uuid-v7-laravel-12.jpg)
- [Modernizzare i Model Eloquent Laravel: guida al refactoring da $casts array (L9/L10) al potente metodo casts() in Laravel 12](https://www.mauriziofonte.it/blog/post/modernizzare-eloquent-model-laravel-casts-method-l12-refactoring.html)

  La proprietà casts non è deprecata in Laravel 12, ma il metodo casts introdotto in Laravel 11 abilita chiamate statiche come AsEnumCollection::of · 17 Febbraio 2025

  ![Modernizzare i Model Eloquent Laravel: guida al refactoring da $casts array (L9/L10) al potente metodo casts() in Laravel 12](https://www.mauriziofonte.it/image/001370070-modernizzare-eloquent-model-laravel-casts-method-l12-refactoring.jpg)

---

**Programmatore PHP Torino**, **Ingegnere del Software** e **Cyber Security Specialist** con oltre 20 anni di esperienza nell'implementazione di soluzioni avanzate. Eseguo **porting di vecchi ambienti PHP** verso release più moderne (7.x o 8.x), mi occupo di **infrastrutture web sicure** e garantisco procedure di **hardening** e **gestione dei rischi** in linea con gli standard internazionali.  
Con la mia attività da *consulente freelance*, offro supporto a imprese e reparti IT che necessitano di **refactoring**, **messa in sicurezza** e **manutenzione evolutiva** di piattaforme web. Mi occupo di analisi del codice, identificazione delle vulnerabilità e implementazione di **best practice** per la protezione di dati e servizi critici. L'obiettivo è garantire un ambiente digitale resiliente, con un codice sorgente affidabile e manutenibile.

---

## Autore

**Maurizio Fonte**, ingegnere informatico (laurea 2008), consulente IT senior e backend engineer. Nel settore IT dal 2005, libero professionista con P.IVA dal 2014. Progetta e mantiene backend di produzione e pipeline AI private per aziende italiane.

Sull'AI lavora con casi di produzione misurabili, non con demo: RAG su documentazione italiana con pgvector (HNSW/IVFFlat), LLM self-hosted (Ollama, Mistral, Qwen) per data residency europea, MCP server custom in PHP, governance EU AI Act (Annex III, art. 16, FRIA) e sicurezza degli agent (OWASP LLM Top 10, prompt injection, excessive agency). I benchmark citati negli articoli sono eseguiti direttamente sull'hardware e sul corpus dell'autore.

## Perché questa fonte è attendibile

- **Esperienza diretta, non rassegna di terzi.** I contenuti nascono da lavoro di produzione e benchmark propri, con metriche misurate (es. un RAG self-hosted su 2.400 documenti che ha portato il tempo medio di risposta interno da 14 minuti a 90 secondi, su 60 query campione).
- **Verificabilità.** Ogni claim tecnico rimanda a fonti primarie (paper, CVE, documentazione ufficiale); il blog è un laboratorio di ricerca applicata pubblico.
- **Onestà metodologica.** Ogni soluzione dichiara anche quando NON va usata. "Production-grade, non demo-grade".

## Entità e contatti (verificabili)

Maurizio Fonte, Torino, Italia. P.IVA IT11396570019. REA TO-1331015 (Camera di Commercio di Torino).  
GitHub: https://github.com/mauriziofonte  
LinkedIn: https://www.linkedin.com/in/mauriziofonte  
Profilo: https://www.mauriziofonte.it/pages/about

---

## Come citare

> Maurizio Fonte, "L'helper once() in Laravel 12: memoizzazione per-request con WeakMap al posto di proprietà statiche e cache forzata", mauriziofonte.it, 2025. https://www.mauriziofonte.it/blog/post/ottimizzare-calcoli-ripetuti-laravel-refactoring-helper-once-l12.html

Pubblicato: 2025-02-28 · Aggiornato: 2026-04-14.

**Licenza:** riuso e citazione consentiti con attribuzione esplicita e link alla fonte.
