Handle Symfony Events with Twilio SMS
Time to read: 7 minutes
Events are an excellent way of building richer, more flexible, and more maintainable applications. Why? Because when events are dispatched, any number of subscribers (often referred to as listeners) can respond asynchronously. Subscribers are not dependent on each other, so they can be added and removed as and when the need arises.
Consider a stereotypical web application, perhaps a CMS of some kind. In that application, a user might be able to register for a new account. When they do that, several processes need to happen. These might include creating one or more database entries for the new user, and sending them a confirmation email so that they know that their account has been created.
In a non-event-aware application, each of these processes would be executed one after another, such as within a controller function. While this can work well, if one of the processes fails, none of the subsequent processes will likely execute, leading to a poor user experience.
When using events, however, each of the respective processes can be encapsulated within a separate class and executed autonomously of any other. Given this, subscribers are easier to test and maintain. They could also be reused throughout the application.
I hope that piqued your curiosity. Because in this tutorial, you'll learn how to use Symfony's EventDispatcher Component to respond to events in a Symfony application by sending SMS messages powered by Twilio's SMS API.
Prerequisites
To follow along with this tutorial, you will need the following:
- Prior experience with both PHP and Symfony
- PHP 8.1 or 8.2 with the intl, ctype, and iconv extensions.
- The Symfony CLI tool
- A Twilio account (free or paid). If you are new to Twilio, click here to create a free account
- A mobile phone number that can receive SMS
What is Symfony's EventDispatcher Component
Quoting the official documentation:
The EventDispatcher component provides tools that allow your application components to communicate with each other by dispatching events and listening to them. The Symfony EventDispatcher component implements the Mediator and Observer design patterns.
Paraphrasing the documentation a little, here is the essence of what happens when using the component:
- A listener (PHP object) tells a central dispatcher object that it wants to listen to one or more events;
- At some point during the application’s lifecycle, one or more of those events are dispatched, passing with them an
Event
object; - The dispatcher notifies (i.e., calls a method on) all listeners of those events, allowing each of them to respond in their own way.
In addition to Listeners, Symfony supports Subscribers. In contrast to Listeners who are told that an event has been dispatched, Subscribers can tell the Dispatcher which events they want to listen to. The code in this tutorial will use Subscribers.
A quick application overview
With the essence of the component in mind, here is how the application you’re going to build will work.
- A single subscriber,
UsageRecordsRetrievedSMSSubscriber
, will tell the dispatcher that it wants to listen for theusage.records.retrieved
event; - When the sole application route is requested, your account usage details for the last month will be retrieved and displayed in JSON format, and the
usage.records.retrieved
event will be dispatched; UsageRecordsRetrievedSMSSubscriber
will then respond to the event, calculating the total cost for those usage records before sending an SMS with that amount;
Check that your development environment meets Symfony’s minimum requirements
Before scaffolding a new Symfony application, use Symfony’s CLI tool to check that your development environment meets Symfony’s minimum requirements, by running the following command in the terminal.
If it does, you will see the following printed to the terminal:
Scaffold a new Symfony application
Next, scaffold a minimal Symfony application, named sms-event-handler, and change into the newly scaffolded application’s directory, by running the following commands.
With the application scaffolded, check that it works. First, start up the application by running the following commands.
Then, open http://127.0.0.1:8000 in your browser of choice, where it should look like the screenshot below.
Now that you know it’s working, in the terminal, press Ctrl+C to stop the application.
Install the required dependencies
Now, it's time to install the three third-party dependencies that the application needs. These are
- Twilio’s PHP Helper Library, which reduces the effort required to send SMS with Twilio;
- Symfony’s EventDispatcher Component
- Symfony’s MakerBundle, which simplifies the creation of the default controller;
To install them, run the following commands in your terminal
Set the required environment variables
The next thing that you need to do is to set four environment variables; these are:
- Your Twilio Account SID and Auth Token, required for making authenticated requests to Twilio’s APIs
- Your Twilio phone number to send the SMS from
- The phone number to receive the SMS from the application
To do that, at the bottom of .env, add the following configuration:
Next, retrieve your Twilio credentials and phone number. To do that, from the Twilio Console's Dashboard, copy your Account SID, Auth Token, and phone number and paste them in place of the respective placeholders for TWILIO_ACCOUNT_SID
, TWILIO_AUTH_TOKEN
, and TWILIO_PHONE_NUMBER
in .env. Finally, replace the placeholder for SMS_RECIPIENT_NUMBER
with your mobile phone number.
Now, it’s time to write some code; not much, just two classes.
Create the Event class
Create a new directory in src named EventSubscriber. Then, in that directory, create a new PHP file named UsageRecordsRetrievedEvent.php and paste the code below into it.
This class extends Symfony\Contracts\EventDispatcher\Event
, which is the base class for containing event data. The class constant (NAME
) defines the event name to dispatch and which subscribers will subscribe to.
The constructor takes an array of account usage information ($usageRecords
) and provides a getter method, getUsageRecords()
, for subscribers to retrieve that information as required.
Create the Subscriber class
Now, for the subscriber class. In src/EventSubscriber, create a new PHP file named UsageRecordsRetrievedSMSSubscriber.php, and paste the code below into the file.
The class constructor takes three arguments:
- A Twilio Client object (
$client
) for interacting with Twilio’s SMS API - The Twilio phone number to send the SMS from (
$smsSender
) - The phone number of the recipient (
$smsRecipient
)
It then defines the getSubscribedEvents()
function. This function specifies which events the class responds to, the method to invoke when responding to a given event, and the subscriber priority.
This final point is worth discussing a bit further. As an event can have any number of subscribers, the priority tells the application the order in which the subscribers react to the dispatched event.
Next, the usageRecordsRetrieved()
function receives a UsageRecordsRetrievedEvent
object ($event
). It then uses that information to determine the total cost of sending the messages, formats that amount in the specified currency, and sends it in an SMS using the $client
object.
Lastly comes the getUsageTotal()
function. This function uses a combination of three, built-in, PHP functions to retrieve the total cost of sending the messages in $usageRecords
. These are:
- array_column: This extracts the cost column from all of the arrays in
$usageRecords
- array_keys: This strips out any information other than the cost
- array_sum: This sums the extracted values
I found these three built-in functions to be an excellent alternative to for
or foreach
loops, or using a combination of iterators, such as FilterIterator. Would you have used them as well?
After that, NumberFormatter::formatCurrency() formats the returned value as a currency, so that it reads as you’d expect.
Update the application’s configuration
Now, it’s time to configure the application. Add the following to the bottom of config/services.yaml, in the services
section:
This configuration adds a service definition for the Twilio Client
class, so that it can be provided to UsageDataController
during instantiation, and tells the Service Container what to supply to UsageRecordsRetrievedSMSSubscriber
for the $smsSender
and $smsRecipient
constructor parameters.
Create the default controller
It’s now time to create a minimalist controller to retrieve the account usage information and to dispatch the usage.records.retrieved
event.
To do that, run the command below.
This will create a new file in src/Controller, named UsageDataController.php. Replace the file’s existing content with the following code.
The class constructor takes a Twilio Client
and an object that implements EventDispatcherInterface
. Then, the index()
function uses $client
to retrieve up to 20 records sent in the last month, storing them in $lastMonth
.
It then iterates over the retrieved records and stores several properties in a new array ($usageRecords
). These are:
- asOf: This is the date that the item was sent
- category: This is the item's category
- count: This is the number of items sent
- price: This is how much each item cost
- priceUnit: The currency in which price is measured, in ISO 4127 format, such as
usd
,eur
, andjpy
.
Then, the event is dispatched, and a JSON representation of $usageRecords
is returned.
Test that the application works
Now, the code's finished, so it's time to test that the application works. Start the application by running the following command
Then, with the application running, open http://localhost:8000/usage-records in your browser. You should see all of the retrieved usage records rendered as JSON. A few moments later, you should receive an SMS telling you how many records were retrieved, along with the total cost of sending them, as in the screenshot below.
That's how to handle Symfony Events with Twilio SMS
There is a lot more to the topic than I have covered in this tutorial, so check out the Symfony documentation to learn more. What’s more, have a play with the code and build a range of different subscribers, ones that perhaps use other Twilio services, or different tooling entirely. I’d love to know what you build.
Matthew Setter is a PHP Editor in the Twilio Voices team and a PHP and Go developer. He’s also the author of Mezzio Essentials and Docker Essentials. When he's not writing PHP code, he's editing great PHP articles here at Twilio. You can find him at msetter@twilio.com, and on Twitter, and GitHub.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.