This commit is contained in:
gcch 2026-04-03 15:24:58 +02:00
commit d3d6dec4f0
22 changed files with 326 additions and 324 deletions

View file

@ -22,7 +22,7 @@ $templates = ['accueil.twig'];
*
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
*/
function load_resources(): void {
function load_page_resources(): void {
Resource::enqueue_style_file(
handle: 'haiku-atelier-2024-styles-page-accueil',
path: '/assets/css/pages/page-accueil.css',
@ -33,7 +33,7 @@ function load_resources(): void {
);
}
add_action('wp_enqueue_scripts', load_resources(...));
add_action('wp_enqueue_scripts', load_page_resources(...));
Timber::render(
data: $context,

View file

@ -34,7 +34,7 @@ $context['image_dimensions'] = $image_dimensions;
*
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
*/
function load_resources(): void {
function load_page_resources(): void {
Resource::enqueue_style_file(
handle: 'haiku-atelier-2024-styles-page-a-propos',
path: '/assets/css/pages/page-a-propos.css',
@ -45,7 +45,7 @@ function load_resources(): void {
);
}
add_action('wp_enqueue_scripts', load_resources(...));
add_action('wp_enqueue_scripts', load_page_resources(...));
Timber::render(
data: $context,

View file

@ -9,127 +9,47 @@ declare(strict_types=1);
namespace HaikuAtelier;
use Exception;
use HaikuAtelier\Data\Cart;
use HaikuAtelier\WP\Resource;
use Illuminate\Support\Number;
use Timber\Timber;
use WC_Shipping_Rate;
use function add_action;
use function collect;
use function Crell\fp\pipe;
use function filemtime;
use function genere_balise_img_multiformats;
use function get_template_directory;
use function get_template_directory_uri;
use function is_bool;
use function recupere_et_formate_attributs_produit;
use function WC;
use function wp_enqueue_script_module;
use function wp_enqueue_style;
// Importe la fonction pour récupérer les informations affichées des Produits dans le Panier
require_once __DIR__ . '/src/inc/TraitementInformations.php';
// Contexte et modèles
$contexte = Timber::context();
$modeles = ['panier.twig'];
$allowed_countries = [
'AD',
'AL',
'AM',
'AR',
'AT',
'AU',
'BA',
'BE',
'BG',
'BR',
'CA',
'CH',
'CL',
'CR',
'CU',
'CY',
'CZ',
'DE',
'DK',
'DZ',
'EE',
'EG',
'ES',
'FI',
'FR',
'GF',
'GP',
'GR',
'HR',
'HU',
'IE',
'IS',
'IT',
'JP',
'KR',
'LB',
'LI',
'LT',
'LU',
'LV',
'MA',
'MD',
'ME',
'MF',
'MQ',
'MT',
'MX',
'NC',
'NL',
'NO',
'NZ',
'PF',
'PL',
'PM',
'PS',
'PT',
'RE',
'RO',
'SE',
'SI',
'SK',
'SM',
'TN',
'TR',
'TW',
'US',
'YT',
'ZA',
];
$context = Timber::context();
$templates = ['panier.twig'];
// Récupère les informations affichés des Produits du Panier
$cart = [];
/** Le sous-total de la Commande dans le Panier. */
$cart_subtotal = WC()->cart->get_subtotal();
/** @var array<string,int> */
$cart_totals = WC()->cart->get_totals();
/** @var string|null $promo_code Le code promo appliqué au Panier s'il existe. */
$promo_code = collect(WC()->cart->get_applied_coupons())->first();
/** @var array<string,int> */
$cart_totals = WC()->cart->get_totals();
/** Le sous-total de la Commande dans le Panier. */
$cart_subtotal = Cart::parse_cart_value($cart_totals['subtotal'] ?? 0);
/** @var string $cart_subtotal_with_discount Le total du montant de la Réduction appliquée au Panier. */
$cart_subtotal_with_discount = $cart_totals['discount_total']
|> (static fn(int $number) => Number::format($number, maxPrecision: 2))
|> (static fn(false|string $number) => is_bool($number) ? '0' : $number);
/** @var float $cart_total Le total de la Commande dans le Panier. */
$cart_total = $cart_totals['total']
|> (static fn(int $number) => Number::format($number, maxPrecision: 2))
|> (static fn(false|string $number) => is_bool($number) ? '0' : $number)
|> (static fn(string $number) => (float) $number);
/** @var string $shipping_subtotal Le sous-total de la livraison. */
$shipping_subtotal = $cart_totals['shipping_total']
|> (static fn(int $number) => Number::format($number, precision: 0))
|> (static fn(false|string $number) => is_bool($number) ? '0' : $number);
/** Le total du montant de la Réduction appliquée au Panier. */
$cart_subtotal_with_discount = Cart::parse_cart_value($cart_totals['discount_total'] ?? 0);
/** Le total de la Commande dans le Panier. */
$cart_total = Cart::parse_cart_value($cart_totals['total'] ?? 0);
/** Le sous-total de la livraison. */
$shipping_subtotal = Cart::parse_cart_value($cart_totals['shipping_total'] ?? 0);
// TODO: Nettoyer ça.
foreach (WC()->cart->get_cart() as $cle_panier => $article_panier) {
$cart[$cle_panier] = [
'attributs' => $article_panier['data']?->get_type() === 'variation'
@ -154,35 +74,37 @@ $email = WC()->customer->get_billing_email();
$adresse_livraison = WC()->customer->get_shipping();
$adresse_facturation = WC()->customer->get_billing();
$adresse_renseignee = $adresse_livraison['city'] !== '';
$allowed_countries = collect(WC()->countries->get_countries())->only($allowed_countries)->toArray();
// TODO: Déplacer ça dans une fonction statique de Cart.
$allowed_countries = collect(WC()->countries->get_countries())->only(Cart::get_allowed_countries())->toArray();
// TODO: Nettoyer ça.
$methodes_livraison = collect(WC()->session->get('shipping_for_package_0')['rates'])
->values()
->map(static fn(WC_Shipping_Rate $methode): array => [
'id' => $methode->get_method_id(),
'prix' => Number::format((int) $methode->get_cost(), maxPrecision: 2),
'prix' => Number::format((int) $methode->get_cost(), precision: 2),
'selectionnee' => collect(WC()->session->get('chosen_shipping_methods'))->first() === $methode->get_id(),
'titre' => $methode->get_label(),
]);
$contexte['email'] = $email;
$contexte['adresse_livraison'] = $adresse_livraison;
$contexte['adresse_facturation'] = $adresse_facturation;
$contexte['adresse_renseignee'] = $adresse_renseignee;
$contexte['sous_total_panier'] = $cart_subtotal;
$contexte['code_promo'] = $promo_code;
$contexte['sous_total_reduction'] = $cart_subtotal_with_discount;
$contexte['total_panier'] = $cart_total;
$contexte['produits_panier'] = $cart;
$contexte['pays_livraison'] = $allowed_countries;
$contexte['sous_total_livraison'] = $shipping_subtotal;
$contexte['methodes_livraison'] = $methodes_livraison;
$context['email'] = $email;
$context['adresse_livraison'] = $adresse_livraison;
$context['adresse_facturation'] = $adresse_facturation;
$context['adresse_renseignee'] = $adresse_renseignee;
$context['sous_total_panier'] = $cart_subtotal;
$context['code_promo'] = $promo_code;
$context['sous_total_reduction'] = $cart_subtotal_with_discount;
$context['total_panier'] = $cart_total;
$context['produits_panier'] = $cart;
$context['pays_livraison'] = $allowed_countries;
$context['sous_total_livraison'] = $shipping_subtotal;
$context['methodes_livraison'] = $methodes_livraison;
/**
* Charge les scripts et styles de la page.
*
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
*/
function load_resources(): void {
function load_page_resources(): void {
Resource::enqueue_style_file(
handle: 'haiku-atelier-2024-styles-page-panier',
path: '/assets/css/pages/page-panier.css',
@ -193,10 +115,10 @@ function load_resources(): void {
);
}
add_action('wp_enqueue_scripts', load_resources(...));
add_action('wp_enqueue_scripts', load_page_resources(...));
// Rendu
Timber::render(
filenames: $modeles,
data: $contexte,
filenames: $templates,
data: $context,
);

View file

@ -1,17 +1,23 @@
<?php
declare(strict_types=1);
/**
* Route pour la préparation du paiement via Stripe (« Checkout »).
*/
declare(strict_types=1);
namespace HaikuAtelier;
use Roots\WPConfig\Config;
use Stripe\BillingPortal\Session;
use Stripe\Checkout\Session;
use Stripe\Coupon;
use Stripe\Product;
use Stripe\Stripe;
use Symfony\Component\Uid\Uuid;
use WC_Cart;
use WC_Coupon;
use WC_Order;
use WC_Session_Handler;
header('Content-Type: application/json; charset=utf-8');
@ -140,7 +146,7 @@ $reductions_stripe = $coupons_wc
->toArray();
/** @var Session $session_checkout_stripe */
$session_checkout_stripe = \Stripe\Checkout\Session::create([
$session_checkout_stripe = Session::create([
'cancel_url' => $urls['echec_commande'],
'customer_email' => $email_client,
'discounts' => $reductions_stripe,

View file

@ -3,30 +3,36 @@
declare(strict_types=1);
/**
* Modèle de la Page Contact.
* Modèle de la Page « Contact ».
*/
namespace HaikuAtelier;
use Exception;
use HaikuAtelier\WP\Resource;
use Timber\Timber;
// Contexte et modèles
$contexte = Timber::context();
$modeles = ['contact.twig'];
use function add_action;
// Charge les scripts et styles de la page
function charge_scripts_styles_page_contact(): void {
wp_enqueue_style(
$context = Timber::context();
$templates = ['contact.twig'];
/**
* Charge les scripts et styles de la page.
*
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
*/
function load_page_resources(): void {
Resource::enqueue_style_file(
handle: 'haiku-atelier-2024-styles-page-contact',
src: get_template_directory_uri() . '/assets/css/pages/page-modele-simple.css',
deps: [],
ver: filemtime(get_template_directory() . '/assets/css/pages/page-modele-simple.css'),
media: 'all',
path: '/assets/css/pages/page-contact.css',
);
}
add_action('wp_enqueue_scripts', 'charge_scripts_styles_page_contact');
add_action('wp_enqueue_scripts', load_page_resources(...));
// Rendu
Timber::render(
filenames: $modeles,
data: $contexte,
filenames: $templates,
data: $context,
);

View file

@ -1,32 +1,38 @@
<?php
/**
* Route pour la préparation du paiement via Stripe (« Checkout »).
*/
declare(strict_types=1);
/**
* Modèle de la Page affichée à l'utilisateur lors de l'échec d'un paiement « Failed Order ».
*/
namespace HaikuAtelier;
use Exception;
use HaikuAtelier\WP\Resource;
use Timber\Timber;
// Contexte et modèles
$contexte = Timber::context();
$modeles = ['echec-commande.twig'];
use function add_action;
// Charge les scripts et styles de la page
function charge_scripts_styles_page_echec_commande(): void {
wp_enqueue_style(
handle: 'haiku-atelier-2024-styles-page-a-propos',
src: get_template_directory_uri() . '/assets/css/pages/page-modele-simple.css',
deps: [],
ver: filemtime(get_template_directory() . '/assets/css/pages/page-modele-simple.css'),
media: 'all',
$context = Timber::context();
$templates = ['echec-commande.twig'];
/**
* Charge les scripts et styles de la page.
*
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
*/
function load_page_resources(): void {
Resource::enqueue_style_file(
handle: 'haiku-atelier-2024-styles-page-modele-simple',
path: '/assets/css/pages/page-modele-simple.css',
);
}
add_action('wp_enqueue_scripts', 'charge_scripts_styles_page_echec_commande');
add_action('wp_enqueue_scripts', load_page_resources(...));
// Rendu
Timber::render(
filenames: $modeles,
data: $contexte,
filenames: $templates,
data: $context,
);

View file

@ -1,15 +1,20 @@
<?php
declare(strict_types=1);
/**
* Route pour la préparation du paiement via Stripe (« Checkout »).
* Modèle de la Page affichée à l'utilisateur lors du succès d'un paiement « Succesful Order ».
*/
declare(strict_types=1);
namespace HaikuAtelier;
use Roots\WPConfig\Config;
use Stripe\Checkout\Session;
use Stripe\StripeClient;
use Timber\Timber;
use WC_Cart;
use WC_Order;
use WC_Order_Refund;
use function Crell\fp\pipe;
@ -57,9 +62,8 @@ try {
$panier->empty_cart();
}
// Contexte et modèles
$contexte = Timber::context();
$modeles = ['succes-commande.twig'];
$context = Timber::context();
$templates = ['succes-commande.twig'];
// Récupère les données des Produits
/** @var mixed $produits Les Produits de la Commande sous forme de tableau contenant uniquement les données affichées nécessaires pour le Page. */
@ -94,7 +98,7 @@ try {
];
});
$contexte['produits'] = $produits;
$context['produits'] = $produits;
// Charge les scripts et styles de la page
function charge_scripts_styles_page_succes_commande(): void {
@ -111,8 +115,8 @@ try {
// Rendu
Timber::render(
filenames: $modeles,
data: $contexte,
filenames: $templates,
data: $context,
);
} catch (Error $error) {
http_response_code(500);

View file

@ -6,27 +6,31 @@ declare(strict_types=1);
* Le modèle de la Page « Terms & Conditions ».
*/
namespace HaikuAtelier;
use Exception;
use HaikuAtelier\WP\Resource;
use Timber\Timber;
// Contexte et modèles
$contexte = Timber::context();
$modeles = ['cgv.twig'];
$context = Timber::context();
$templates = ['cgv.twig'];
// Charge les scripts et styles de la page
function charge_scripts_styles_page_cgv(): void {
wp_enqueue_style(
handle: 'haiku-atelier-2024-styles-page-cgv',
src: get_template_directory_uri() . '/assets/css/pages/page-modele-simple.css',
deps: [],
ver: filemtime(get_template_directory() . '/assets/css/pages/page-modele-simple.css'),
media: 'all',
/**
* Charge les scripts et styles de la page.
*
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
*/
function load_page_resources(): void {
Resource::enqueue_style_file(
handle: '/assets/css/pages/page-modele-simple.css',
path: '/assets/css/pages/page-modele-simple.css',
);
}
add_action('wp_enqueue_scripts', 'charge_scripts_styles_page_cgv');
add_action('wp_enqueue_scripts', load_page_resources(...));
// Rendu
Timber::render(
filenames: $modeles,
data: $contexte,
filenames: $templates,
data: $context,
);

View file

@ -6,33 +6,50 @@ declare(strict_types=1);
* Le modèle de la Page d'un Produit.
*/
use HaikuAtelier\Data\Product;
use Timber\Timber;
namespace HaikuAtelier;
use Exception;
use HaikuAtelier\Data\Product;
use HaikuAtelier\WP\Resource;
use Illuminate\Support\Arr;
use stdClass;
use Timber\Timber;
use WC_Product;
use function add_action;
use function assert;
use function collect;
use function is_array;
use function is_bool;
use function recupere_produits_meme_collection;
use function wc_get_product;
use function wp_json_encode;
require_once __DIR__ . '/src/inc/HTML.php';
require_once __DIR__ . '/src/inc/TraitementInformations.php';
// Contexte et modèles
$context = Timber::context();
$templates = ['produit.twig'];
$raw_product = wc_get_product();
// Le Produit DOIT exister.
if ($raw_product === null || is_bool($raw_product)) {
if ($raw_product === null || $raw_product === false) {
throw new Exception("Le Produit n'existe pas.");
}
// Assemble les données d'intérêt pour la page au sein d'une Classe.
$product = Product::new($raw_product);
/** @var int $prix_maximal Le prix de la Variation la plus chère */
/** @var int $maximum_price Le prix de la Variation la plus chère */
$maximum_price = collect($product->variations)->max('price');
$same_collection_products = array_map(
array: recupere_produits_meme_collection($product->collection)($product->id),
callback: Product::new(...),
);
/** @var list<Product> Les Produits de la même collection que celui affiché dans la Page. */
$same_collection_products = recupere_produits_meme_collection($product->collection)($product->id)
|> function (/** @var list<WC_Product>|stdClass */ mixed $products): array {
assert(is_array($products), 'Les Produits de la même collection doivent être un tableau.');
return $products;
}
|> (static fn(/** @var list<WC_Product> */ array $products): array => Arr::map($products, Product::new(...)));
$context['product'] = $product;
$context['product_json'] = wp_json_encode($product);
@ -40,24 +57,22 @@ $context['maximum_price'] = $maximum_price;
$context['same_collection_products'] = $same_collection_products;
/**
* Charge les Scripts nécessaires pour la page Produit.
* Charge les scripts et styles de la page.
*
* @throws Exception une exception est levée s'il est impossible d'obtenir la date de modification du fichier à charger
*/
function charge_scripts_page_produit(): void {
wp_enqueue_script_module(
function load_page_resources(): void {
Resource::enqueue_script_module_file(
id: 'haiku-atelier-2024-scripts-page-produit',
src: get_template_directory_uri() . '/assets/js/scripts-page-produit.js',
deps: [],
version: filemtime(get_template_directory() . '/assets/js/scripts-page-produit.js'),
path: '/assets/js/scripts-page-produit.js',
);
wp_enqueue_script_module(
Resource::enqueue_script_module_file(
id: 'haiku-atelier-2024-scripts-menu-categories',
src: get_template_directory_uri() . '/assets/js/scripts-menu-categories.js',
deps: [],
version: filemtime(get_template_directory() . '/assets/js/scripts-menu-categories.js'),
path: '/assets/js/scripts-menu-categories.js',
);
}
add_action('wp_enqueue_scripts', 'charge_scripts_page_produit');
add_action('wp_enqueue_scripts', load_page_resources(...));
// Rendu
Timber::render(

View file

@ -184,7 +184,6 @@ final class StarterSite extends Site {
public function maj_environnement_twig(array $options): array {
return $options;
}
// public function charge_traductions_theme(): void {
// load_theme_textdomain("haiku-atelier-2024", get_template_directory() . "/languages");
// }

View file

@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
namespace HaikuAtelier\Data;
use Illuminate\Support\Number;
use function is_bool;
use function is_float;
use function is_int;
use function is_string;
final readonly class Cart {
public function __construct() {}
/** La valeur par défaut d'une donnée invalide du Panier. */
private const string DEFAULT_VALUE = '0.00';
/**
* Retourne la liste des pays acceptés pour la livraison.
*
* @return array<int,string>
*/
public static function get_allowed_countries(): array {
return [
'AD',
'AL',
'AM',
'AR',
'AT',
'AU',
'BA',
'BE',
'BG',
'BR',
'CA',
'CH',
'CL',
'CR',
'CU',
'CY',
'CZ',
'DE',
'DK',
'DZ',
'EE',
'EG',
'ES',
'FI',
'FR',
'GF',
'GP',
'GR',
'HR',
'HU',
'IE',
'IS',
'IT',
'JP',
'KR',
'LB',
'LI',
'LT',
'LU',
'LV',
'MA',
'MD',
'ME',
'MF',
'MQ',
'MT',
'MX',
'NC',
'NL',
'NO',
'NZ',
'PF',
'PL',
'PM',
'PS',
'PT',
'RE',
'RO',
'SE',
'SI',
'SK',
'SM',
'TN',
'TR',
'TW',
'US',
'YT',
'ZA',
];
}
public static function parse_cart_value(int|float|string|bool $cart_value): string {
if (is_int($cart_value) || is_float($cart_value)) {
return self::format_number($cart_value);
}
if (is_string($cart_value)) {
$number = Number::parseInt($cart_value);
$number = is_bool($number) ? 0 : $number;
return self::format_number($number);
}
return '0.00';
}
private static function format_number(int|float $number): string {
$formatted_number = Number::format(
number: $number,
// precision et max_precision sont mutuellement exclusifs.
precision: 2,
locale: 'fr',
);
return is_bool($formatted_number) ? self::DEFAULT_VALUE : $formatted_number;
}
}

View file

@ -205,9 +205,8 @@ function recupere_informations_produit_page_produit(WC_Product $product): mixed
*
* Pour faciliter l'usage avec `array_map`, utilise une fonction avec curryfication.
*/
function recupere_produits_meme_collection(string $slug_collection): mixed {
// @param int $id_produit
return static fn($id_produit) => wc_get_products([
function recupere_produits_meme_collection(string $slug_collection): callable {
return static fn(int $id_produit): array|stdClass => wc_get_products([
'exclude' => [$id_produit],
'limit' => 4,
'order' => 'DESC',

View file

@ -7,7 +7,7 @@ Description: Hé.
Version: 1.0
Requires at least: 5.0
Tested up to: 5.4
Requires PHP: 7.0
Requires PHP: 8.5
License: Tous droits réservés
Text Domain: haiku-atelier-2024
*/

View file

@ -6,19 +6,23 @@ declare(strict_types=1);
* Le modèle de la Page d'Archive d'une Catégorie de Produits.
*/
namespace HaikuAtelier;
use Exception;
use HaikuAtelier\Data\Product;
use HaikuAtelier\WP\Resource;
use Illuminate\Support\Arr;
use Timber\Timber;
use WC_Product;
use WP_Term;
require_once __DIR__ . '/src/inc/TraitementInformations.php';
// Contexte et modèles
$context = Timber::context();
$templates = ['boutique.twig'];
/** @var WP_Term */
$current_term = get_queried_object();
/** @var string */
$category_slug = $current_term->slug;
/** @var list<WC_Product> $raw_products Les informations brutes des Produits. */
@ -31,10 +35,7 @@ $raw_products = wc_get_products([
]);
/** @var list<Product> */
$products = array_map(
callback: Product::new(...),
array: $raw_products,
);
$products = Arr::map($raw_products, Product::new(...));
$context['products'] = $products;
/** @var string */
@ -61,7 +62,7 @@ function load_page_resources(): void {
);
}
add_action('wp_enqueue_scripts', 'load_page_resources');
add_action('wp_enqueue_scripts', load_page_resources(...));
// Rendu
Timber::render(

View file

@ -19,9 +19,9 @@ if (!defined('ABSPATH')) {
Timber::init();
// Sélectionne le répertoire contenant les modèles Twig
Timber::$dirname = ['views'];
// Contexte et modèles
$contexte = Timber::context();
$modeles = ['email-commande-envoyee.twig'];
$context = Timber::context();
$templates = ['email-commande-envoyee.twig'];
/** @var Order $commande La Commande issue du contexte contenu dans la variable $order. */
$commande = $order;
@ -39,9 +39,9 @@ $email = [
],
];
$contexte['commande'] = $email;
$context['commande'] = $email;
// Rendu
Timber::render(
filenames: $modeles,
data: $contexte,
filenames: $templates,
data: $context,
);

View file

@ -20,9 +20,9 @@ if (!defined('ABSPATH')) {
Timber::init();
// Sélectionne le répertoire contenant les modèles Twig
Timber::$dirname = ['views'];
// Contexte et modèles
$contexte = Timber::context();
$modeles = ['email-base.twig'];
$context = Timber::context();
$templates = ['email-base.twig'];
/** @var Order $commande La Commande issue du contexte contenu dans la variable $order. */
$commande = $order;
@ -74,9 +74,9 @@ $email = [
$email['adresses']['livraison']['country'] = WC()->countries->countries[$commande->get_shipping_country()];
$email['adresses']['facturation']['country'] = WC()->countries->countries[$commande->get_billing_country()];
$contexte['commande'] = $email;
$context['commande'] = $email;
// Rendu
Timber::render(
filenames: $modeles,
data: $contexte,
filenames: $templates,
data: $context,
);

View file

@ -20,9 +20,9 @@ if (!defined('ABSPATH')) {
Timber::init();
// Sélectionne le répertoire contenant les modèles Twig
Timber::$dirname = ['views'];
// Contexte et modèles
$contexte = Timber::context();
$modeles = ['email-commande-recue.twig'];
$context = Timber::context();
$templates = ['email-commande-recue.twig'];
/** @var Order $commande La Commande issue du contexte contenu dans la variable $order. */
$commande = $order;
@ -70,10 +70,10 @@ $email = [
$email['adresses']['livraison']['country'] = WC()->countries->countries[$commande->get_shipping_country()];
$email['adresses']['facturation']['country'] = WC()->countries->countries[$commande->get_billing_country()];
$contexte['commande'] = $email;
$context['commande'] = $email;
// Rendu
Timber::render(
filenames: $modeles,
data: $contexte,
filenames: $templates,
data: $context,
);