2

So I am using magento 2.4.6 and have created a PDF that customer can either download or email. The PDF is created and downlaoded successfully.
Below is my code which sends an email without PDF for now, I have tried so many different approaches but still unable to attach PDF to the email.

<?php
namespace Vendor\Module\Controller\Index;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Quote\Api\CartRepositoryInterface;
use Vendor\Module\Block\Images;
use Zend_Pdf;
use Zend_Pdf_Page;
use Zend_Pdf_Style;
use Zend_Pdf_Color_Rgb;
use Zend_Pdf_Font;
use Zend_Pdf_Image;
use Zend\Mime\Part as MimePart;
use Zend\Mime\Mime;
use Magento\Framework\App\Response\Http\FileFactory;
use Magento\Framework\Mail\Template\TransportBuilder;
use Magento\Framework\Translate\Inline\StateInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\UrlInterface;

class GeneratePdf extends Action
{
    protected $resultJsonFactory;
    protected $quoteRepository;
    protected $imagesBlock;
    protected $transportBuilder;
    protected $inlineTranslation;
    protected $storeManager;
    protected $fileFactory;
    protected $url;

    public function __construct(
        Context $context,
        JsonFactory $resultJsonFactory,
        CartRepositoryInterface $quoteRepository,
        Images $imagesBlock,
        TransportBuilder $transportBuilder,
        StateInterface $inlineTranslation,
        StoreManagerInterface $storeManager,
        FileFactory $fileFactory,
        UrlInterface $url // Add this dependency
    ) {
        $this->resultJsonFactory = $resultJsonFactory;
        $this->quoteRepository = $quoteRepository;
        $this->imagesBlock = $imagesBlock;
        $this->transportBuilder = $transportBuilder;
        $this->inlineTranslation = $inlineTranslation;
        $this->storeManager = $storeManager;
        $this->fileFactory = $fileFactory;
        $this->url = $url; // Assign it to the class property
        parent::__construct($context);
    }

    public function execute()
    {
        $quoteId = $this->getRequest()->getParam('quote_id');
        $action = $this->getRequest()->getParam('action');
    
        try {
            $quote = $this->quoteRepository->get($quoteId);
    
            $itemsByCategory = [];
            foreach ($quote->getAllItems() as $item) {
                $categoryId = $item->getCategoryId();
                $itemsByCategory[$categoryId][] = $item;
            }
    
            $pdf = new Zend_Pdf();
            $page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4);
            $pdf->pages[] = $page;
    
            // Get media URL and convert to file path
            $mediaUrl = $this->imagesBlock->getMediaUrl('porto_child/images/foodpass_logo.png');
            $baseUrl = $this->storeManager->getStore()->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA);
            $mediaPath = str_replace($baseUrl, '', $mediaUrl); // Remove base URL part
            $imagePath = BP . '/pub/media/' . ltrim($mediaPath, '/'); // BP is Magento base path
    
            if (file_exists($imagePath)) {
                $image = Zend_Pdf_Image::imageWithPath($imagePath);
                $page->drawImage($image, 30, 770, 130, 820); // Adjust coordinates as needed
            } else {
                throw new \Exception("Image file not found: " . $imagePath);
            }
    
