Hyvä-Checkout erweitern: eigene Steps, Felder, Validierung
AI generated
Hyvä
phtml
Hyvä-Checkout · Magento 2 · Alpine.js · Tailwind CSS v4
Hyvä-Checkout erweitern:
eigene Steps, Felder und Validierung

Wer den Hyvä-Checkout in Magento 2 anpassen will, trifft auf eine Architektur ganz ohne Knockout.js und UI Components: Checkout-Steps werden als phtml-Templates serverseitig gerendert und über Alpine.js reaktiv gemacht. Dieser Artikel zeigt anhand von echtem Code, wie eigene Checkout-Steps, zusätzliche Formularfelder und eine saubere Validierung in den Hyvä-Checkout integriert werden, ohne den Theme-Kern zu verändern und ohne ein einziges Knockout-Template anzufassen.

18 Min. Lesezeit Alpine.js · GraphQL · ViewModel · Plugin Magento 2.4.8-p4 · Hyvä Themes · Tailwind v4

1. Warum Checkout-Anpassungen im Hyvä-Checkout anders funktionieren als in Luma/Knockout.js

Wer aus der Luma-Welt kommt, sucht beim Hyvä-Checkout zunächst vergeblich nach vertrauten Konzepten: kein uiComponent-Layout in checkout_index_index.xml mit jsLayout-Merges, keine data-bind-Attribute, kein requirejs-config.js-Mixin, das eine Knockout-Komponente um eine zusätzliche Methode erweitert. Der Hyvä-Checkout verzichtet vollständig auf das Knockout.js-Databinding und auf UI Components. Zwei Frameworks, die in Luma für nahezu jede Interaktion einen eigenen Layer aus JS-Templates, ViewModels und Observable-Ketten erzwingen. Stattdessen wird jeder Checkout-Schritt als gewöhnliches phtml-Template serverseitig gerendert und erst im Browser durch Alpine.js mit x-data und x-model interaktiv gemacht.

Diese Verschiebung hat direkte Konsequenzen für jede Form von Checkout-Anpassung: Statt eine Knockout-Komponente per di.xml-jsLayout-Merge zu erweitern und ein separates .html-Template über requirejs zu laden, schreibt man beim Hyvä-Checkout Customizing ein PHP-ViewModel, das Daten aufbereitet, und ein phtml-Template, das diese Daten direkt ausgibt. Die Business-Logik bleibt in PHP, die Reaktivität bleibt im Template. Es gibt keinen dritten Layer aus JS-ViewModels, der synchron gehalten werden muss. Das reduziert die Anzahl der Dateien, die für eine einzige Änderung angefasst werden müssen, von vier oder fünf auf typischerweise zwei.

Für Entwickler bedeutet das: Debugging findet direkt im gerenderten HTML statt, nicht in einer Kette aus Knockout-Bindings, die erst zur Laufzeit im Browser aufgelöst werden. Wer den Hyvä-Checkout erweitern will, profitiert von kürzeren Feedback-Zyklen, weil Alpine.js keinen Build-Schritt benötigt und Änderungen am Markup sofort im Browser sichtbar sind, sobald der Hyvä-Watcher den Cache invalidiert hat.

2. Architektur des Hyvä-Checkouts

Die Modulstruktur des Hyvä-Checkouts folgt dem üblichen Hyvä-Muster: Templates liegen unter view/frontend/templates/checkout/, Layout-Anpassungen unter view/frontend/layout/checkout_index_index.xml, und JS-Komponenten werden entweder inline im phtml oder als separate Datei unter view/frontend/web/js/ abgelegt. Der Checkout-Flow selbst ist im DOM als eine Abfolge von Bereichen organisiert, die per x-show="step === 'shipping'", x-show="step === 'payment'" und x-show="step === 'review'" ein- und ausgeblendet werden. Anstatt, wie in Knockout, als eigenständige Components mit eigenem Lifecycle registriert zu sein.

