CakePHP Email Sending

On December 23, 2020
8min read
Piotr Malek Technical Content Writer @ Mailtrap

Coding in PHP, you’ve got a number of wonderful frameworks to choose from. Laravel or Symfony are certainly among the most popular ones. CakePHP may not be that famous, but it’s also garnered a lot of faithful supporters around it. Devs love it for its relatively easy learning curve, handy use of conventions, and general simplicity. To no surprise, sending emails from CakePHP is also far from complex. If you haven’t mastered it yet, our tutorial should come in handy.

Getting started

Emails are one of the core functionalities of nearly any application. You need to be able to welcome users in style. No matter how great they are, they’ll forget their passwords and will need to be reminded over and over again about failed payments. There’s a bunch of transactional emails to be implemented and it’s not hard at all to put them in place.

Disclaimer: This tutorial is valid for CakePHP versions 4.x. If you’re running an older version, consider consulting the documentation for any possible changes. 

Building a Mailer profile

There’s no email without a sender and a recipient (and a few other handful details). You could configure it for each email individually. You could also create profiles with default settings that you would use throughout your app, saving you time and making the code cleaner. You can always modify email details for individual instances if you need to.

When an application starts, configuration from app/config.php is passed into Mailer. 

'Email' => [
   'default' => [
       'transport' => 'mailtrap',
       'from' => 'you@localhost',
       /*
        * Will by default be set to config value of App.encoding, if that exists otherwise to UTF-8.
        */
       //'charset' => 'utf-8',
       //'headerCharset' => 'utf-8',
   ],
],

If you’ve already defined a configuration, you can either use the setProfile() method: 

$mailer = new Mailer();
$mailer->setProfile('default_profile');

or pass it directly to the constructor:

$mailer = new Mailer('mailtrap');

For each profile, you can configure a number of fields. These include the standard fields that we’ve already talked about (‘from’, ‘to’, ‘cc’, etc.). You can also set up a default subject line, load a custom template (more about that later), or include a default attachment with all emails.

For all available elements, please refer to the CakePHP documentation.

Additional configuration

CakePHP uses the Mailer class (CakeMailerMailer) for sending emails. First things first, let’s load it with the following:

use Cake\Mailer\Mailer;

Then, we’ll need to configure the sender and the recipients of a message. 

To populate the ‘to’, ‘cc’, and ‘bcc’ fields, we can use either the addTo or setTo method.

  • ‘addTo’ will result in recipients getting added to the existing list of recipients (if one exists). 
  • ‘setTo’, on the other hand, will overwrite the existing recipients.

The sender’s address can also be specified with the ‘setTo’ method. If you don’t set it, the default address will be used. It’s also a good idea to specify the ReturnPath address (the address bounce reports go to) with ‘setReturnPath’. It keeps your inbox clean and improves email deliverability.

Configuring a transporter

To send transactional emails, you’ll need to configure a transporter. Among the available options is PHP’s default mail() function, which we don’t recommend because of its poor performance. An alternative is setting up an SMTP server for sending or, as will be our case, testing emails. For that, we’ll use Mailtrap.

Mailtrap lets you set up a testing environment that captures your test emails, sent from Staging, Development, and QA. You can then inspect them, validate the HTML and headers, view the spam score, and others. If you haven’t yet, consider signing up for a free account.

Once you log in, pick CakePHP from the dropdown list of integrations. If you’re on CakePHP 3.7 or newer, your individual configuration code will look as follows:

In app/config.php:

[
'EmailTransport' => [
   'mailtrap' => [
       'host' => 'smtp.mailtrap.io',
       'port' => 2525,
       'username' => 'username',
       'password' => 'password',
       'className' => 'Smtp'
   ]
],
],

You can also change the config in runtime calling this command:

TransportFactory::setConfig('mailtrap', [
  'host' => 'smtp.mailtrap.io',
  'port' => 2525,
  'username' => 'f9c01f92153f78', // swap with your credentials
  'password' => 'b01d7bfc4f9ff7', // swap with your credentials
  'className' => 'Smtp'
]);

