Tutorials

Create a scalable private messaging application using PHP5

Dec 06, 2010 insic 12 Comments

Most social networking sites support two types of messages: public and private messages. Private messages are generally sent in a similar fashion to e-mails, and public messages being posted on user’s profiles for other users to see.

In this article by Michael Peacock, author of the book PHP 5 Social Networking, we will learn how to allow users to post private messages to each other.

PHP 5 Social Networking

Create a powerful and dynamic Social Networking website in PHP by building a flexible framework

  • Build a flexible Social Networking framework using PHP which can be extended to fit the needs of any Social Networking site
  • Develop a suitable structure for our framework, with MVC to structure the architecture and a Registry to store core Objects
  • Allow users to connect and communicate with each other using communication with friends list, flexible user profiles, messages, discussions, and much more
  • Plan marketing and scaling strategies, to entice more users and ensure the site can cope with the demand
  • Packed with real-world code and clear explanation, this book uses an ongoing case study for creating a Social Networking framework

Read more about this book

We obviously need to keep private messages separate from the rest of the site, and ensure that they are only accessible to the sender and the receiver. While we could alter the public messages feature developed earlier, this would raise a few issues, such as being more difficult to tell whether the message being sent or read was private, and when using the Internet in a public area, the message would be shown on the area of the social network the user would most likely be visiting, which isn’t ideal for private information.

Because private messages will be separate from statuses, and won’t need to make use of other media types to make them more interesting (though, we could set them up to make use of other media if we wanted), it makes sense for us to also use separate database tables and models for this feature.

Database

Our database needs provisions for the sender of the message, the recipient of the message, the subject of the message, and of course the message itself. We should also provide for if the message has been read, when the message was sent, and an ID for the message.

The following illustrates a suitable structure for a messages table in our database:

php5 database structure

More than one recipient?
This database structure, and the code that follows, only supports one recipient per message. Our users might want to send to more than one recipient—feel free to add this functionality if you wish.

Message model

As with the majority of our database access, we require a model (models/message. php) to create, update, and retrieve message-related data from the database and encapsulate it within itself.

It would also be helpful if the model pulled in a little more information from the database, including:

  • A more user friendly representation of the date (we can get this via the MySQL DATE_FORMAT function)
  • The name of the sender, by joining the messages table to the profile table
  • The name of the recipient, by joining the messages table to the profile table again

The first part of our model simply defines the class variables:

The constructor takes the registry and ID of the message as parameters, if the ID has been defined, then it queries the database and sets the class variables. The database query here also formats a copy of the date into a friendlier format, and looks up the names of the sender and recipient of the message:

Next, we have setter methods for most of the class variables:

The save method takes the class variables that directly relate to the messages table in the database and either inserts them as a new record, or updates the existing record:

One getter method that we need, is to return the user ID of the recipient, so we can check that the currently logged in user has permission to read the message:

We should also provide a method to delete the message from the database, should the user wish to delete a message:

Finally, we have a toTags method, which converts all of the non-object and non-array variables into template tags, so when we create a view message method in the controller, we simply need to construct the message object and call the toTags method:

Messages model

Similar to how we have a model for representing a single relationship and another for representing a number of relationships, we also need a model to represent a number of messages within the site. This is to handle the lookup of a user’s private message inbox.

Controllers and views

Our controller needs functionality for:

  • Listing a user’s private messages
  • Indicating which messages have been read, and which ones are unread
  • Reading a message
  • Deleting a message
  • Composing a new message

And we need three templates, for:

  • Our inbox
  • Viewing a message
  • Creating a new message

Listing messages

To list our messages, we simply require our messages controller, call the getInbox method, and send the cache to the template engine. The read_style field in the results of the query can be used to highlight rows in the messages table (HTML table in the view), which represent unread messages:

Inbox

The inbox template (views/default/templates/messages/inbox.tpl.php) simply requires a table with a template loop to contain the messages. Notice that the class of the table row is set based on whether the message has been read or not:

Reading a message

To read a message we need to require our message model, construct the message object with the registry and message ID, check that the recipient is the currently logged in user, load the template, and send the message information to the template:

View message template

The view message template (views/default/templates/messages/view.tpl. php) simply contains template tags for the message properties, and a few additional links:

Mark as read

Once a user has read a message, we should update the database to indicate that the message has been read, so that the user can see at a glance which of their messages are new and unread, and which ones have already been read. However, for privacy reasons, we shouldn’t show this information to the sender of the message.

To do this, we simply set the read property on the model to 1, and save the record, as illustrated by the changes to viewMessage highlighted below:

Now once the user reads a message, the database records the message as having been read.

Deleting a message

To delete a message, we simply call the model’s delete method, provided the message was sent to the logged in user of course!

What about the sender?
These deletes will remove the message completely, even from the sender, but what if we want to have the sender keep their copy, or have the sender be able to delete their copy? Feel free to extend this to have an additional field to indicate whether the message has been deleted by the sender, and add the functionality in.

If the message wasn’t sent to the logged in user, or if there was a problem deleting the message, we should display an appropriate error message to the user:

Composing a new message

Composing a new message is the most complicated aspect for the feature, as there are a number of aspects to consider (and change, depending on the needs of the social network). Let’s discuss these as we walk through the code:

The two strands of this feature (displaying the new message form, and processing the new message) require knowing a list of members that the message can be sent to (provided we wish to restrict sending to contacts only). As this is the case, it makes sense for us to require the relationships model, and instantiate it before progressing

If the user has submitted a new message, we need to check that the recipient they have selected is in their network (that is, that they are allowed to send them a message):

If the recipient is in the user’s network, then we create an instance of the message model, populate it with the data the user has submitted, and then save the message and redirect the user after displaying a confirmation message:

If the recipient isn’t in their network, we display an error page. Alternatively, we could check the recipient’s privacy settings to see if they allow messages from any user—something to consider when implementing privacy controls:

If the user hasn’t submitted the form, then we need to display the new message form to them. We can get the list of potential recipients (to go in a drop down) from the relationships model, which we associate with a template variable.

If the message is in reply to another message, then we can provide some basic support for this, such as pre-selecting the recipient, and pre-completing the subject line based off the message this is in reply to:

Creating a message template

For the new message template (views/default/templates/messages/create. tpl.php), we have a template loop for possible recipients. If the message is a reply, the recipient is selected via the additionalParsingData set in the controller. The subject is also pre-populated for us:

In action

If we visit our message centre /messages, we can see our inbox (once the controller is added to the controllers table in the database!), as shown in the screenshots earlier in the article.

If we click reply, we are taken to the same screen as with creating a new message, except the recipient is pre-selected and the subject is the subject of the previous message prefixed with Re:.

Room for improvement?

There are three particular areas that can be improved significantly:

  • Sent items
  • Replies
  • Group messages

Sent items

At the moment, we can’t see sent items, and while in principle this could be added easily, the problem lies if a user deletes the message. We would need to add two columns to the database, one indicating that the recipient has deleted it from their inbox, and one indicating the sender has deleted it from their sent items, as we discussed earlier in the article.

Replies

Our replies are not linked together; they are all stored as standalone messages, which is something we could improve.

Group messages

At the moment, private messages are only between two users, but we may wish to extend this in the future to support any number of users.

Summary

We have created a private messages area that allows users to communicate with one another in private.

About the author: insic

Subscribe in my RSS Feed for more updates on Web Design and Development related articles. Follow me on twitter or drop a message to my inbox.