Die Klammer um alle Schritte bildet ein zentraler Alpine.store('checkout'), der den aktuellen Schritt, den Ladezustand und gemeinsam genutzte Werte wie die aktuelle cartId hält. Jede einzelne Checkout-Komponente liest und schreibt über Alpine.store('checkout'), statt Zustand über Props von einer Parent-Komponente nach unten zu reichen. Das Hyvä-typische Block-System bleibt dabei vollständig erhalten: $block->getChildNames() iteriert weiterhin über die registrierten Kindblöcke, sodass sich neue Checkout-Bereiche sauber über Layout-XML einhängen lassen, ohne den Rendering-Mechanismus zu verändern.

Wichtig für jede tiefere Hyvä-Checkout-Anpassung: Die Kommunikation mit dem Server läuft nahezu ausschließlich über GraphQL-Mutationen wie setShippingAddressesOnCart, setPaymentMethodOnCart und placeOrder, aufgerufen über die globale Hilfsfunktion hyva.graphqlQuery(), die Hyvä-Themes standardmäßig bereitstellt. Es gibt keine serverseitig gerenderten JS-Templates, die bei jeder Änderung neu kompiliert werden müssten. Der Browser fragt Daten aktiv über GraphQL ab und aktualisiert den Alpine-Store mit der Antwort.

3. Eigenen Checkout-Step hinzufügen

Ein neuer Schritt im Hyvä-Checkout beginnt immer mit einer Layout-XML-Datei, die ein zusätzliches Template als Kindblock des Checkout-Containers registriert. Wichtig ist die Positionierung über before oder after, damit der neue Schritt an der richtigen Stelle im DOM landet. Die Reihenfolge im Markup bestimmt beim Hyvä-Checkout direkt die visuelle Reihenfolge, es gibt keine separate Sortierungs-Konfiguration wie bei Knockout-jsLayout.

Der folgende Ausschnitt hängt einen Schritt für Lieferanweisungen zwischen Versand und Zahlung ein und übergibt ein eigenes ViewModel, das die Optionen für das Formular bereitstellt:


<?xml version="1.0"?>
<!--
  File: app/design/frontend/Mironsoft/default/Magento_Checkout/layout/checkout_index_index.xml
  Registers a custom checkout step template between shipping and payment.
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      layout="1column"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <block class="Magento\Framework\View\Element\Template"
                   name="checkout.delivery-instructions"
                   template="Mironsoft_CheckoutCustomFields::checkout/delivery-instructions.phtml"
                   before="checkout.payment-methods">
                <arguments>
                    <argument name="view_model" xsi:type="object">
                        Mironsoft\CheckoutCustomFields\ViewModel\DeliveryInstructions
                    </argument>
                </arguments>
            </block>
        </referenceBlock>
    </body>
</page>

Die Sichtbarkeit des neuen Schritts wird nicht in PHP entschieden, sondern im Alpine-Store gesteuert: Ein Eintrag steps: ['shipping', 'delivery-instructions', 'payment', 'review'] im zentralen Store legt fest, in welcher Reihenfolge und unter welchen Bedingungen der Schritt angezeigt wird. Damit lässt sich ein Schritt im Hyvä-Checkout auch bedingt ein- oder ausblenden, etwa nur für bestimmte Versandarten, ohne dass die Layout-XML angepasst werden muss.

4. Eigene Felder im Versand- oder Zahlungsschritt einfügen

Eigene Formularfelder im Hyvä-Checkout brauchen zunächst einen Speicherort. Für Felder an der Quote oder Adresse eignet sich ein Extension Attribute, deklariert über etc/extension_attributes.xml und persistiert über eine eigene Tabelle, die per db_schema.xml deklariert wird. Kein InstallSchema-Skript, wie es in älteren Modulen üblich war. Das ViewModel liefert dem Template anschließend sowohl die verfügbaren Optionen als auch den aktuell gespeicherten Wert aus der Quote.


