Apparence
Les résolveurs de contexte
Un résolveur répond à une question de contexte : « quel est le panier courant ? la devise ? la zone fiscale ? le taux de taxe applicable ? ». Chacun est un contrat que vous pouvez remplacer pour changer la stratégie, sans toucher au code qui le consomme.
Le problème
Le calcul d'un prix a besoin de savoir dans quelle devise et quelle zone fiscale se place le client. Un site B2C lit ça d'un cookie et de l'adresse du panier ; un site B2B le déduira du compte entreprise. Le pricing ne doit dépendre d'aucune de ces stratégies : il se contente de poser la question.
Le principe
Le cœur demande « le X courant » à un contrat. Une implémentation par défaut (B2C) répond ; vous la remplacez pour changer la logique. C'est une application directe du conteneur & des contrats, spécialisée pour le contexte de la requête.
Les résolveurs de Slab
| Contrat | Répond à | Défaut (B2C) |
|---|---|---|
CurrentCartResolver | le panier courant (et sa transition à la connexion) | panier du compte, sinon panier invité (cookie) ; vide et en mémoire tant qu'il n'a aucun produit |
CurrentCurrencyResolver | la devise d'affichage | cookie, sinon devise du shop |
CurrentAreaResolver | la zone fiscale | zone de l'adresse de facturation, sinon zone par défaut |
TaxResolver | le taux de taxe pour une règle + une zone | taux par zone, sinon taux par défaut de la règle |
Remplacer un résolveur
php
use Slab\Framework\Core\Currency\Contracts\CurrentCurrencyResolver;
$this->app->scoped(CurrentCurrencyResolver::class, DeviseParPaysResolver::class);Votre classe implémente le contrat :
php
use Slab\Framework\Core\Currency\Contracts\CurrentCurrencyResolver;
use Slab\Framework\Models\Currency;
final class DeviseParPaysResolver implements CurrentCurrencyResolver
{
public function resolve(): Currency
{
// votre logique (géo-IP, préférence compte, sous-domaine…)
}
}Tout le cœur (prix, totaux, affichage) bascule aussitôt sur votre devise.
Le résolveur de panier
CurrentCartResolver demande plus qu'un resolve(), car le panier courant naît et change de main en cours de navigation. Votre implémentation porte trois méthodes :
php
use Slab\Framework\Core\Cart\Contracts\CurrentCartResolver;
use Slab\Framework\Models\Cart;
final class PanierParEntrepriseResolver implements CurrentCartResolver
{
public function resolve(): Cart
{
// le panier courant ; s'il n'existe pas encore, un `Cart` vide en mémoire
}
public function remember(Cart $cart): void
{
// un panier vient d'être enregistré : s'il est le panier courant, le retenir
}
public function setOnLogin(Cart $guestCart): Cart
{
// basculer vers le panier du compte, y verser le panier invité au besoin
}
}resolve(): un panier ne s'enregistre qu'avec son premier produit. Tant que le client n'a rien ajouté (visiteur de passage, robot d'indexation), vous recevez un panier vide en mémoire : on peut le lire (quantité, adresse, zone), mais il n'existe pas en base. Chaque page lit le panier (pour la quantité affichée dans l'en-tête, pour la zone fiscale des prix). Le créer dès la lecture enregistrerait un panier par visiteur.remember(): le cœur l'appelle dès qu'unCartest enregistré (écouteurRememberNewCartsureloquent.created), quelle que soit l'action qui a créé le panier : un premier produit, un premier bon de réduction… S'il s'agit du panier courant, il le reste pour les pages suivantes. Le défaut pose le cookie pour un invité ; un panier de compte se retrouve par le compte.setOnLogin(): à la connexion, le panier courant devient celui du compte. Le panier invité y est fusionné si le panier du compte est vide.
TIP
Les opérations sur un panier (ajout, mise à jour, suppression, fusion, commande) passent par CartService, qui travaille sur un panier explicite et ne sait rien du « panier courant ». Le résolveur répond seulement à la question « lequel ? ».
Bon à savoir
- Les résolveurs de contexte sont liés à la requête (
scoped) : une instance par requête, jamais de statique partagé entre process. Enregistrez les vôtres de la même façon. Avec unbind, chaque résolution crée une instance neuve : un résolveur de panier qui mémorise le panier courant sur son instance ne le reconnaîtrait plus dansremember(). - Le défaut est résolument B2C (cookies, fusion panier invité→compte). Une couche B2B (
apisell) fournit ses propres résolveurs en les enregistrant dans le conteneur, exactement par ce mécanisme. - Les noms des cookies B2C ne sont pas en dur :
shop.cookies.cart(cart_idpar défaut) etshop.cookies.currency(currency_isopar défaut). Renommez-les en config, ou via les variables d'environnementCART_COOKIE/CURRENCY_COOKIE, sans remplacer le résolveur.
Voir aussi
- Le conteneur & les contrats : le mécanisme sous-jacent.
- Les pipelines : les consommateurs principaux du contexte.
- Les points d'extension : la liste complète des contrats remplaçables.