            $style = new Zend_Pdf_Style();
            $style->setLineColor(new Zend_Pdf_Color_Rgb(0, 0, 0));
            $font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA); // Using Arial (Helvetica in Zend_Pdf)
            $style->setFont($font, 16);
            $page->setStyle($style);
            $x = 30;
            $y = 730; // Adjusted to make space for the image
    
            foreach ($itemsByCategory as $categoryId => $items) {
                $categoryName = $this->imagesBlock->getCategoryNameById($categoryId);
    
                $page->setFont($font, 16);
                $page->drawText("Store: " . $categoryName, $x, $y);
                $y -= 20;
    
                $page->setFont($font, 13);
    
                // Draw table headers
                $headers = ["Product", "Format", "Brand", "Quantity", "Price"];
                $headerX = [$x, $x + 150, $x + 250, $x + 350, $x + 450];
                foreach ($headers as $index => $header) {
                    $page->drawText($header, $headerX[$index], $y);
                }
                $y -= 20;
    
                // Draw table line
                $page->setLineWidth(0.5);
                $page->drawLine($x, $y, $x + 500, $y);
                $y -= 20;
    
                foreach ($items as $item) {
                    if ($y < 50) {
                        $page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4);
                        $pdf->pages[] = $page;
                        $page->setStyle($style);
                        $y = 750;
                    }
    
                    $itemDetail = $this->imagesBlock->getProductById($item->getProductId());
    
                    // Draw item details in table format with text wrapping
                    $productName = $item->getName();
                    $price = number_format($item->getPrice(), 2); // Format price to 2 decimal places
                    $quantity = $item->getQty();
                    $productFormatAttribute = $itemDetail->getCustomAttribute('product_format');
                    $productFormat = $productFormatAttribute ? $productFormatAttribute->getValue() : '';
                    $productBrandAttribute = $itemDetail->getCustomAttribute('product_brand');
                    $productBrand = $productBrandAttribute ? $productBrandAttribute->getValue() : '';
    
                    // Wrap text for product name if it's too long
                    $maxProductNameWidth = 150;
                    $wrappedProductName = $this->wrapText($productName, $page->getFont(), $page->getFontSize(), $maxProductNameWidth);
    
                    // Draw the wrapped text line by line
                    foreach ($wrappedProductName as $line) {
                        $page->drawText($line, $x, $y);
                        $y -= 20;
                    }
                    $page->drawText($productFormat, $x + 150, $y + 20 * count($wrappedProductName)); // Draw format at correct position
                    $page->drawText($productBrand, $x + 250, $y + 20 * count($wrappedProductName)); // Draw brand at correct position
                    $page->drawText($quantity, $x + 350, $y + 20 * count($wrappedProductName)); // Draw quantity at correct position
                    $page->drawText($price . '$', $x + 450, $y + 20 * count($wrappedProductName)); // Draw price at correct position
    
                    $y -= 20;
                }
                $y -= 20; // Extra space after each category
            }
    
            $fileName = 'quote_' . $quoteId . '.pdf';
            $filePath = 'var/' . $fileName;
    
            $directory = dirname($filePath);
            if (!is_dir($directory)) {
                mkdir($directory, 0777, true);
            }
    
            file_put_contents($filePath, $pdf->render());
    
            if ($action === 'email') {
                $this->sendEmail($quote, $filePath);
            } else {
                $this->getResponse()->setHeader('Content-type', 'application/pdf');
                $this->getResponse()->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"');
                $this->getResponse()->setBody(file_get_contents($filePath));
                return;
            }
    
            $result = $this->resultJsonFactory->create();
            return $result->setData(['pdf_url' => $this->url->getBaseUrl() . $filePath]);
        } catch (\Exception $e) {
            // Log the error message
            $result = $this->resultJsonFactory->create();
            return $result->setData(['error' => 'Error generating PDF: ' . $e->getMessage()]);
        }
    }
    
    protected function wrapText($text, $font, $fontSize, $maxWidth)
    {
        $wrappedLines = [];
        $words = explode(' ', $text);
        $line = '';
    
        foreach ($words as $word) {
            $testLine = $line ? $line . ' ' . $word : $word;
            $testWidth = $this->getTextWidth($testLine, $font, $fontSize);
    
            if ($testWidth <= $maxWidth) {
                $line = $testLine;
            } else {
                if ($line) {
                    $wrappedLines[] = $line;
                }
                $line = $word;
            }
        }
    
        if ($line) {
            $wrappedLines[] = $line;
        }
    
        return $wrappedLines;
    }
    
    protected function getTextWidth($text, $font, $fontSize)
    {
        $drawingText = iconv('UTF-8', 'UTF-16BE//IGNORE', $text);
        $characters = [];
        for ($i = 0; $i < strlen($drawingText); $i++) {
            $characters[] = (ord($drawingText[$i++]) << 8) | ord($drawingText[$i]);
        }
    
        $glyphs = $font->glyphNumbersForCharacters($characters);
        $widths = $font->widthsForGlyphs($glyphs);
        $textWidth = (array_sum($widths) / $font->getUnitsPerEm()) * $fontSize;
    
        return $textWidth;
    }

    public function sendEmail($quote, $filePath)
    {
        $writer = new \Zend_Log_Writer_Stream(BP . '/var/log/custom.log');
        $logger = new \Zend_Log();
        $logger->addWriter($writer);
        try {
            $customerEmail = $quote->getCustomerEmail();
            $this->inlineTranslation->suspend();
            $sender = [
                'name' => 'Emailname',
                'email' => '[email protected]'
            ];
            $transport = $this->transportBuilder
                ->setTemplateIdentifier('foodpass_quote_email_template')
                ->setTemplateOptions(
                    [
                        'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
                        'store' => \Magento\Store\Model\Store::DEFAULT_STORE_ID,
                    ]
                )
                ->setTemplateVars([
                    'templateVar'  => 'My Topic',
                ])
                ->setFrom($sender)
                ->addTo($customerEmail)
                ->getTransport();
                
            $transport->sendMessage();
            $this->inlineTranslation->resume();
        } catch (\Exception $e) {
            $logger->info($e->getMessage());
            $this->logger->debug($e->getMessage());
        }
    }
}