<?php

declare(strict_types=1);

namespace Mironsoft\CheckoutCustomFields\ViewModel;

use Magento\Checkout\Model\Session as CheckoutSession;
use Magento\Framework\View\Element\Block\ArgumentInterface;

/**
 * Provides delivery instruction options and the current quote value to the checkout template.
 */
class DeliveryInstructions implements ArgumentInterface
{
    /**
     * @param CheckoutSession $checkoutSession Active checkout session used to read the current quote
     */
    public function __construct(
        private readonly CheckoutSession $checkoutSession
    ) {
    }

    /**
     * Returns the selectable delivery instruction options for the checkout form.
     *
     * @return array<string, string>
     */
    public function getInstructionOptions(): array
    {
        return [
            'ring_bell' => 'Klingeln',
            'leave_at_door' => 'Vor der Tür ablegen',
            'neighbor' => 'Bei Nachbarn abgeben',
        ];
    }

    /**
     * Returns the delivery instruction currently stored on the quote extension attributes.
     *
     * @return string
     */
    public function getCurrentInstruction(): string
    {
        $quote = $this->checkoutSession->getQuote();
        $extensionAttributes = $quote->getExtensionAttributes();

        return $extensionAttributes?->getDeliveryInstruction() ?? '';
    }
}

Die Übergabe des Feldwerts an den Server läuft im Hyvä-Checkout nicht über ein klassisches Formular-POST, sondern über eine dedizierte GraphQL-Mutation, die als Custom-Resolver in einem eigenen schema.graphqls deklariert wird. Alpine ruft die Mutation über hyva.graphqlQuery() auf, sobald sich der Feldwert ändert oder der Nutzer den Schritt verlässt. Die folgende JSON-Struktur zeigt Request und Response dieser Mutation:


{
  "request": {
    "query": "mutation SetDeliveryInstruction($cartId: String!, $instruction: String!) { setDeliveryInstructionOnCart(input: { cart_id: $cartId, delivery_instruction: $instruction }) { cart { id delivery_instruction } } }",
    "variables": {
      "cartId": "a1b2c3d4e5f6",
      "instruction": "ring_bell"
    }
  },
  "response": {
    "data": {
      "setDeliveryInstructionOnCart": {
        "cart": {
          "id": "a1b2c3d4e5f6",
          "delivery_instruction": "ring_bell"
        }
      }
    }
  }
}

Wichtig bei jeder Hyvä-Checkout-Anpassung dieser Art: Der Resolver muss die cart_id in eine reale Quote auflösen und prüfen, ob der aktuelle Kunde überhaupt Zugriff auf diese Quote hat. Sonst öffnet man ungewollt eine Lücke, über die fremde Warenkörbe manipuliert werden könnten.

5. Serverseitige Validierung

Clientseitige Validierung im Hyvä-Checkout verhindert offensichtliche Fehleingaben, ersetzt aber nie die serverseitige Prüfung. GraphQL-Requests lassen sich problemlos direkt gegen die API senden, unter Umgehung jedes Frontend-Codes. Die serverseitige Validierung sitzt entweder direkt im GraphQL-Resolver oder als Plugin auf dem Quote-Repository, je nachdem, ob der Wert über einen Custom-Resolver oder über einen Standard-Mutation-Pfad wie setShippingAddressesOnCart hereinkommt.


<?php

declare(strict_types=1);

namespace Mironsoft\CheckoutCustomFields\Plugin;

use Magento\Framework\Exception\LocalizedException;
use Magento\Quote\Api\CartRepositoryInterface;
use Magento\Quote\Api\Data\CartInterface;

/**
 * Validates the custom delivery instruction attribute before the quote gets persisted.
 */
class ValidateDeliveryInstructionPlugin
{
    private const array VALID_INSTRUCTIONS = ['ring_bell', 'leave_at_door', 'neighbor', ''];

