7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
  1. Code
  2. Yii

Building Your Startup With PHP: Email Commands

Scroll to top
Read Time: 11 mins
This post is part of a series called Building Your Startup With PHP.
Building Your Startup: Delivering the Meeting Invitation
Building Your Startup With PHP: Email Commands
Final product imageFinal product imageFinal product image
What You'll Be Creating


This tutorial is part of the Building Your Startup With PHP series on Envato Tuts+. In this series, I'm guiding you through launching a startup from concept to reality using my Meeting Planner app as a real-life example. Every step along the way, I'll release the Meeting Planner code as open-source examples you can learn from. I'll also address startup-related business issues as they arise.

What Does This Episode Cover?

In the last tutorial, we began emailing meeting invitations which included numerous links for participants to respond, i.e. view the meeting page, accept all places and times, reject a place or time, etc.

In this tutorial, I'll review how I chose to construct and process those links in a secure, functional manner. The majority of meeting participants (especially at first) won't have used Meeting Planner before—they'll be unknown to us. Yet we'll want to securely authenticate them to allow them to view and interact with the meeting request and to create their own for the future. We also want to have some safeguards for when people forward the meeting requests with their secure codes without thinking about the ramifications (neophytes! perhaps, or just everyday people).

Just a reminder, all of the code for Meeting Planner is written in the Yii2 Framework for PHP. If you'd like to learn more about Yii2, check out my parallel series Programming With Yii2 at Envato Tuts+.

By the time you're reading this, you can probably begin to try out meeting invitations at the live website, MeetingPlanner.io (keep in mind, there remains a lot of user experience improvement work and polishing to do). I do participate in the comment threads below and am especially interested if you have additional ideas or want to suggest topics for future tutorials. You can also reach me on Twitter @reifman.

Meeting Planner Commands

The Importance of Commands

During my design process, I thought of commands in email as elements of both the meeting planning process and the time leading up to the actual event. 

When a participant receives an email invitation, they'll need to be offered secure permissions to view the meeting page but also to respond to whether specific places and times work well for them.

After a meeting is finalized, we may begin sending reminders to participants that offer specialized commands such as "I'm running late," which would text the other parties of your predicament, or "request a change in place" or "cancel". 

All of these commands need to authenticate the recipient and provide them secure access to the website so Meeting Planner can properly process their replies. However, the site also needs to safeguard not releasing a member's entire contact list if they mistakenly forward a meeting invitation email to another party, who then clicks the links. The secondary party would easily log into the recipient's Meeting Planner account and be able to see all of their meetings and personal information.

What Commands Are Needed?

As I thought about the vision for the application further, there are a large number of potential commands. Here are some in the initial invitation (which I may simplify at some point to improve the user experience):

  • View the meeting
  • Accept all the places and times
  • Decline the invitation
  • Accept or reject particular places
  • Accept or reject particular dates and times
  • Finalize the meeting*
  • Suggest another place*
  • Suggest another date and time*
  • Choose the final place*
  • Choose the final date and time*
  • Add or reply to meetings notes
  • View a map of the location of places within the context of the meeting
  • Review your email settings
  • Block this organizer from emailing you
  • Unsubscribe from all Meeting Planner emails

Note: The appearance of starred (*) items depends on the organizer's meeting settings.

Once the meeting is planned, there are also a variety of followup commands:

  • Reschedule the meeting
  • Cancel the meeting
  • Show me a map
  • Get driving directions
  • Request a change to the time
  • Request a change to the place
  • Notify parties you're running late

Architectural Considerations

Given the plethora of various commands, I felt it would be useful to authenticate and process all of them identically in a single controller. 

For now, I created a single processing point in MeetingController, but I expect I'll later create a dedicated CommandController. I've also considered creating an API access controller in the future and channeling all of the application's functionality through this single secure entry point. For now, I'll hold off on that.

To begin, I gave every command a specific constant definition within the Meeting.php model:

Building the Command Links

I decided that, for now, each command will have the following URL arguments:

  • $id for the meeting_Id
  • $cmd for the command action (from constants above)
  • $obj_id for whatever object might be acted upon i.e. place or date time
  • $actor_id for the user_id invoking the command
  • $k for the key which authenticates the $actor_id to their account

The majority of participants at first will not have registered, but we do create authentication keys linked to their invitation emails when the meeting is created. So that's how we authenticate their links from email invitations.

Here's an example URL link embedded in emails:


Given the complexity of creating the URLs with various arguments from many places in the code, I created a /common/components/MiscHelpers.php library, which began with buildCommand:

