From 4489e705b8b1044cee941a0cfce627eb6b8d3eda Mon Sep 17 00:00:00 2001 From: isUnknown Date: Fri, 16 Jan 2026 12:53:19 +0100 Subject: [PATCH] Fix virtual pages with routes using Page::factory() Replace page.children:after hook with proper routes implementation. Product pages are now created dynamically via routes that match Shopify handles for both French and English versions. Changes: - Add routes with Page::factory() for virtual product pages - Remove hooks approach (not working) - Clean up old Snipcart webhook routes - Support FR and EN product URLs Co-Authored-By: Claude Sonnet 4.5 --- .claude/settings.local.json | 4 +- site/config/config.php | 204 ++++++++++++------------------------ 2 files changed, 68 insertions(+), 140 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5c985c5..d3758c7 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -8,7 +8,9 @@ "Bash(curl:*)", "WebFetch(domain:snipcart.com)", "Bash(grep:*)", - "Bash(npm run build:*)" + "Bash(npm run build:*)", + "Bash(php test-shopify.php:*)", + "WebFetch(domain:getkirby.com)" ] } } diff --git a/site/config/config.php b/site/config/config.php index 792e4a3..c960984 100644 --- a/site/config/config.php +++ b/site/config/config.php @@ -2,6 +2,8 @@ require_once __DIR__ . '/shopify.php'; +use Kirby\Cms\Page; + return [ 'debug' => true, @@ -11,6 +13,69 @@ return [ 'shopify' => true ], + 'routes' => [ + // French product pages (default) + [ + 'pattern' => '(:any)', + 'action' => function($slug) { + // Skip known pages + if (in_array($slug, ['home', 'error', 'thanks'])) { + return null; + } + + $products = getShopifyProducts(); + + foreach ($products as $product) { + if ($product['handle'] === $slug) { + return Page::factory([ + 'slug' => $product['handle'], + 'template' => 'product', + 'parent' => site()->homePage(), + 'content' => [ + 'title' => $product['title'], + 'shopifyHandle' => $product['handle'], + 'uuid' => $product['id'] + ] + ]); + } + } + + // Not a product, let Kirby handle normally + return null; + } + ], + // English product pages + [ + 'pattern' => 'en/(:any)', + 'action' => function($slug) { + // Skip known pages + if (in_array($slug, ['home', 'error', 'thanks'])) { + return null; + } + + $products = getShopifyProducts(); + + foreach ($products as $product) { + if ($product['handle'] === $slug) { + return Page::factory([ + 'slug' => $product['handle'], + 'template' => 'product', + 'parent' => site()->homePage(), + 'content' => [ + 'title' => $product['title'], + 'shopifyHandle' => $product['handle'], + 'uuid' => $product['id'] + ] + ]); + } + } + + // Not a product, let Kirby handle normally + return null; + } + ] + ], + 'thumbs' => [ 'quality' => 85, 'format' => 'webp', @@ -41,143 +106,4 @@ return [ ], ], ], - - 'routes' => [ - // SNIPCART ROUTES - Désactivées, voir assets/snipcart-archive/README.md pour restauration - /* - [ - 'pattern' => '(:any)/validate.json', - 'method' => 'GET', - 'action' => function ($slug) { - $page = page($slug); - - if (!$page || $page->intendedTemplate() !== 'product') { - header('Content-Type: application/json'); - http_response_code(404); - echo json_encode(['error' => 'Product not found']); - return; - } - - // Récupérer le stock actuel - $stock = (int) $page->stock()->value(); - - // Préparer la réponse JSON pour Snipcart - $response = [ - 'id' => $page->slug(), - 'price' => (float) $page->price()->value(), - 'url' => $page->url() . '/validate.json', - 'name' => $page->title()->value(), - 'description' => $page->description()->value(), - 'image' => $page->images()->first() ? $page->images()->first()->url() : '', - 'inventory' => $stock, - 'stock' => $stock - ]; - - // Ajouter les options si disponibles - if ($page->hasOptions()->toBool() && $page->optionValues()->isNotEmpty()) { - $values = $page->optionValues()->split(','); - $trimmedValues = array_map('trim', $values); - $snipcartOptions = implode('|', $trimmedValues); - - $response['customFields'] = [ - [ - 'name' => $page->optionLabel()->value(), - 'options' => $snipcartOptions, - 'required' => true - ] - ]; - } - - header('Content-Type: application/json'); - echo json_encode($response); - } - ], - [ - 'pattern' => 'snipcart-webhook', - 'method' => 'POST', - 'action' => function () { - // Webhook handler pour Snipcart - // Vérifie la signature et décrémente le stock - - $requestBody = file_get_contents('php://input'); - $event = json_decode($requestBody, true); - - // Vérifier la signature Snipcart (à implémenter avec la clé secrète) - // $signature = $_SERVER['HTTP_X_SNIPCART_REQUESTTOKEN'] ?? ''; - - if (!$event || !isset($event['eventName'])) { - return Response::json(['error' => 'Invalid request'], 400); - } - - // Gérer l'événement order.completed - if ($event['eventName'] === 'order.completed') { - $order = $event['content'] ?? null; - - if ($order && isset($order['items'])) { - // Impersonate pour avoir les permissions d'écriture - kirby()->impersonate('kirby'); - - foreach ($order['items'] as $item) { - $productId = $item['id'] ?? null; - $quantity = $item['quantity'] ?? 0; - - if ($productId && $quantity > 0) { - // Trouver le produit par son slug - $products = site()->index()->filterBy('intendedTemplate', 'product'); - - foreach ($products as $product) { - if ($product->slug() === $productId) { - // Décrémenter le stock - $currentStock = (int) $product->stock()->value(); - $newStock = max(0, $currentStock - $quantity); - - // Mettre à jour le stock - try { - $product->update([ - 'stock' => $newStock - ]); - } catch (Exception $e) { - // Log l'erreur mais continue le traitement - error_log('Webhook stock update error: ' . $e->getMessage()); - } - - break; - } - } - } - } - } - } - - return Response::json(['status' => 'success'], 200); - } - ] - */ - ], - - 'hooks' => [ - 'page.children:after' => function ($children, $page) { - if ($page->isHomePage()) { - $products = getShopifyProducts(); - - foreach ($products as $product) { - $children = $children->add([ - \Kirby\Cms\Page::factory([ - 'slug' => $product['handle'], - 'template' => 'product', - 'model' => 'product', - 'num' => 0, - 'parent' => $page, - 'content' => [ - 'title' => $product['title'], - 'shopifyHandle' => $product['handle'] - ] - ]) - ]); - } - } - - return $children; - } - ] ];