    /**
     * Rejects unknown delivery instruction values before the quote is saved.
     *
     * @param CartRepositoryInterface $subject Original repository instance
     * @param CartInterface $quote Quote about to be persisted
     * @return array{CartInterface}
     * @throws LocalizedException
     */
    public function beforeSave(CartRepositoryInterface $subject, CartInterface $quote): array
    {
        $extensionAttributes = $quote->getExtensionAttributes();
        $instruction = $extensionAttributes?->getDeliveryInstruction() ?? '';

        if (!in_array($instruction, self::VALID_INSTRUCTIONS, true)) {
            throw new LocalizedException(
                __('Die gewählte Lieferanweisung ist ungültig.')
            );
        }

        return [$quote];
    }
}

Für Custom-Resolver reicht das Plugin allein nicht aus. Hier prüft der Resolver den Wert direkt und wirft bei einem Fehler eine GraphQlInputException, die Magento automatisch in ein sauberes GraphQL-Error-Objekt mit message und category übersetzt. Alpine kann diese Fehlermeldung direkt aus dem errors-Array der GraphQL-Antwort lesen und im entsprechenden Feld anzeigen, ohne dass ein generischer "Etwas ist schiefgelaufen"-Text den eigentlichen Grund verschleiert.

6. Clientseitige Validierung mit Alpine.js

Clientseitige Validierung im Hyvä-Checkout lebt vollständig in Alpine-Komponenten: reaktive Fehlerzustände als einfache Objekteigenschaften, geprüft bei @blur oder @input, und ein Submit-Button, der über :disabled an einen berechneten Getter gebunden ist. Der entscheidende Unterschied zu Knockout: Es gibt keine ko.computed()-Kette und keine separate Validator-Bibliothek, die über requirejs-config.js gemappt werden muss. Die gesamte Logik steht direkt im x-data-Objekt.


// File: app/design/frontend/Mironsoft/default/Magento_Checkout/templates/checkout/delivery-instructions.phtml
// Alpine component providing reactive validation for the delivery instruction field.
function deliveryInstructionsForm() {
  return {
    instruction: '',
    error: '',
    options: {
      ring_bell: 'Klingeln',
      leave_at_door: 'Vor der Tür ablegen',
      neighbor: 'Bei Nachbarn abgeben'
    },

    // Computed getter. Drives the disabled state of the submit button
    get isValid() {
      return this.instruction !== '' && this.error === '';
    },

    // Runs on @change/@blur. Never on every keystroke to avoid noisy error flicker
    validate() {
      this.error = this.instruction === ''
        ? 'Bitte eine Lieferanweisung auswählen.'
        : '';
    },

    async submit() {
      this.validate();
      if (!this.isValid) {
        return;
      }

      const result = await hyva.graphqlQuery(
        `mutation { setDeliveryInstructionOnCart(input: { cart_id: "${window.checkoutConfig.quoteData.cartId}", delivery_instruction: "${this.instruction}" }) { cart { id } } }`
      );

      if (result.errors) {
        this.error = result.errors[0].message;
        return;
      }

      window.dispatchEvent(new CustomEvent('delivery-instruction-saved', {
        detail: { instruction: this.instruction }
      }));
    }
  };
}

Im zugehörigen phtml-Markup wird der Fehlertext niemals als Mustache-Ausdruck geschrieben, sondern immer über x-text="error" gebunden. Ein literaler {{ error }}-Ausdruck würde von Magentos Template-Direktiven-Parser fehlinterpretiert, da {{ }} in phtml-Dateien für Magento-eigene Direktiven reserviert ist. Nach jedem Inline-<script>-Block im phtml folgt außerdem zwingend $hyvaCsp->registerInlineScript(), damit die Content Security Policy das Skript nicht blockiert.

7. State-Management zwischen Checkout-Steps

