We recently started updating a project that will be re-released, with a new face, and a new back, in June. Re-released because it’s been around for many years, and until last month, it relied on an SMS modem to receive messages, and then forward them to a database to be processed. Every time we wanted to install this project in a new city, we had to run around to find a SIM card, adjust the modem’s settings if we were in a country with a different infrastructure than Canada, and make sure we found a plan that wouldn’t cost us a limb. Then came Twilio. That’s all the back story I’m going to cover for now, more on that project later.
This post does NOT cover the use of the Twilio library, because we simply haven’t had the need for it for this project. The next steps explain how to receive, and reply to SMS messages that are sent to a Twilio number, with Twilio set to make requests to a CakePHP app.
This example is intended for CakePHP 2.1, because it makes this process so easy, but a few tweaks will make it work with earlier versions.
1. Setup a Twilio number, and a TwiML application.
If you got to this point in the post, your should know what Twilio is. You can use the number that comes with the trial version, but we decided to purchase a number that we’ll keep around to test this project, and future ones.
After you purchase the number, you’ll be taken to a page where you can enter the Request URLs that Twilio will call when it receives Voice call and SMS messages. You could enter Request URLs directly here, but don’t worry about those for now, we will create a TwiML application instead. One reason why we prefer using a TwiML application is because it makes it easier to switch quickly between development urls and production urls.
While in your account, open the ‘Apps’ tab, and create a new TwiML app. You should see a field for the name, and sections for the Voice, and below, the SMS parameters. Name the app something funky, and in the SMS section, look for the field where you can enter the Request URL that Twilio will call. Our Request URL looks something like this: http://fancydomain.com/twilio/installations/receive_sms.xml
Here are the different parts, and how they relate to our CakePHP project:
- fancydomain.com – just the domain, nothing special here.
- twilio – this is optional, but we use the twilio prefix to make our code more readable.
- installations – this is our controller, yours will probably be different.
- receive_sms – this is our action that receives an SMS message, and sends a response.
- xml – last but not least, the file extension that controls the view to output the xml response.
The next few steps will cover the changes (or additions) you need to make to your CakePHP app so that each parts of the Request URL is treated correctly.
2. Setting up the Twilio prefix
This part is optional. If you remove ‘/twilio’ from the URL, you can skip this step. If you decide to not use this, just remember not to include the ‘twilio_’ prefix in your action name when you get to step 4.
All that is required is one line in your ‘app/Config/core.php’ file. Find the line that configures ‘Routing.prefixes’, and change it to the following to setup the ‘twilio’ prefix. We already had the ‘admin’ prefix so ours looks like this:
Configure::write('Routing.prefixes', array('admin', 'twilio'));
3. Parsing the file extension in the router
Another easy step, we need a single line in ‘app/Config/routes.php’ to handle the ‘.xml’ extension of our Request URL. Adding the following line — we added it at the beginning — will tell CakePHP to switch to the XmlView to render the response as XML.
Router::parseExtensions('xml');
4. Add the action
In our Request URL we specified, first, the ‘twilio’ prefix, then the ‘installations’ controller, and finally the ‘receive_sms’ action, so we need to create the following function in ‘app/Controller/InstallationsController.php’:
public function twilio_receive_sms() { //make sure we have a post request if (!$this->request->is('post')) { throw new MethodNotAllowedException(); } //find the correct installation using the twilio phone number $installation = $this->Installation->findByPhone($this->request->data['To']); if ($installation == null) { return; } $message = array( 'installation_id' => $installation['Installation']['id'], 'source' => $this->request->data['From'], 'message'=> $this->request->data['Body'], ); $this->Installation->Message->create(); if (!$this->Installation->Message->save($message)) { return; } //send response $this->set('response', array( 'Response' => array( 'Sms' => 'Thank you!', ) )); $this->set('_serialize', 'response'); }
A few notes about this code before we move on. At the top, we check to make sure the action is called from a POST request. If you specified GET instead of POST in your TwiML application, then you should check for the same method your assigned to your Request URL. Then, for our project, each installation object is matched with a Twilio number, so we find the installation with the ‘findByPhone’ method, passing it the ‘To’ field that Twilio passed to the Request URL. We then create a message array, but in this case, we only need the ‘From’ and ‘Body’ fields. If you want to know what data Twilio passes, you should read the Twilio SMS Request page in their documentation. After that, we create and save a message object linked to the correct installation. Finally we create an array for the response object, and make sure we set the ‘_serialize’ variable so that the XmlView will know to output the content of the ‘response’ variable as XML, following the instructions for the JSON and XML views of CakePHP.
5. Try it!
Give it a go. Send a text message to your Twilio number, and if you set up everything correctly, you should receive an SMS reply that says ‘Thank you!’.
can you please post the findbyPhone method and your view. It would help me to get this working.
findByPhone is a method automatically generated by CakePHP for my Installation model. I have an ‘installations’ table, with a field named ‘phone’ that stores a varchar(20). When I create an installation I assign my Twilio phone number (e.g. ).
There is no view file. You need to set your view class to Xml at the top of your controller:
var $viewClass = 'Xml';
Your app should contain an Xml layout by default in app > View > Layout > Xml > default.ctp