12

In Magento 1.x if you set this config to enabled Store > Configration > Sales > Sales Emails > General Settings > Asynchronous sending, Magento, instead of sending the email when the order is placed, adds an entry in a queue table (core_email_queue), and then a cron job comes, reads all the entries from the queue and sends the emails.

I don't find core_email_queue table in Magento 2. How does this feature work in Magento 2?

1
  • 2
    Bunyamin's answer below is excellent, so I won't add a separate answer, but to address one bit he didn't address: rather than using a core_email_queue table, Magento simply queries the order table itself for orders with the email_sent flag set to null. (The order is the "entity" referenced in the code snippet he shows.) Commented Jul 17, 2019 at 14:53

1 Answer 1

63

I had no idea how emails are sent in Magento 2, but your question took my interest so I investigated a little.

I have started my investigation by searching email keyword on cronjobs, and got the following output.

$ n98-magerun2 sys:cron:list | grep mail
| sales_send_order_creditmemo_emails                  | default       | */1  | *    | * | * | *  |
| sales_send_order_emails                             | default       | */1  | *    | * | * | *  |
| sales_send_order_invoice_emails                     | default       | */1  | *    | * | * | *  |
| sales_send_order_shipment_emails                    | default       | */1  | *    | * | * | *  |

So, there are some cronjobs which are responsible for sending emails. This shows us that they are async.

I have selected sales_send_order_emails cronjob to investigate because I can easily create a new order to test it. Then I have searched for sales_send_order_emails in the whole project, and found the following file vendor/magento/module-sales/etc/crontab.xml. It has the following cron-job entry.

<job name="sales_send_order_emails" instance="SalesOrderSendEmailsCron" method="execute">
    <schedule>*/1 * * * *</schedule>
</job>

SalesOrderSendEmailsCron is clearly a virtual type otherwise it should have a namespaces. This virtual type is defined in vendor/magento/module-sales/etc/di.xml.

<virtualType name="SalesOrderSendEmailsCron" type="Magento\Sales\Cron\SendEmails">
    <arguments>
        <argument name="emailSenderHandler" xsi:type="object">SalesOrderSendEmails</argument>
    </arguments>
</virtualType>

So the virtual type SalesOrderSendEmailsCron is actually the class Magento\Sales\Cron\SendEmails. As you can see this class has a constructor with only one argument, and it is given as SalesOrderSendEmails virtual type. This virtual type is also defined in the vendor/magento/module-sales/etc/di.xml. Here is the type definition:

<virtualType name="SalesOrderSendEmails" type="Magento\Sales\Model\EmailSenderHandler">
    <arguments>
        <argument name="emailSender" xsi:type="object">Magento\Sales\Model\Order\Email\Sender\OrderSender</argument>
        <argument name="entityResource" xsi:type="object">Magento\Sales\Model\ResourceModel\Order</argument>
        <argument name="entityCollection" xsi:type="object" shared="false">Magento\Sales\Model\ResourceModel\Order\Collection</argument>
    </arguments>
</virtualType>

So the real mail sender is Magento\Sales\Model\EmailSenderHandler. If you open this file, you will see the following method:

public function sendEmails()
{
    if ($this->globalConfig->getValue('sales_email/general/async_sending')) {
        $this->entityCollection->addFieldToFilter('send_email', ['eq' => 1]);
        $this->entityCollection->addFieldToFilter('email_sent', ['null' => true]);

        /** @var \Magento\Sales\Model\AbstractModel $item */
        foreach ($this->entityCollection->getItems() as $item) {
            if ($this->emailSender->send($item, true)) {
                $this->entityResource->save(
                    $item->setEmailSent(true)
                );
            }
        }
    }
}

If you set sales_email/general/async_sending config as true, then this cronjob will be sending mails. It also has the following filters to fetch orders.

$this->entityCollection->addFieldToFilter('send_email', ['eq' => 1]);
$this->entityCollection->addFieldToFilter('email_sent', ['null' => true]);

As a result, you have send_email and email_sent attributes in your sales_order table. If send_email is set to 1 for an order, this cronjob should send an email for the order. If email_sent is not NULL, then it has already done.

1
  • 10
    great explanation! Commented Apr 12, 2018 at 11:35

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.