1 Answer 1

1

I have updated the code for the sendEmail function. Please review the changes and let me know if you have any questions.

public function sendEmail($quote, $filePath)
    {
        $writer = new \Zend_Log_Writer_Stream(BP . '/var/log/custom.log');
        $logger = new \Zend_Log();
        $logger->addWriter($writer);
    
        try {
            $customerEmail = $quote->getCustomerEmail();
            $this->inlineTranslation->suspend();
            $sender = [
                'name' => 'ProjectName',
                'email' => '[email protected]'
            ];
    
            // Read the file contents to attach
            $fileContents = file_get_contents($filePath);
            $fileName = basename($filePath);

            // get templateId value from config
            $templateId  = $this->scopeConfig->getValue(
            self::XML_PATH_EMAIL,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE,
            $this->storeManager->getStore()->getStoreId()
            );

            $templateVars = [
                'quote' => $quote,
                'store' => $this->storeManager->getStore()
            ];
    
            // Build the email with the template and set template variables
            $transportBuilder = $this->transportBuilder
                ->setTemplateIdentifier($templateId)
                ->setTemplateOptions(
                    [
                        'area' => \Magento\Framework\App\Area::AREA_FRONTEND,
                        'store' => \Magento\Store\Model\Store::DEFAULT_STORE_ID,
                    ]
                )
                ->setTemplateVars($templateVars)
                ->setFrom($sender)
                ->addTo($customerEmail);
    
            // Create the MIME part for the attachment
            $attachmentPart = new \Laminas\Mime\Part($fileContents);
            $attachmentPart->type = \Laminas\Mime\Mime::TYPE_OCTETSTREAM;
            $attachmentPart->disposition = \Laminas\Mime\Mime::DISPOSITION_ATTACHMENT;
            $attachmentPart->encoding = \Laminas\Mime\Mime::ENCODING_BASE64;
            $attachmentPart->filename = $fileName;
    
            // Get the transport and the message object
            $transport = $transportBuilder->getTransport();
            $message = $transport->getMessage();
    
            // Get the existing body and append the attachment
            $existingBody = $message->getBody();
            $mimeParts = $existingBody->getParts();
            $mimeParts[] = $attachmentPart;
    
            // Create the MIME message with both parts
            $mimeMessage = new \Laminas\Mime\Message();
            $mimeMessage->setParts($mimeParts);
    
            // Set the MIME message as the body of the email message
            $message->setBody($mimeMessage);
    
            // Send the email
            $transport->sendMessage();
    
            $this->inlineTranslation->resume();
        } catch (\Exception $e) {
            $logger->info($e->getMessage());
            $this->inlineTranslation->resume();
        }
    }
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.