Here's an example of our invitation-html.php view file calling buildCommand() for displaying rows of places. Each place has commands that must provide all of these arguments in URLs:

You can see what they look like below:

Meeting Planner Places and Commands for PlacesMeeting Planner Places and Commands for PlacesMeeting Planner Places and Commands for Places

Processing the Commands

Then, I built the controller function for authenticating and processing the commands. Here's the first part:

Initially, I wanted to provide safeguards for my own testing as well as people forwarding emails with their authentication links.

One event I check is whether the $actor_id is a different user than the currently logged-in user. That could happen during testing with multiple accounts, or it could happen if the participant forwards their invitation to the organizer. Ultimately, I'll provide information about the situation and offer choices for people. However, for now, I just log the current user out before authenticating the requesting user.

If the user is already logged in as the $actor_id, then they are authenticated. If they're not authenticated, we run the authentication check:

We use Yii's built-in findIdentity and validateAuthKey functions for this.

In the near future, I plan to make authenticating from email provide a limited access to account features. For example, whenever users aren't logged in but click on command links, then we'll restrict their activities to just that meeting and a few related features. They won't be able to see other meetings, the account holder's friends, etc. However, we will offer a friendly link for them to log in to their account via password or social login. This will minimize the security impacts of people forwarding invitations around.

Similarly, if a new user who's never registered before clicks a command link, we'll present reminders for them to register and create a password or social login. The User.php model has status fields which indicate whether a user has ever registered themselves, or if they were passively invited to a meeting.

For now, if authentication succeeds, we can just process each of the commands:

For features I haven't built yet, I created a view to indicate the feature is unavailable, e.g. /views/site/unavailable.php, or if the command is misunderstood, then /views/site/error.php.

Two Sample Commands

Let's look at two example commands. First, let's look at suggesting another place:

In this case, the functionality requires the user return to our website to fill in a form where they can select a new place. So, we just redirect them to the create meeting place page for that meeting_id. And they're already authenticated and logged in from above. 

Here's an example of this—notice the breadcrumb menu reflect the context of the meeting, e.g. Breakfast Meeting:

Meeting Planner Email Commands - Add a Meeting PlaceMeeting Planner Email Commands - Add a Meeting PlaceMeeting Planner Email Commands - Add a Meeting Place

Second, let's look at accepting all dates and times:

In this case, we need to accept all the times for that meeting and $actor_id. The acceptance is done transparently behind the scenes. After that, we can redirect them to view the meeting.

Here's what it looks like when reaching the meeting view with everything accepted, e.g. okayokayokay for places and times below:

Meeting Planner Email Commands - Accept all places and timesMeeting Planner Email Commands - Accept all places and timesMeeting Planner Email Commands - Accept all places and times

A Funny Story

Implementing all these commands definitely took some time, but Meeting Planner's features really began to take life. And I was able to send my first invitations out into the world.

A woman I'd been dating knew I was getting close to finishing this functionality so she decided to motivate me to finish it more quickly. She said:

"I have no idea when I'm going to see you next because I haven't yet received my Meeting Planner invitation." 

With a couple days of extra work, I sent her the second meeting planner invitation—the first went to a friend for testing.

Impressively, when my date received her invitation, she quickly asked for two useful features. First, she said she wasn't sure she'd be able to show up for our date unless the event was in her phone's Google Calendar (generally I prefer to date iOS users, not Android). The next tutorial will tell the story of building an iCal (.ics) file for importing (so my date would know where to go). I won't keep you in suspense—I finished the feature in time for our date.

Secondly, she asked for a feature I'd thought of but hadn't realized its importance. She wanted to be able to specify a place with a time. In other words, Canlis Restaurant on Friday at 7pm but Paseo on Saturday at 8pm. Currently, places and times are offered separately and not in combination. I'll save this feature for a future episode. 

This raises the general issue of how during the startup process do you regularly gather feedback from people and integrate it into your requirements and development planning. Not all of your users will offer you dates in exchange for their favorite features. I have a tutorial episode planned to discuss how to do this in the future as well, despite the lack of secondary motivation.

What's Next?

In the next episode, I'll detail building Calendar files (.ics) for import to Google Calendar, Outlook and Apple Calendar with the invitation details. Including contact details and maps and managing time zone issues are all key aspects of this.

Watch for upcoming tutorials in my Building Your Startup With PHP series—I hope you're getting eager to try out Meeting Planner. Give it a try right now!

Please feel free to add your questions and comments below; I try to participate in the discussions on a regular basis. You can also reach me on Twitter @reifman.

Related Links

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.