CustomFields in Mail Template verwenden

Hallo zusammen,

Ich komme gerade nicht weiter und bin um jede Hilfe froh:

  • Kontext: Bei jeder Bestellung wählt der Kunde ein Lieferdatum aus. Dieses speichere ich als customField in der ‚order_delivery‘ DB (siehe screenshot). Das ganze funktioniert einwandfrei und ich kann das customField problemlos im Frontend verwenden. Aber…
  • Problem: Ich möchte dieses Feld nun im Mail-Template der Bestellbestätigung verwenden mittels {{ order.deliveries.first.customFields.deliveryDate }}. Das führt dazu, dass keine Bestellbestätigung mehr verschickt wird, da es das customField wahrscheinlich nicht findet und deshalb irgendwo abbricht…
  • Frage: Es sollte schon grundsätzlich möglich sein customFields in Mail-Templates zu verwenden? Falls ja, mache ich irgenwas mit der twig-syntax falsch?

Unten noch ein Screenshot des Mail-Templates und das customField in der Datenbank:

Update: Habe das Problem gefunden. Das customField war nicht verfügbar, da es zum Zeitpunkt, an dem der MailerService auf die ‚order‘ zugreift noch nicht geladen wurde. Musste es jetzt ziemlich umständlich lösen, indem ich den CartService extende.

Thread kann geschlossen werden.

 

Zur Ergänung hier noch der entsprechende log-Eintrag:

app.ERROR: Could not render Mail-Template with error message: Failed rendering string template using Twig: Impossible to access an attribute ("deliveryDate") on a null variable [...]

 

Hi @Shopware6User‍,

ich strehe grade vor dem selben Problem. Kannst du deine Lösung evtl. hier teilen?

Viele Grüße

Hat hier inzwischen jemand Erfahrungen mit gemacht?

Wir erstellen das Custom Field in der Install Methode so:

$customFieldSetRepository = $this->container->get('custom_field_set.repository');

        $customFieldSetRepository->create([
            [
                'name' => 'myfield_set',
                'global' => true,
                'config' => [
                    'label' => [
                        'de-DE' => 'xxx',
                        'en-GB' => 'xxx'
                    ]
                ],
                'relations' => [[
                    'entityName' => 'order'
                ]],
                'customFields' => [
                    ['name' => 'myfield', 'type' => CustomFieldTypes::DATETIME],
                ]
            ]
        ], Context::createDefaultContext());

Wie gehe ich jetzt am besten vor, um das Feld in die Mail-Templates zu bekommen?

 

@Shopware6User‍ Wie hast du es denn nun gemacht?

*push*

Hat hier jemand inzwischen neue Infos?

Push! Würde uns auch interessieren

 

Hallo zusammen, ich bin hier noch eine Antwort schuldig:

Vorneweg: Ich konnte für dieses Problem keine saubere Lösung finden. Die unstenstehende Lösung ist nicht empfehlenswert und sehr anfällig für zukünftige Updates , da es interne Klassen überschreibt!

Ich überschreibe den OrderService mit folgendem:

public function createOrder(DataBag $data, SalesChannelContext $context): string
    {
        $c = new \ReflectionClass(OrderService::class);
        $validateOrderDataMethod = $c->getMethod('validateOrderData');
        $addCustomerCommentMethod = $c->getMethod('addCustomerComment');
        $addAffiliateTrackingMethod = $c->getMethod('addAffiliateTracking');
        $validateCartMethod = $c->getMethod('validateCart');

        $validateOrderDataMethod->setAccessible(true);
        $addCustomerCommentMethod->setAccessible(true);
        $addAffiliateTrackingMethod->setAccessible(true);
        $validateCartMethod->setAccessible(true);


        // get additional data from request object
        $customFields = $data->only('deliveryDate');

        $validateOrderDataMethod->invoke($this, $data, $context);

        $cart = $this->cartService->getCart($context->getToken(), $context);
        $addCustomerCommentMethod->invoke($this, $cart, $data);
        $addAffiliateTrackingMethod->invoke($this, $cart, $data);

        $validateCartMethod->invoke($this, $cart, $context->getContext());
        // pass customField as parameter
        return $this->cartService->order($cart, $context, $customFields);
    }