Sobald mehrere Schritte im Hyvä-Checkout voneinander abhängen, etwa wenn die Zahlungsart die Sichtbarkeit eines Feldes im Versandschritt beeinflusst, wird ein zentraler Zustand unverzichtbar. Alpine.store('checkout') übernimmt diese Rolle: Jede Komponente liest den relevanten Ausschnitt des Stores per $store.checkout.currentStep und schreibt Änderungen direkt zurück, ohne dass Zustand als Prop von einer Elternkomponente nach unten gereicht werden muss.

Für Kommunikation, die nicht über den gemeinsamen Store laufen soll, etwa weil zwei Komponenten in unterschiedlichen Blöcken liegen und keine direkte Store-Abhängigkeit haben sollen, eignen sich CustomEvent-Objekte. Eine Komponente feuert window.dispatchEvent(new CustomEvent('delivery-instruction-saved', { detail: {...} })), eine andere hört mit x-on:delivery-instruction-saved.window="handleSaved($event.detail)" zu. Dieses Muster vermeidet Prop-Drilling vollständig und hält Komponenten lose gekoppelt. Eine Komponente muss die Existenz der anderen nicht kennen, nur den Event-Namen.

Der zentrale Store sollte dabei bewusst schlank bleiben: Nur Zustand, der wirklich von mehreren Schritten im Hyvä-Checkout gemeinsam benötigt wird, gehört hinein. Rein lokale UI-Zustände wie ein aufgeklapptes Akkordeon bleiben im lokalen x-data der jeweiligen Komponente, sonst wächst der globale Store zu einer unübersichtlichen Ansammlung von Feldern, die niemand mehr zuordnen kann.

8. Testing und Debugging von Checkout-Anpassungen

Für das Debugging von Alpine-Komponenten im Hyvä-Checkout reichen die Browser-Devtools meist aus: Im Elements-Panel zeigt das Alpine-DevTools-Plugin den aktuellen Zustand jeder Komponente inklusive aller reaktiven Properties an, ohne dass ein console.log im Code stehen muss. Für schnelle Prüfungen genügt auch $el.__x.$data in der Konsole, um den internen Zustand einer Komponente direkt am DOM-Element zu inspizieren.

Die häufigste Fehlerquelle bei neuen Checkout-Anpassungen ist die Content Security Policy. Jeder Inline-<script>-Block, der nicht über $hyvaCsp->registerInlineScript() registriert wurde, wird von der Browser-Konsole mit einer Refused to execute inline script-Meldung blockiert. Der Code sieht dann im Quelltext korrekt aus, läuft aber schlicht nicht. Ein zweiter typischer CSP-Fehler entsteht, wenn eine neue Checkout-Komponente einen externen Endpunkt per fetch() anspricht, der nicht in etc/csp_whitelist.xml eingetragen ist. Die Anfrage wird dann lautlos blockiert, ohne dass die Anwendung selbst einen Fehler wirft.

Für automatisierte Tests eignet sich die Kombination aus PHPUnit für die serverseitigen Teile (ViewModel, Plugin, Resolver) und einem End-to-End-Test, der den kompletten Checkout-Flow im Browser durchspielt. Da der Hyvä-Checkout keine Knockout-Components kapselt, lassen sich Formularfelder in E2E-Tests über normale CSS-Selektoren ansprechen, ohne auf Knockout-spezifische Bindings Rücksicht nehmen zu müssen.

9. Deployment und Performance-Überlegungen

Jede Änderung an Tailwind-Klassen im Hyvä-Checkout erfordert einen neuen CSS-Build über bin/npm --prefix app/design/frontend/Mironsoft/default/web/tailwind run build, bevor die Static Content Deployment läuft. Vor dem Deploy müssen var/view_preprocessed und pub/static/frontend geleert werden. Wird dieser Schritt übersprungen, liefert Magento unter Umständen weiterhin die alten, bereits kompilierten phtml-Ausgaben aus dem Preprocessing-Cache aus, obwohl der Quellcode längst aktualisiert wurde.

