Files
2026-09-15 10:35:56 +08:00

151 lines
8.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Esun\EmailPreview\Model;
use Magento\Email\Model\TemplateFactory;
use Magento\Framework\App\Area;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Payment\Helper\Data as PaymentHelper;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Address\Renderer as AddressRenderer;
use Magento\Sales\Model\OrderFactory;
use Magento\Sales\Model\ResourceModel\Order\Creditmemo\CollectionFactory as CreditmemoCollectionFactory;
use Magento\Sales\Model\ResourceModel\Order\Invoice\CollectionFactory as InvoiceCollectionFactory;
use Magento\Sales\Model\ResourceModel\Order\Shipment\CollectionFactory as ShipmentCollectionFactory;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
class PreviewService
{
public function __construct(
private readonly StoreManagerInterface $storeManager,
private readonly ScopeConfigInterface $scopeConfig,
private readonly TemplateFactory $templateFactory,
private readonly TransportBuilder $transportBuilder,
private readonly OrderFactory $orderFactory,
private readonly ShipmentCollectionFactory $shipments,
private readonly CreditmemoCollectionFactory $creditmemos,
private readonly InvoiceCollectionFactory $invoices,
private readonly AddressRenderer $addressRenderer,
private readonly PaymentHelper $paymentHelper
) {}
public function render(array $request): array
{
[$identifier, $variables, $storeId] = $this->prepare($request);
$template = $this->templateFactory->create();
$template->setDesignConfig(['area' => Area::AREA_FRONTEND, 'store' => $storeId]);
$template->setId($identifier);
$template->setVars($variables);
$html = $template->processTemplate();
return ['html' => $html, 'subject' => $template->getProcessedTemplateSubject($variables)];
}
public function send(array $request, string $recipient): void
{
if (!filter_var($recipient, FILTER_VALIDATE_EMAIL)) {
throw new LocalizedException(__('Enter a valid test recipient email address.'));
}
[$identifier, $variables, $storeId] = $this->prepare($request);
$this->transportBuilder
->setTemplateIdentifier($identifier)
->setTemplateOptions(['area' => Area::AREA_FRONTEND, 'store' => $storeId])
->setTemplateVars($variables)
->setFromByScope('general', $storeId)
->addTo($recipient)
->getTransport()->sendMessage();
}
private function prepare(array $request): array
{
$type = (string)($request['type'] ?? '');
if (!isset(TemplateTypes::TYPES[$type])) {
throw new LocalizedException(__('Select a supported email type.'));
}
$storeId = (int)($request['store_id'] ?? 0);
$store = $this->storeManager->getStore($storeId);
$definition = TemplateTypes::TYPES[$type];
$identifier = '';
$variables = [];
if (in_array($type, ['order', 'order_update', 'invoice', 'invoice_update', 'shipment', 'shipment_update', 'creditmemo', 'creditmemo_update'], true)) {
$order = $this->loadOrder((string)($request['increment_id'] ?? ''), $storeId);
if ($identifier === '') {
$default = $order->getCustomerIsGuest() && isset($definition['guest']) ? $definition['guest'] : $definition['template'];
$configPath = $order->getCustomerIsGuest() ? str_replace('/template', '/guest_template', $definition['config']) : $definition['config'];
$identifier = (string)($this->scopeConfig->getValue($configPath, ScopeInterface::SCOPE_STORE, $storeId) ?: $default);
}
$variables = $this->orderVariables($order);
$variables['comment'] = 'This is a preview of the latest customer notification.';
if (in_array($type, ['invoice', 'invoice_update'], true)) {
$entity = $this->invoices->create()->setOrderFilter($order)->setOrder('entity_id', 'DESC');
$number = trim((string)($request['entity_id'] ?? ''));
if ($number !== '') { $entity->addFieldToFilter('increment_id', $number); }
$invoice = $entity->getFirstItem();
if (!$invoice->getId()) { throw new LocalizedException(__('No invoice was found for this order.')); }
$variables += ['invoice' => $invoice, 'invoice_id' => $invoice->getId()];
} elseif (in_array($type, ['shipment', 'shipment_update'], true)) {
$entity = $this->shipments->create()->setOrderFilter($order)->setOrder('entity_id', 'DESC');
$number = trim((string)($request['entity_id'] ?? ''));
if ($number !== '') { $entity->addFieldToFilter('increment_id', $number); }
$shipment = $entity->getFirstItem();
if (!$shipment->getId()) { throw new LocalizedException(__('No shipment was found for this order.')); }
$variables += ['shipment' => $shipment, 'shipment_id' => $shipment->getId(), 'comment' => ''];
} elseif (in_array($type, ['creditmemo', 'creditmemo_update'], true)) {
$entity = $this->creditmemos->create()->setOrderFilter($order)->setOrder('entity_id', 'DESC');
$number = trim((string)($request['entity_id'] ?? ''));
if ($number !== '') { $entity->addFieldToFilter('increment_id', $number); }
$creditmemo = $entity->getFirstItem();
if (!$creditmemo->getId()) { throw new LocalizedException(__('No credit memo was found for this order.')); }
$variables += ['creditmemo' => $creditmemo, 'creditmemo_id' => $creditmemo->getId(), 'comment' => ''];
}
} else {
if ($identifier === '') {
$identifier = (string)($this->scopeConfig->getValue($definition['config'], ScopeInterface::SCOPE_STORE, $storeId) ?: $definition['template']);
}
$customer = new DataObject([
'id' => 0, 'firstname' => 'Preview', 'lastname' => 'Customer',
'name' => 'Preview Customer', 'email' => 'customer@example.com',
'rp_token' => 'preview-only-token'
]);
$customer->setData('confirmation', 'preview-confirmation-key');
$variables = ['store' => $store, 'customer' => $customer, 'back_url' => ''];
}
return [$identifier, $variables + $this->storeVariables($storeId), $storeId];
}
private function loadOrder(string $incrementId, int $storeId): Order
{
if (trim($incrementId) === '') { throw new LocalizedException(__('Enter an order number for this email type.')); }
$order = $this->orderFactory->create()->loadByIncrementId(trim($incrementId));
if (!$order->getId() || (int)$order->getStoreId() !== $storeId) { throw new LocalizedException(__('The order was not found in the selected Store View.')); }
return $order;
}
private function orderVariables(Order $order): array
{
return [
'order' => $order, 'order_id' => $order->getId(), 'billing' => $order->getBillingAddress(),
'payment_html' => $this->paymentHelper->getInfoBlockHtml($order->getPayment(), (int)$order->getStoreId()),
'store' => $order->getStore(),
'formattedShippingAddress' => $order->getIsVirtual() ? '' : $this->addressRenderer->format($order->getShippingAddress(), 'html'),
'formattedBillingAddress' => $this->addressRenderer->format($order->getBillingAddress(), 'html'),
'created_at_formatted' => $order->getCreatedAtFormatted(2),
'order_data' => new DataObject(['customer_name' => $order->getCustomerName(), 'is_not_virtual' => $order->getIsNotVirtual(), 'email_customer_note' => $order->getEmailCustomerNote(), 'frontend_status_label' => $order->getFrontendStatusLabel()])
];
}
private function storeVariables(int $storeId): array
{
return [
'store_email' => (string)$this->scopeConfig->getValue('trans_email/ident_general/email', ScopeInterface::SCOPE_STORE, $storeId),
'store_phone' => (string)$this->scopeConfig->getValue('general/store_information/phone', ScopeInterface::SCOPE_STORE, $storeId),
'store_hours' => (string)$this->scopeConfig->getValue('general/store_information/hours', ScopeInterface::SCOPE_STORE, $storeId),
];
}
}