Und den  CartService :

    public function order(Cart $cart, SalesChannelContext $context, Array $customFields = null): string
    {
// dd($customFields);
        $orderId = $this->orderRoute->order($cart, $context, $customFields)->getOrder()->getId();

        if (isset($this->cart[$cart->getToken()])) {
            unset($this->cart[$cart->getToken()]);
        }

        return $orderId;
    }

Vielleicht hilfts ja einigen von euch.  @kommad, @codeiverse, @kanuma, @Schmuh

Danke für das Feedback @Shyim‍ ?

Ihr könnt euch eigentlich an das Event \Shopware\Core\Checkout\Cart\Order\CartConvertedEvent subscriben und die Daten aus dem Cart um mappen in die Order. Dann sollte es im OrderPlacedEvent auch zur verfügung stehen.

1 Like

Hi zusammen,

wenn ich es mache im OrderSubscriber:

public static function getSubscribedEvents(): array
    {
        return[
            CheckoutOrderPlacedEvent::class => 'onOrderPlaced',
        ];
    }

    /**
     * @param CheckoutOrderPlacedEvent $event
     * @throws \Exception
     */
    public function onOrderPlaced(CheckoutOrderPlacedEvent $event)
    {

        $orderId = $event->getOrder()->getId();
        $context = $event->getContext();

        $deliverydate = $this->session->get('deliverydate', null);

        if($deliverydate) {
            $this->orderRepository->upsert([[
                'id' => $orderId,
                'customFields' => ['dtgs_delivery_date' => $deliverydate]
            ]], $context);
        }

        $existingCustomFields = $event->getOrder()->getCustomFields();
        if(!$existingCustomFields) $existingCustomFields = [];
        //add delivery date
        $deliverydateCustomFields = [
            'deliveryDate' => $deliverydate
        ];
        $mergedCustomFields = array_merge($existingCustomFields, $deliverydateCustomFields);
        $event->getOrder()->setCustomFields($mergedCustomFields);

        //unset session key
        $this->session->remove('deliverydate');

    }

Ist mein Datum in der Bestellbestätigung verfügbar über {{ order.customFields.deliveryDate }}

In den anderen Mailtemplates muss ich aber {{order.customFields.dtgs_delivery_date}} verwenden und es ist dort auch anders formatiert. Warum ist das so? Bzw. war es so schon die ganze Zeit dort verfügbar?

Und kann mir noch jemand einen Hinweis geben, wie ich das Datum in den Lieferscheinen unterbringen kann?

Wie hast du es denn hinbekommen, dass das Datumsfeld in den Bestellungen bearbeitbar ist?

Bei mir ist weiterhin das Feld „Read-Only“ ohne Bearbeitungsmöglichkeiten.

Werden die Custom Fields in der DB gefüllt wenn die Bestellung erstellt wird?
In dem Fall wird das Mail Template nicht darauf zugreifen können, da es zu spät passiert sodass die Informationen für den Mailer nicht zur Verfügung stehen.

Generell für den Anwendungsfall, dass man Custom Fields von Bestellungen für die Bestellbestätigungs-Email verwenden will sollte man dafür am besten über das CartConvertedEvent die custom Fields direkt mitgeben, sodass diese im Konvertierungsprozess von Cart zu Order schon gleich übernommen werden.

In dieser Beispiel Funktion rufe ich in einem Subscriber eine Kunden Referenz aus einem Textfeld ab und füge diese als Custom Field Key in das Cart ein.

public function onCartConvertedEvent(CartConvertedEvent $event): void {

    $customerReference = $this->requestStack->getCurrentRequest()->request->get('customer- reference');

    $cart = $event->getConvertedCart();
    $cart['customFields']['customer_reference'] = $customerReference;

    $event->setConvertedCart($cart);
}

Danach kann ich den Wert von „customer_reference“ in dem Mail Template zur Bestellbestätigung unter
order.customFields.customer_reference
abrufen.