Auf der Performance-Seite lohnt sich ein genauer Blick auf die Anzahl der GraphQL-Requests, die eine Checkout-Anpassung auslöst. Ein @input-Handler, der bei jedem Tastendruck eine Mutation an den Server schickt, erzeugt bei einem zehnstelligen Eingabefeld theoretisch zehn Requests. Ein @blur- oder debounced @input.debounce.500ms-Handler reduziert das auf einen einzigen Request pro abgeschlossener Eingabe. Für Daten, die sich innerhalb eines Checkout-Durchlaufs nicht ändern, etwa die Liste der Versandarten, lohnt sich ein clientseitiges Caching im Alpine-Store, statt bei jedem Schrittwechsel erneut zu fragen.

Quote-Daten selbst profitieren zusätzlich vom serverseitigen Full-Page-Cache und den Standard-Magento-Caching-Mechanismen für Quote und Cart. Ein Hyvä-Checkout Customizing, das eigene GraphQL-Resolver hinzufügt, sollte diese Resolver so schreiben, dass sie ausschließlich die tatsächlich benötigten Felder aus der Quote laden, statt das komplette Objekt zu serialisieren und damit unnötig viel Datenbank-I/O zu erzeugen.

Aufgabe Luma / Knockout.js Hyvä-Checkout mit Alpine.js Vorteil
Eigenen Step hinzufügen Knockout-Component + jsLayout-Merge in di.xml phtml-Template per Layout-XML einhängen Weniger Dateien, kein Build-Schritt nötig
Formularfeld ergänzen ko.observable() + data-bind-Template x-model direkt im phtml Markup und Logik in einer Datei sichtbar
Validierung einbauen JS-Validator-Mixin + requirejs-config.js Alpine x-data + PHP-Plugin serverseitig Client- und Server-Validierung klar getrennt
State zwischen Components teilen uiRegistry / globaler JS-Namespace Alpine.store('checkout') Kein Prop-Drilling, klare Datenquelle
Deploy nach Änderung RequireJS-Bundling + Knockout-Template-Cache Tailwind-Build + Static Content Deploy Direkt serverseitig gerendert, kein JS-Bundle

Die Tabelle zeigt den durchgängigen Trend: Jede Aufgabe, die im Luma-Checkout einen zusätzlichen JS-Layer aus Observable-Ketten und Template-Bindings brauchte, löst sich im Hyvä-Checkout mit deutlich weniger beteiligten Dateien und ohne Build-Abhängigkeit zwischen PHP und JS-Bundle.

Mironsoft

Hyvä-Theme-Entwicklung, Checkout-Customizing und Magento-2-Architektur

Eigene Checkout-Steps, Felder oder Validierung im Hyvä-Checkout gesucht?

Wir erweitern euren Hyvä-Checkout um eigene Schritte, Formularfelder und Validierungslogik. Mit sauberer Layout-XML, PHP-ViewModels, GraphQL-Resolvern und Alpine.js-Komponenten statt Workarounds im Theme-Kern.

Checkout-Audit

Analyse bestehender Checkout-Anpassungen auf CSP-Konformität und Wartbarkeit

Custom Steps

Eigene Checkout-Schritte, Felder und GraphQL-Resolver nach Maß

Validierung & Tests

Server- und clientseitige Validierung mit PHPUnit- und E2E-Absicherung

10. Zusammenfassung

Das Hyvä-Checkout Customizing folgt durchgängig demselben Prinzip: PHP-ViewModels liefern Daten, phtml-Templates rendern sie serverseitig, und Alpine.js macht sie im Browser reaktiv. Ohne Knockout.js, ohne UI Components und ohne separate JS-Template-Kompilierung. Ein eigener Checkout-Step entsteht aus einer Layout-XML-Ergänzung und einem phtml-Template, eigene Felder aus Extension Attributes plus GraphQL-Mutation, und Validierung verteilt sich bewusst auf zwei Ebenen: Alpine.js für sofortiges Feedback im Browser, PHP-Plugins und GraphQL-Resolver für die verbindliche serverseitige Prüfung.