If you use an older version of CakePHP, the code will be slightly different:

Email::configTransport('mailtrap', [
  'host' => 'smtp.mailtrap.io',
  'port' => 2525,
  'username' => 'f9c02f93153d68', // swap with your credentials
  'password' => 'b00d8bfa4e9ff7',  // swap with your credentials
  'className' => 'Smtp'
]);

Copy the code into your config/app.php file. As a final step, we’ll need to tell Mailer to use our transportation method. The same as was the case with profiles, we have two similar methods:

First:

$mailer->setTransport('mailtrap');

Or second, with a constructed object:

$mailer->setTransport(new \Cake\Mailer\Transport\DebugTransport());

All in all, the example code could look like this:

$mailer = new Mailer();
$mailer->setSender('peter@mailtrap.io', 'Peter from Mailtrap');
$mailer->setReturnPath('bounces@mailtrap.io');
$mailer->setTo('kate@mailtrap.io', 'Kate');
// previous list of recipients is overridden
$mailer->addCc('john@mailtrap.io', 'John');
$mailer->addBcc('ann@mailtrap.io', 'Ann');
// John and Ann have been added on top of the existing cc and bcc recipients

Sending a test email

Now, let’s send an example email with CakePHP. It will be processed by the transporter we just set up.

$mailer = new Mailer();
$mailer->setSender('peter@mailtrap.io', 'Peter from Mailtrap')
   ->setTo('kate@mailtrap.io', 'Kate')
   ->setSubject('What do you think?')
   ->addBcc('ann@mailtrap.io', 'Ann')
   ->deliver('Hey Kate, can we sync after lunch? Peter');

Sadly, Kate won’t ever see my email, but it will appear a second later in our Mailtrap inbox.

Of course, we could send the email to multiple recipients as well:

$mailer = new Mailer();
$mailer->setSender('peter@mailtrap.io', 'Peter from Mailtrap')
   ->setTo([
     'kate@mailtrap.io' => 'Kate',
     'john@mailtrap.io' => 'John',
     'paul@mailtrap.io' => 'Paul'
   ])
   ->setSubject('What do you think?')
   ->addBcc('ann@mailtrap.io', 'Ann')
   ->deliver('Hey Kate, can we sync after lunch? Piotr');

Test Your Emails Now

Digging deeper

Using templates

Of course, we can do much better than sending plain text emails. CakePHP supports HTML templates handled by the view layer (the ‘V’ in the MVC pattern that the framework is built on). For customer-facing emails, we’ll definitely need those. 

templates/email is the designated folder in your application’s files for all CakePHP email templates. Layouts are stored in templates/layout/email.

When sending emails, we can choose to send text, HTML, or both versions of an email. The latter is definitely the best option – if the recipient’s email can’t (or won’t) handle HTML, it will attempt to display a plain text version of it. To avoid any rendering issues, it’s better to provide an option for both occasions.

We’ll use our earlier sample with the additional settings and execute it with deliver().

$mailer = new Mailer();
$mailer->
      ->setSender('peter@mailtrap.io', 'Peter from Mailtrap')
 ->setReturnPath('bounces@mailtrap.io')
      ->setEmailFormat('both')
      ->setTo('kate@mailtrap.io', 'Kate')
 ->addCc('john@mailtrap.io', 'John')
 ->addBcc('ann@mailtrap.io', 'Ann')
      ->viewBuilder()
           ->setTemplate('intro')
           ->setLayout('casual');
$mailer->deliver();


If we failed to specify the template and layout files (or simply mistyped the names), the default templates would be loaded. You can find and edit them at layout/email/text/default.php for text and layout/email/html/default.php for HTML.

Inserting dynamic fields

Putting a bit of effort into personalizing emails is often appreciated by customers. This can be a user’s name, the company they represent, or any relevant details related to their account, purchase, or others.

