Laravel 12.50 : hasMany(), listeners uniques et getters typés pour le Cache
Laravel 12.50 enrichit le framework avec une nouvelle méthode hasMany() sur les collections, le support des listeners uniques en queue, des getters typés sur la façade Cache et la possibilité d’exclure les attributs appendés d’un modèle Eloquent. Cette version, sortie le 4 février 2026, améliore la sécurité de typage et la productivité au quotidien.
Contexte de cette release
Laravel 12.50.0 s’inscrit dans la continuité de la v12.49 qui avait introduit hasSole() sur les collections. Cette fois, l’équipe complète la palette avec hasMany(), son pendant logique. Le reste de la release se concentre sur deux axes récurrents depuis Laravel 12.45 : le support étendu des enums PHP natifs à travers le framework et l’amélioration du typage statique pour renforcer la confiance du code.
Au total, cette version contient plus de 40 pull requests fusionnées, dont plusieurs correctifs importants pour Octane, le validateur et les tests parallèles.
La méthode hasMany() sur les collections
Le cas est fréquent : vérifier qu’une collection contient plusieurs éléments correspondant à un critère avant de lancer un traitement. Jusqu’ici, il fallait combiner filter() et count(). La méthode hasMany() résout ce besoin en une seule ligne.
use Illuminate\Support\Collection;
$commandes = collect([
['statut' => 'en_attente', 'montant' => 50],
['statut' => 'en_attente', 'montant' => 120],
['statut' => 'expédiée', 'montant' => 30],
]);
// Vérifier s'il y a plusieurs commandes en attente
$commandes->hasMany('statut', 'en_attente'); // true
// Avec un opérateur de comparaison
$commandes->hasMany('montant', '>=', 100); // false (une seule >= 100)
// Avec un callback personnalisé
$commandes->hasMany(fn ($c) => $c['montant'] > 40); // true
// Sans argument : vérifie simplement que la collection a plus d'un élément
$commandes->hasMany(); // true (3 éléments)
Cette méthode est le miroir de hasSole() (v12.49). Là où hasSole() confirme l’existence d’exactement un résultat, hasMany() confirme qu’il y en a au moins deux. Le cas d’usage typique : un workflow conditionnel qui ne doit s’exécuter que lorsque plusieurs éléments correspondent.
PR : #58550
Listeners uniques en queue (ShouldBeUnique)
Les listeners dispatchés en queue peuvent désormais implémenter le contrat ShouldBeUnique, exactement comme les jobs. Le scénario classique : un événement OrderPlaced est émis plusieurs fois en rafale, mais le listener qui génère la facture ne doit s’exécuter qu’une seule fois par commande.
namespace App\Listeners;
use App\Events\OrderPlaced;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
class GenerateInvoice implements ShouldQueue, ShouldBeUnique
{
// Identifiant unique basé sur l'événement reçu
public function uniqueId(OrderPlaced $event): string
{
return (string) $event->order->id;
}
public function handle(OrderPlaced $event): void
{
// Génération de la facture...
// Ce listener ne sera jamais dupliqué dans la queue
// pour la même commande
}
}
La différence avec le middleware WithoutOverlapping est fondamentale : WithoutOverlapping empêche l’exécution concurrente (le job entre quand même dans la queue). ShouldBeUnique empêche le job d’entrer dans la queue s’il y est déjà. Le contrat ShouldBeUniqueUntilProcessing est également supporté pour libérer le verrou dès le début du traitement.
PR : #58402
Getters typés sur la façade Cache
Récupérer une valeur du cache impliquait souvent un cast manuel ou un @var en commentaire pour satisfaire l’analyse statique. Laravel 12.50 ajoute des méthodes typées directement sur la façade Cache.
use Illuminate\Support\Facades\Cache;
// Récupérer un entier avec valeur par défaut
$nbVues = Cache::integer('article:42:vues', 0);
// Récupérer une chaîne
$nomUtilisateur = Cache::string('session:nom', 'Invité');
// Récupérer un booléen
$maintenanceActive = Cache::boolean('maintenance', false);
// Récupérer un float
$tauxConversion = Cache::float('taux_eur_usd', 1.0);
// Récupérer un tableau
$panier = Cache::array('panier:user:7', []);
Chaque méthode applique un cast strict au retour. Si la valeur en cache n’est pas du type attendu, elle est convertie selon les règles de PHP (intval, strval, etc.). Le bénéfice principal : votre IDE et PHPStan connaissent le type exact du retour sans annotation supplémentaire.
PR : #58451
withoutAppends() sur les modèles Eloquent
Lorsqu’un modèle déclare des accesseurs appendés (via $appends), ceux-ci sont systématiquement inclus lors de la sérialisation. Certains de ces accesseurs peuvent être coûteux en calcul ou en requêtes. La méthode withoutAppends() permet de les exclure ponctuellement.
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
class User extends Model
{
protected $appends = ['full_name', 'avatar_url', 'stats'];
protected function fullName(): Attribute
{
return Attribute::get(fn () => $this->first_name . ' ' . $this->last_name);
}
// avatar_url et stats : accesseurs potentiellement coûteux
}
// Dans un contrôleur API : exclure les attributs coûteux
$users = User::all();
// Retirer tous les appends
$users->each->withoutAppends();
// Ou retirer uniquement certains appends
$user->withoutAppends(['stats', 'avatar_url']);
return response()->json($users);
Cette approche est plus légère que de créer une API Resource dédiée lorsque le seul besoin est de masquer quelques attributs calculés.
PR : #58552
Autres ajouts notables
Plusieurs améliorations complémentaires méritent d’être signalées :
Uri::authority(): extraction du composant authority (user:pass@host:port) depuis un objetUri— PR #58534- Enums comme clés de cache en tableau :
Cache::get([MonEnum::CleA, MonEnum::CleB])fonctionne nativement — PR #58616 - Résolution correcte de la queue factory sous Octane : correction d’un bug où le queue manager n’était pas correctement résolu entre les requêtes — PR #58530
- Nettoyage du cache de vues en tests parallèles : les répertoires de cache des vues compilées sont maintenant correctement nettoyés — PR #58525
- Morphmap lors de la sérialisation des identifiants de modèles : le morph map est désormais respecté lors de la sérialisation des model identifiers pour les jobs en queue — PR #58482
Points d’attention
- La méthode
hasMany()retournetruedès que deux ou plus éléments correspondent. Ce n’est pas un compteur : pour connaître le nombre exact, utilisezfilter()->count(). - Les typed getters du Cache (
integer(),string(), etc.) effectuent un cast. Si la valeur stockée est un objet ou une structure complexe, le résultat peut ne pas être celui attendu. Réservez-les aux valeurs scalaires. - L’unicité des listeners en queue repose sur le cache (comme pour les jobs). Vérifiez que votre driver de cache supporte les locks atomiques (Redis, database, etc.).
withoutAppends()est une opération mutable sur l’instance du modèle. Si vous la passez ensuite à un autre service, les appends resteront exclus.
Mise à jour
composer update laravel/framework
Vérifiez que votre version se met bien à jour vers ^12.50.0 dans votre composer.lock.
Résumé
Laravel 12.50 apporte des outils concrets pour écrire du code plus expressif et plus sûr. hasMany() et withoutAppends() réduisent le code boilerplate, les getters typés du Cache renforcent la confiance statique, et les listeners uniques en queue comblent un manque réel en production. Des ajouts ciblés, sans breaking change, qui s’intègrent immédiatement dans les projets existants.
Ressources
À propos de Laravel Actu
Développeur passionné par Laravel et son écosystème.
Voir tous les articles