Wer den Hyvä-Checkout anpassen möchte, sollte den zentralen Alpine.store('checkout') bewusst schlank halten, CustomEvents für lose gekoppelte Kommunikation zwischen Komponenten nutzen und nach jedem Inline-Script-Block $hyvaCsp->registerInlineScript() nicht vergessen. Die Deploy-Sequenz aus Tailwind-Build, Cache-Löschung und Static-Content-Deployment bleibt dabei genauso verbindlich wie bei jeder anderen Hyvä-Anpassung. Wer sie überspringt, jagt Bugs, die in Wirklichkeit nur veraltete Caches sind. Richtig umgesetzt liefert der Hyvä-Checkout damit einen Checkout, der sich in Stunden statt Tagen erweitern lässt und dabei spürbar performanter bleibt als jede vergleichbare Knockout-Lösung.

Hyvä-Checkout Customizing. Das Wichtigste auf einen Blick

Architektur

phtml-Templates + Alpine.js statt Knockout.js + UI Components. Serverseitiges Rendering per PHP-ViewModel statt JS-Template-Kompilierung.

Eigene Steps & Felder

Layout-XML für neue Schritte, Extension Attributes plus GraphQL-Mutation für zusätzliche Felder an Quote oder Adresse.

Validierung

Alpine.js für reaktives Client-Feedback, PHP-Plugins und GraphQL-Resolver für die verbindliche Server-Validierung.

Deployment & CSP

Tailwind-Build vor Static-Content-Deploy, Caches konsequent leeren, registerInlineScript() nach jedem Inline-Script.

11. FAQ: Hyvä-Checkout Customizing

1Was unterscheidet den Hyvä-Checkout vom Standard-Luma-Checkout?
Serverseitiges Rendering per phtml und Reaktivität per Alpine.js statt Knockout.js-Databinding und UI-Components-Layout mit eigenem JS-Build.
2Brauche ich Knockout.js-Kenntnisse dafür?
Nein. PHP für ViewModels und Plugins sowie Alpine.js-Grundkenntnisse reichen vollständig aus.
3Wie füge ich einen eigenen Checkout-Step hinzu?
Layout-XML mit before/after-Positionierung registriert das Template, der Alpine-Store steuert Sichtbarkeit und Reihenfolge.
4Wie übergebe ich eigene Felder an die Quote?
Extension Attribute plus db_schema.xml, gebunden per x-model im phtml, übertragen per GraphQL-Mutation.
5Wo gehört serverseitige Validierung hin?
In den GraphQL-Resolver bei Custom-Mutationen oder in ein Plugin auf dem Quote-Repository bei Standard-Mutationspfaden.
6Wie funktioniert clientseitige Validierung mit Alpine.js?
Reaktive Fehlerzustände im x-data-Objekt, ausgelöst per @blur/@input, Submit-Button per :disabled an berechneten Getter gebunden, Ausgabe stets über x-text.
7Wie kommunizieren Komponenten miteinander?
Über den zentralen Alpine.store('checkout') für geteilten Zustand und CustomEvents für lose gekoppelte Kommunikation.
8Was ist bei der CSP zu beachten?
Jeden Inline-Script-Block mit $hyvaCsp->registerInlineScript() registrieren, externe fetch()-Ziele in csp_whitelist.xml eintragen.
9Welche Schritte gehören zur Deploy-Sequenz?
Tailwind-Build, Caches löschen, setup:static-content:deploy, cache:flush - in genau dieser Reihenfolge.
10Wie vermeide ich zu viele GraphQL-Requests?
Debounce oder @blur statt @input für jeden Tastendruck, statische Daten im Alpine-Store zwischenspeichern.