Ich bedanke mich an dieser Stelle bei meiner Kollegin die mir bei diesem Ansatz geholfen hat.
Wir hatten zuvor das gleiche Problem bezüglich der Bestellbestätigung und konnten es erst nach viel Recherche und rumprobieren lösen.

Ich hoffe diese Antwort kann auch einigen anderen hier weiterhelfen.

2 Likes

Hi @Ni_Bo, könnten Sie mir bitte sagen, in welcher Datei Sie das geändert/hinzugefügt haben? Ich habe es in der CartConvertedEvent.php hinzugefügt aber leider werden mir meine Custom Fields immer noch nicht im E-Mail Template angezeigt…

Nein, nicht in der CartConvertedEvent.php
Der Code muss in einem eigenen Subscriber implementiert werden.
Bzgl. Implementieren von Subscribern in einem eigenen Plugin: https://developer.shopware.com/docs/guides/plugins/plugins/plugin-fundamentals/listening-to-events.
Die Funktion wird dann vom Subscriber aufgerufen wenn das Event ausgelöst wird.

Dazu ist es aber notwendig in der getSubscribedEvents Methode des Subscribers eine Referenz auf die Methode einzubinden:

public static function getSubscribedEvents(): array {
        return [
            CartConvertedEvent::class => 'onCartConvertedEvent'
        ];
    }

Damit weiß der Subscriber welche Methode aufgerufen werden soll, wenn das Event ausgelöst wird.

1 Like

@Ni_Bo Vielen Dank für die Schnelle Antwort! Ich werde das dann mal ausprobieren.

Ich bin noch etwas unerfahren und wir haben folgendes Problem:
Wir haben Zusatzfelder für Produkte erstellt und diese sollen bei der Bestellung eben mit übermittelt werden und auch in der Bestellbestätigung angezeigt werden. Hilft Ihre Lösung mir dabei? oder ist Ihre Lösung für Felder in der Bestellbestätigung?

@der-bergmann Also das Anzeigen der CustomFields von Produkten ist durchaus möglich.
Dabei kann der Ansatz in der oben beschriebenen Lösung grundsätzlich verwendet werden, aber die Umsetzung ist etwas komplexer.

Ich habe hier mal auf die Schnelle die Funktion angepasst um den Anwendungsfall ungefähr abzudecken zur Veranschaulichung:

public function onCartConvertedEvent(CartConvertedEvent $event): void {
    
    $customFieldName = 'name_deines_customfields';
    $originalCart = $event->getCart();
    $cart = $event->getConvertedCart();
    $lineItems = $cart['lineItems'];
    $referencedCartLineItems = $originalCart->getLineItems();
    
    foreach ($lineItems as $index => $lineItem) {
        $id = $lineItem['identifier'];
        $referencedLineItem = $referencedCartLineItems->get($id);
        if ($referencedLineItem != NULL) {
            $referencedPayload = $referencedLineItem->getPayload();
            $deinCustomField = $referencedPayload[$customFieldName];
            $cart['lineItems'][$index]['payload']['customFields'][$customFieldName] = $deinCustomField;
        }
    }

    $event->setConvertedCart($cart);
}

Damit kann man CustomFields der Produkte im Warenkorb in den Mailtemplates theoretisch auslesen.
Ich kann aber nicht garantieren, dass der Code ohne Anpassungen problemlos funktioniert, da ich ihn teils aus meiner Erinnerung gerade geschrieben und nicht getestet habe.

Hoffentlich hilft das weiter!

1 Like

@Ni_Bo super vielen lieben Dank!

@Ni_Bo müssen die CustomFields im Warenkorb sichtbar sein, damit man diese über order.customFields.mein_custom_field auslesen kann im Mail Template? Denn bis jetzt sind die Custom Fields nur auf der Produkt-Detailseite im Frontend sichtbar…

Nein sie müssen nicht unbedingt sichtbar sein.
Die CustomFields die sich auf Produkte beziehen kann man in den Mail Templates durch die LineItems wiedergeben.
So in etwa:

{% for lineItem in order.lineItems %}
    {{ lineItem.payload.customFields.dein_custom_field }}
{% endfor %}
1 Like