We can personalize an email in CakePHP by inserting the data directly into the template and the view file. We’ll use the viewVars() method for this purpose.

Let’s consider the following, simple template:

<<strong>p</strong>>Hi <?php echo $userName; ?></<strong>p</strong>>
<<strong>p</strong>>
Thanks for signing up to Mailtrap, we're happy to see you around. <<strong>br</strong>>
Here are your account details: <<strong>br</strong>>
Email address:  <?php echo $userEmail; ?> <<strong>br</strong>>
Account type:  <?php echo $accountType; ?> <<strong>br</strong>>
Any questions, <<strong>a</strong> href="mailto:contact@mailtrap.io">let us know!</<strong>a</strong>>
</<strong>p</strong>>

Now let’s prepare an array with data, name it $userFields, and use the aforementioned method to pass the data to our view.

$userFields = array(
   'userName' => 'Kate',
   'userEmail' => 'kate@mailtr.io',
   'accountType' => 'Team Plan',
);
$mailer = new Mailer();
$mailer
   ->setSender('peter@mailtrap.io', 'Peter from Mailtrap')
   ->setReturnPath('bounces@mailtrap.io')
   ->setEmailFormat('both')
   ->setTo('kate@mailtrap.io', 'Kate')
   ->addCc('john@mailtrap.io', 'John')
   ->addBcc('ann@mailtrap.io', 'Ann')
   ->setViewVars($userFields);
$viewBuilder = $mailer->viewBuilder();
$viewBuilder
   ->setTemplate('intro')
   ->setLayout('casual');
$mailer->deliver();

Adding attachments

Frequently, we also include attachments with emails. It’s useful for sending invoices, data imports, and a bazillion other use cases. We can do it with the following array:

$mailer->setAttachments([
   'invoice.pdf' => [
       'file' => '/full/path/to/invoice.php',
       'mimetype' => 'application/pdf'
   ]
]);

Naturally, we can include more than one attachment:

$mailer->setAttachments([
   'invoice.pdf' => [
      'file' => '/full/path/to/invoice.php',
      'mimetype' => 'application/pdf'
   ],
   'design.png' => [
      'file' => '/full/path/to/design.png',
      'mimetype' => 'image/png'
   ]
]);

There are several methods for adding attachments available with Mailer. Refer to the docs for more on that.

CakePHP not sending emails – what to do

There are a number of errors you may encounter when sending emails from CakePHP. 

If emails are sent, but never arrive, the sending infrastructure may be the weak point. To validate that, consider setting up Mailtrap as a transporter and shooting a few test emails. If your app successfully dispatches those emails, then everything on the server-side works just fine. The issue is with the sending method.

If this happens, consider the following: 

  • Sending from the localhost doesn’t work, in general. Try alternative methods instead.
  • If sending via the default mail() function, consider using an SMTP server instead. These emails often suffer from poor deliverability and may not show up in an inbox. This is especially true if you haven’t sent many emails from your server before.
  • Make sure you have TLS enabled when sending via Gmail and similar providers (Outlook, Thunderbird, etc.)
  • When sending via Gmail, you’ll need to also give permission for “less secure apps” to access your account (CakePHP is considered as such).

If delivering emails isn’t a problem, but the CakePHP returns an error, double check whether the templates, layout, or any attachments reside precisely where you wanted them to. Be sure to review the documentation of your respective CakePHP release. The naming of functions and classes has changed over time, so keep that in mind if you’re running an older version.

When in doubt, refer to the Cake logs (app/tmp/logs) for details.

Wrapping up

If the described methods aren’t sufficient for your needs, try other solutions for sending emails with PHP that we’ve covered for you earlier on our blog. Among them, PHPMailer and Swift Mailer are among the most popular approaches.

Thanks for reading, and best of luck!

Article by Piotr Malek Technical Content Writer @ Mailtrap