dotMailer is an email marketing provider that handles all of the templating and sending of emails, as well as the reporting and statistics that are generated as a result. Their service is excellent and their systems provide various solutions to send emails to tailored address books based on recipients attributes. They also have an extensive API that provides much of what can be done on the dotMailer site and is quite easy to integrate with.
There have been Drupal projects in past at Access that have had small component that integrated into dotMailer. Although these components worked well they weren't built with code reuse in mind and as such needed tweaking when they were used again. With our biggest dotMailer integration in a recent project we decided to try and encapsulate this code into a class so that it could be dropped into any project and used without any problems.
After doing some research we realised that there are no userland code examples that encapsulate dotMailer into a PHP class. The dotMailer WordPress plugin (created by dotMailer) had a small PHP class but this only covered a couple of the API calls we needed. So we set out creating out own integration class that would exist outside any Drupal code. We would then include this class into the project we were working on and create some Drupal integration points.
The dotMailer API works through a series of SOAP calls that push data to and from the dotMailer servers. Any API call that needs to interact with data requires a username and password to be sent as parameters. These credentials are special 'API only' credentials and can be obtained from the dotMailer website; you'll need to have an account to apply for them though. We took the decision only to integrate with the API calls that we needed as there was little point implementing stuff that wouldn't be needed for the project at hand. If we did need anything more in the future we could easily extend the class to provide that functionality.
The class was created and developed using test driven development using PHPUnit as the testing framework. It made sense to do it this way so that we could make sure the code worked as expected and was able to adapt correctly to different parameters and situations. A small amount of refactoring was done half way through the writing of the class to standardize the SOAP calls into a single method, which was greatly sped up by the fact that unit tests had been written for it.
Those of you who are familiar with Drupal will wonder why we used PHPUnit instead of the SimpleTest framework that is built into Drupal. The simple (no pun intended) reason is that the class was written independently of Drupal before being incorporated into a module. The idea of creating a reusable and independent component class for PHP was paramount and we didn't want it to be tied to a particular system. We also had the intention of releasing this code to the public so that other people might benefit from (or even contribute to) the code.
A good place to start with any API is with a heartbeat function. This is something that (usually) doesn't need authentication, and dotMailer provides this in the form of the GetServerTime method. This returns a result object that contains the current server time. Here is some example PHP code that calls this SOAP method.
1 2 3 4 5 | <?php $request_url = 'http://apiconnector.com/api.asmx?WSDL'; $client = new SoapClient($request_url); $result = $client->GetServerTime()->GetServerTimeResult; print $result; |
This produces the following result:
2012-06-29T13:28:48.9154394Z
As most of the SOAP calls required a username and password to be passed as variables these were incorporated into the constructor of the class. It is only the GetServerTime method that doesn't use them so it was only this method that would act slightly differently. The following is a section of the DotMailer PHP class showing the constructor, the method used to call the API and a couple of methods of interacting with the API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | <?php class DotMailer { private $request_url = 'http://apiconnector.com/api.asmx?WSDL'; private $username; private $password; private $client; private $lastFault = false; /** * Set up the SOAP client. * * @param string $username The account username. * @param string $password The account password. * * @throws UsernameAndPasswordNotFoundException */ public function __construct($username, $password) { if ($username == '' && $password == '') { throw new UsernameAndPasswordNotFoundException(); } $this->username = $username; $this->password = $password; $this->client = new SoapClient($this->request_url); } /** * Get the last SOAP fault. * * @return mixed False if no fault found, otherwise returns SoapFault object. */ public function getLastFault() { return $this->lastFault; } /** * Given a method and an array of parameters make a SOAP call using the client. * * Returns false if an error occurred whilst processing the request. If an * error did occurr then the $lastFault parameter is filled in with the * error response from dotMailer. * * @param string $method The SOAP method to be called. * @param array $parameters An array of paramters. * * @return mixed The result of the SOAP call. */ protected function makeSoapCall($method, $parameters) { $this->lastFault = false; try { return $this->client->$method($parameters); } catch (SoapFault $fault) { $this->lastFault = $fault; return false; } } /** * Get the current server time from dotmailer. * * @return string The time */ public function GetServerTime() { $result = $this->makeSoapCall('GetServerTime', array()); if ($result === false) { return false; } return $result->GetServerTimeResult; } /** * List all of the address books. * * @return array A list of address books available. */ public function ListAddressBooks() { $parameters = array( 'username' => $this->username, 'password' => $this->password ); $result = $this->makeSoapCall('ListAddressBooks', $parameters); if ($result === false) { return false; } return $result->ListAddressBooksResult->APIAddressBook; } } |
Each of the method names in this class are the same as the API method it is calling. The rest of the class can be found on github. Feel free to use it in your own projects or create pull requests for bugs or new features.
To use this class you would do something like the following. The ListAddressBooks method returns an array of objects that contain the address book information, we are just printing out the names here.
1 2 3 4 5 | $dotmailer = new DotMailer('username', 'password'); $addressBooks = $dotmailer->ListAddressBooks(); foreach ($addressBooks as $addressBook) { echo $addressBooks->Name . PHP_EOL; } |
The PHPUnit test class has been included with the repository, but it takes a little bit of setup in order to get them working correctly so I thought I would detail that here. When you sign up to the dotMailer API you will be given a username and password, which are required to use the service. I didn't want to hard code these variables into the dotMailer test class so I used a PHPUnit configuration file to pull in these values at run time. If you create a file called phpunit.xml in your test directory then PHPUnit will automatically pick it up and read it for configuration options. This file is also used to store a test address book and campaign ID and a blank version has been included in the repository to help when setting things up.
1 2 3 4 5 6 7 8 | <phpunit> <php> <var name="username" value=""/> <var name="password" value=""/> <var name="addressBookId" value="" /> <var name="campaignId" value="" /> </php> </phpunit> |
To run the unit tests just enter the test directory and type the following command.
phpunit DotMailerTest.php
When you setup dotMailer you will be given a Test address book that you can use to test campaigns on before they get sent out. For some reason you can't add contacts to this address book via the API, you can only do this via the website itself. If you find that your tests have a lot of failures in them then make sure you haven't entered this test address book.