Magento 2 – tworzenie e-maili w Magento cz. 2

Ostatnio rozmawialiśmy o tworzeniu e-maili w Magento 1. Czas na Magento 2. Nie jest tajemnicą, że Magento 2 totalnie zmieniło swoją architekturę. Jednak co do zasady wiele funkcjonalności działa podobnie. Dziś pokażę Wam jak stworzyć e-mail przypominający o konieczności dokonania płatności za złożone zamówienie.

Gdzie znajdują się szablony e-mail w Magento 2?

Magento 2 charakteryzuje się większą „kapsulacją”. Oznacza to, że jeśli będziemy szukać e-maili sprzedażowych, należałoby zajrzeć do lokalizacji

vendor/magento/module-sales/view/frontend/email

Jeżeli natomiast interesuje nas szablon e-mail formularza kontaktowego:

vendor/magento/module-contact/view/frontend/email/submitted_form.html

Jest to podstawowa różnica w stosunku do magento 1. Jeśli więc będziemy tworzyć swój indywidualny e-mail, umieścimy go w przestrzeni naszego modułu.

Jak dodać nowy szablon e-mail w Magento 2?

Załóżmy, że mamy swój moduł Namespace_Payreminder i chcemy, aby obsługiwał wysłanie naszego reminder’a. W tym celu potrzebujemy dwóch dodatkowych plików poza module.xml w katalogu etc naszego modułu, a mianowicie:

Namespace/Payreminder/etc/config.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <design>
            <email>
                <payreminder>payreminder_email_general_email</payreminder>
            </email>
        </design>
    </default>
</config>
Namespace/Payreminder/etc/email_templates.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Email:etc/email_templates.xsd">
    <template id="payreminder_email_general_email" label="Payreminder" file="payreminder.html" type="html" module="Namespace_Payreminder" area="frontend"/>
</config>

Dzięki tym dwóm plikom powiemy naszemu sklepowi skąd ma wziąć plik szablonu e-mail (email_templates.xml) i jaki będzie jego id-handler dla wywołań w skrypach. Plik email_templates.xml stanowi więc uzupełnienie dla szablonu zgłaszanego w pliku config.xml. Całość dzięki temu nabiera większej czytelności, choć dla wielu może być to skomplikowaniem czegoś co świetnie działało w Magento 1. Jednak jak się tak lepiej zastanowić, to w magento 1 w pliku config.xml modułu, można było znaleźć dosłownie wszystko – w przypadku bardziej skomplikowanych modułów bardzo zamazywało to jego czytelność. Dla mnie więc osobiście ta zmiana jest bardzo na plus.

Wracając do szablonów, aby dodać treść naszego szablonu wystarczy stworzyć plik w lokalizacji:

Namespace/Payreminder/view/frontend/email/payreminder.html

o treści:

<!--@subject Payment reminder @-->
<!--@vars {
"var formattedBillingAddress|raw":"Billing Address",
"var order.getEmailCustomerNote()":"Email Order Note",
"var order.increment_id":"Order Id",
"layout handle="sales_email_order_items" order=$order area="frontend"":"Order Items Grid",
"var payment_html|raw":"Payment Details",
"var formattedShippingAddress|raw":"Shipping Address",
"var order.getShippingDescription()":"Shipping Description"
} @-->
{{template config_path="design/email/header_template"}}
Dzień dobry,
<br />
Nie odnotowaliśmy płatności za  {{trans 'zamówienie nr #%increment_id' increment_id=$order.increment_id |raw}}
Pozdrawiamy
Twój sklep
{{template config_path="design/email/footer_template"}}

Jeśli poprawnie wszystko uzupełnicie, będziecie mogli odnaleźć i załadować swój e-mail za pomocą mechanizmu, o którym pisałam w artykule dot. e-maili tranksakcyjnych w Magento 2 i customizacji e-maili sprzedażowych.

Warto również zauważyć, że o ile wcześniej nasze e-maile leżały w konkretnym language pack’u, tak teraz możemy dokonywać tłumaczeń bezpośrednio w mailu. Służy do tego funkcja trans().

Tworzenie konfiguracji dla e-maili transakcyjnych

Podobnie jak w Magento 1 tak i w Magento 2 mamy tzw. „store configuration”. Jest to bardzo przydatna funkcjonalność do określania indywidualnych konfiguracji dla poszczególnych storów / websitów. W tym celu podobnie jak w magento1 musimy stworzyć plik system.xml.
Zmianie uległa natomiast lekko lokalizacja:

Namespace/Payreminder/etc/adminhtml/system.xml

W pliku tym umieścić należy poniższy xml:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="payreminder_email" translate="label" sortOrder="130" showInDefault="1" showInWebsite="1" showInStore="1">
            <label>Payreminder</label>
            <tab>sales</tab>
            <resource>Namespace_Payreminder::configuration</resource>
            <group id="general" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0">
                <label>General</label>
                <field id="email" translate="label comment" type="select" sortOrder="3" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Template</label>
                    <comment>Email template chosen based on theme fallback when "Default" option is selected.</comment>
                    <source_model>Magento/Config/Model/Config/Source/Email/Template</source_model>
                </field>
            </group>
        </section>
    </system>
</config>

Najważniejszą różnicą pomiędzy Magento 1, a Magento 2, która na moment mnie wstrzymała, to ID szablonu. Wcześniej tak naprawdę nie ważne było, pod jakim ID jest szablon, a pod jakim konfiguracja dla niego. Tutaj po nazwaniu pola konfiguracyjnego email otrzymałam komunikat w pliku var/log/exception.log:

Na nic czyszczenie cache, robienie upgrade i sto innych prób wyświetlenia czegoś na ekranie. Próbowałam podstawiać inne rodzaje source_model. Ku memu zdziwieniu działały. Zmieniłam ID szablonu, na to wskazane w logu, w plikach config.xml oraz emails_templates.xml i zadziałało 🙂

Podobnie jak w przypadku Magento 1, jeśli chcemy korzystać ze store config, musimy zmodyfikować nasz skrypt PHP, tak by pobierał informacje o identyfikatorze szablonu.

$transport = $this->transportBuilder->setTemplateIdentifier(
        $this->scopeConfig->getValue('payreminder_email/general/email',
        MagentoStoreModelScopeInterface::SCOPE_STORE)
    )
    ->setTemplateOptions($templateOptions)
    ->setTemplateVars($templateVars)
    ->setFrom($sender)
    ->addTo($to)
    ->getTransport();

Ustawienia ACL w Magento 2 dla konfiguracji naszego modułu

Skoro nasza konfiguracja już działa, pozostaje upewnić się, że tylko odpowiedni operatorzy (np. tylko admini) mają do niej dostęp. W tym celu tworzymy plik:

Namespace/Payreminder/etc/acl.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
    <acl>
        <resources>
            <resource id="Magento_Backend::admin">
                <resource id="Magento_Backend::stores">
                    <resource id="Magento_Backend::stores_settings">
                        <resource id="Magento_Config::config">
                            <resource id="Namespace_Payreminder::configuration" title="Payreminder" />
                        </resource>
                    </resource>
                </resource>
            </resource>
        </resources>
    </acl>
</config>

Zauważcie, że resource id jest taki sam jak w pliku: Namespace/Payreminder/etc/adminhtml/system.xml
Teraz pozostaje już tylko sprawdzić czy nasza rola się wyświetla. Znajdziemy ją w drzewie w lokalizacji:

System → Role użytkownika → Dodaj rolę → Zasoby roli

Wysyłanie e-maili w Magento 2 za pomocą PHP

Następnym krokiem jest przygotowanie skryptu shellowego dla Magento 2.

W naszym głównym katalogu projektu tworzymy plik payreminder.php z zawartością:

<?php
require_once __DIR__ . '/app/bootstrap.php';
class Payreminder implements MagentoFrameworkAppInterface
{
    protected $orderRepository;
    protected $storeManager;
    protected $scopeConfig;
    protected $inlineTranslation;
    protected $transportBuilder;
    protected $state;
    public function __construct(
        \Magento\Framework\App\State $state,
        \Magento\Sales\Api\OrderRepositoryInterface $orderRepository,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Framework\Translate\Inline\StateInterface $inlineTranslation,
        \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder
    )
    {
        $state->setAreaCode(MagentoFrameworkAppArea::AREA_GLOBAL);
        $this->orderRepository = $orderRepository;
        $this->storeManager = $storeManager;
        $this->scopeConfig = $scopeConfig;
        $this->inlineTranslation = $inlineTranslation;
        $this->transportBuilder = $transportBuilder;
    }
    public function  launch()
    {
        $order = $this->orderRepository->get(1);
        $storeId = $order->getStoreId();
        $templateOptions = array('area' => MagentoFrameworkAppArea::AREA_FRONTEND, 'store' => $storeId);
        $templateVars = array(
            'store' => $this->storeManager->getStore($order->getStoreId()),
            'order' => $order,
        );
        $this->inlineTranslation->suspend();
        $to = array($order->getCustomerEmail());
        $sender = [
            'name' => $this->scopeConfig->getValue(
                'trans_email/ident_sales/name',
                MagentoStoreModelScopeInterface::SCOPE_STORE
            ),
            'email' => $this->scopeConfig->getValue(
                'trans_email/ident_sales/email',
                MagentoStoreModelScopeInterface::SCOPE_STORE
            )
        ];
        $transport = $this->transportBuilder->setTemplateIdentifier('payreminder_email_general_email')
            ->setTemplateOptions($templateOptions)
            ->setTemplateVars($templateVars)
            ->setFrom($sender)
            ->addTo($to)
            ->getTransport();
        $transport->sendMessage();
        $this->inlineTranslation->resume();
        die();
   }
   public function catchException(
        MagentoFrameworkAppBootstrap $bootstrap, 
        Exception $exception)
   {
      echo "expcetionn";
      return true;
   }
}
$bootstrap = MagentoFrameworkAppBootstrap::create(BP, $_SERVER);
$app = $bootstrap->createApplication('Payreminder');
$bootstrap->run($app);

Skrypt nie jest trudny w zrozumieniu. To co wymaga jednak wytłumaczenia, to struktura klasy. InterfaceAppInterface wymaga od nas posiadania metody launch() i catchException(). Pierwsza z nich to idealne miejsce dla „ciała” naszego skryptu. Skrypt nie posiada pętli, która by wskazywała, które zamówienia powinny być notyfikowane. Na potrzeby tego artykułu wzięłam jedynie pod uwagę zamówienie o ID = 1.

Dzięki temu artykułowi sama czegoś się nowego nauczyłam 🙂 Podobnie jak dla Was tak i dla mnie był mój pierwszy e-mail w Magento 2. Mój pierwszy własny e-mail w Magento 2!

W następnym poście popełnię zestawienie najważniejszych kwestii dot. e-maili w Magento zarówno 1 jak i 2. Do zobaczenia 🙂