Twitter App

Twitter Emulation Using MooTools 1.2 and PHP

Nov 11th in PHP by David Walsh

People all over the world love Twitter because of how easy it is to use. All you need to do is type in your current status, click "Update", and you're done. What most people probably don't know is how simple it is to emulate Twitter's functionality. Using PHP, MySQL, and MooTools javascript, you can implement a Twitter-like status system in no time.

PG

Author: David Walsh

David Walsh is an eccentric programmer that finds a way to make things work. His expertise is in CSS, PHP, and MooTools javascript. You can read his ramblings at the David Walsh Blog (http://davidwalsh.name)

Assumptions

Before we create this system, lets keep in mind a few assumptions we're making:

  1. We assume that the only user we're keep track of is the person logged in and that the user's ID is 1.
  2. We also assume that user has javascript enabled. The great thing about this solution is that it works with javascript turned on or off.
  3. We assume that the user's image is stored in the following directory structure: /graphics/users/{user_id}.png
  4. We assume that we're using the full MooTools 1.2 core with the Fx.Slide plugin.

The Show

Here's the sequence of events that will take place in our concoction:

  • The page loads and shows previous statuses (or "tweets").
  • The user types in their new status and clicks submit.
  • A message slides in from behind the submit button saying "Status Updated!"
  • The new status and user photo slides in right after the "Recent Updates" heading.

As you can see, it's very simple functionality that will be presented in an elegant manner.

Step One: The MySQL

We need a place to store these updates, right? Here's what our "statuses" table will look like:

		CREATE TABLE IF NOT EXISTS `statuses` (
		  `status_id` MEDIUMINT(10) unsigned NOT NULL auto_increment,
		  `user_id` SMALLINT(5) NOT NULL,
		  `status` varchar(150) NOT NULL,
		  `date_set` datetime NOT NULL,
		  PRIMARY KEY  (`status_id`)
		) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
	

It's important that status ID is the primary key and that the field auto-increments.

Step Two: The XHTML

Before any of our MooTools magic can be used, we need to create the "update" form and place our message DIV next to the submit button so that MooTools can effectively slide in the message from behind the button. Note that we set the form's action to this same page. Also note that we place an initial $message variable inside the message DIV for users who do not have javascript enabled.

		<h3>What are you doing?</h3>
		<form action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post">
			<textarea name="status" id="status"></textarea><br />
			<input type="submit" value="Update Status" id="submit" />
			<div id="message"><?php echo $message; ?></div>
		</form>
	

We're not done with the XHTML part yet. The next step is to add the "wrapper" DIV for all of the previous statuses.

		<h3>Recent Updates</h3>
		<div id="statuses">
			<!-- php/mysql will go here -->
		</div>
	

Lastly, we include the MooTools library in the header of the page.

		<script type="text/javascript" src="moo1.2.js"></script>
	

Step 3: The PHP/MySQL - Part 1

This first snippet of PHP will be placed inside our "statuses" DIV. We've chosen to display the most recent 20 statuses.

		$query  = 'SELECT status, DATE_FORMAT(date_set,\'%M %e, %Y @ %l:%i:%s %p\') AS ds FROM statuses ORDER BY date_set DESC LIMIT 20';
		$result = mysql_query($query,$link) or die(mysql_error().': '.$query);
		while($row = mysql_fetch_assoc($result))
		{
			echo '<div class="status-box">',stripslashes($row['status']),'<br /><span class="time">',$row['ds'],'</span></div>';
		}
	

Step 4: The CSS

As you know, CSS is our presentation layer so style up your page however you'd like. We create the "failure" and "success" classes for the result message -- note that the success message uses green and failure message is colored red. Also note that the status-box class contains the user's avatar, set by PHP.

		#message		{ padding:7px 10px; float:left; width:350px; }
		#status		{ border:1px solid #999; padding:5px; width:500px; height:75px; margin:5px 0; }
		#statuses	{ width:512px; }
		#submit		{ cursor:pointer; padding:5px; border:1px solid #ccc; float:left; margin:0 20px 0 0; }
		
		.status-box	{ padding:10px 20px 10px 70px; height:48px; background:url(/graphics/users/<?php echo $_SESSION['user_id']; ?>.png) 10px 10px no-repeat; border-bottom:1px dotted #aaa; }
		.status-box:hover	{ background-color:#eee; }
		.success		{ color:#008000; }
		.failure		{ color:#f00; }
		.time			{ color:#2a447b; font-size:10px; }
	

Step 5: The MooTools Javascript

Now for the fun part -- using MooTools 1.2 goodness to create our subtle animations and Ajax request.

Once the DOM is ready...

window.addEvent('domready', function() {

We create a horizontal slider for the success/failure message. We hide it for now...

var fx = new Fx.Slide('message', { mode: 'horizontal' }).hide();

We now create our (Ajax) Request. We do this outside of the click event (which will follow in a moment) so that we don't waste memory.

When the user clicks the submit button, we want to disabled it to prevent double-submission. Upon completion, we enable the submit button and direct the status message notifier message to hide in 2 seconds.

If the request fails, the message reflects that by sliding in the "Status could not be updated. Try again." message. If the request is successful, quite a bit more happens.

We start by sliding in a "Status Updated" message. Next we clear the status textarea. Then we inject the new element box into the statuses container and slide it in. Here is the MooTools code in its entirety.

		//make the ajax call to the database to save the update
		var request = new Request({
			url: '<?php echo $_SERVER['PHP_SELF']; ?>',
			method: 'post',
			onRequest: function() {
				$('submit').disabled = 1;
			},
			onComplete: function(response) {
				$('submit').disabled = 0;
				$('message').removeClass('success').removeClass('failure');
				(function() { fx.slideOut(); }).delay(2000);
			},
			onSuccess: function() {
				//update message
				$('message').set('text','Status updated!').addClass('success');
				fx.slideIn();
				
				//store value, clear out box
				var status = $('status').value;
				$('status').value = '';
				
				//add new status to the statuses container
				var element = new Element('div', {
					'class': 'status-box',
					'html': status + '<br /><span class="time">A moment ago</span>'
				}).inject('statuses','top');
				
				//create a slider for it, slide it in.
				var slider = new Fx.Slide(element).hide().slideIn();
				
				//place the cursor in the text area
				$('status').focus();
				
			},
			onFailure: function() {
				//update message
				$('message').set('text','Status could not be updated.  Try again.').addClass('failure');
				fx.slideIn();
			}
		});
	

When form submission is triggered...

$('submit').addEvent('click', function(event) {

Prevent the page from refreshing due to the form submission.

event.preventDefault();

If the "status" textarea has a value...

if($('status').value.length && !$('status').disabled) {

We execute the request, passing along the new status.

			request.send({
				data: {
					'status': $('status').value,
					'ajax': 1
				}
			});
	

Here's the complete MooTools script:

		/* when the dom is ready */
		window.addEvent('domready', function() {
			
			//create the message slider
			var fx = new Fx.Slide('message', {
				mode: 'horizontal'
			}).hide();
			
			//make the ajax call to the database to save the update
			var request = new Request({
				url: '',
				method: 'post',
				onRequest: function() {
					$('submit').disabled = 1;
				},
				onComplete: function(response) {
					$('submit').disabled = 0;
					$('message').removeClass('success').removeClass('failure');
					(function() { fx.slideOut(); }).delay(2000);
				},
				onSuccess: function() {
					//update message
					$('message').set('text','Status updated!').addClass('success');
					fx.slideIn();
					
					//store value, clear out box
					var status = $('status').value;
					$('status').value = '';
					
					//add new status to the statuses container
					var element = new Element('div', {
						'class': 'status-box',
						'html': status + '
A moment ago' }).inject('statuses','top'); //create a slider for it, slide it in. var slider = new Fx.Slide(element).hide().slideIn(); //place the cursor in the text area $('status').focus(); }, onFailure: function() { //update message $('message').set('text','Status could not be updated. Try again.').addClass('failure'); fx.slideIn(); } }); //when the submit button is clicked... $('submit').addEvent('click', function(event) { //stop regular form submission event.preventDefault(); //if there's anything in the textbox if($('status').value.length && !$('status').disabled) { request.send({ data: { 'status': $('status').value, 'ajax': 1 } }); } }); });

Step 5: The PHP/MySQL - Part 2

This "header" portion of PHP code goes at the top of the same PHP file the rest of our code is in. This code will also be run by both Ajax requests and users who have javascript disabled. Upon form submission (and assuming the user is logged in), we insert the new status into the database. If the Ajax flag is set (by the MooTools code above), we know that the user does have javascript enabled and we kill the script. If the user did not use javascript, we simply set the $message variable's initial value to "Status Updated!" and display the page. Simple!

		//connect to the db
		$link = @mysql_connect('localhost','username','password');
		@mysql_select_db('blog',$link);
		
		/* form submission post */
		if(isset($_POST['status']) && $_SESSION['user_id'])
		{
			//record the occurence
			$query = 'INSERT INTO nettuts1 (user_id, status, date_set) VALUES ('.$_SESSION['user_id'].',\''.mysql_escape_string(htmlentities(strip_tags($_POST['status']))).'\',NOW())';
			$result = @mysql_query($query,$link);
			
			//die if this was done via ajax...
			if($_POST['ajax']) { die(); } else { $message = 'Status Updated!'; }
		}

	

That's It?

Yes! Here's the entire PHP / MooTools / XHTML / CSS file:

<?php

//connect to the db
$link = @mysql_connect('localhost','username','password');
@mysql_select_db('blog',$link);

/* form submission post */
if(isset($_POST['status']) && $_SESSION['user_id'])
{
	//record the occurence
	$query = 'INSERT INTO nettuts1 (user_id, status, date_set) VALUES ('.$_SESSION['user_id'].',\''.mysql_escape_string(htmlentities(strip_tags($_POST['status']))).'\',NOW())';
	$result = @mysql_query($query,$link);
	
	//die if this was done via ajax...
	if($_POST['ajax']) { die(); } else { $message = 'Status Updated!'; }
}

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"  dir="ltr">
<head>
<title>Twitter Emulation</title>
<style type="text/css">
	
	#message		{ padding:7px 10px; float:left; width:350px; }
	#status		{ border:1px solid #999; padding:5px; width:500px; height:75px; margin:5px 0; }
	#statuses	{ width:512px; }
	#submit		{ cursor:pointer; padding:5px; border:1px solid #ccc; float:left; margin:0 20px 0 0; }
	
	.status-box	{ padding:10px 20px 10px 70px; height:48px; background:url(nettuts-david.jpg) 10px 10px no-repeat; border-bottom:1px dotted #aaa; }
	.status-box:hover	{ background-color:#eee; }
	.success		{ color:#008000; }
	.failure		{ color:#f00; }
	.time			{ color:#2a447b; font-size:10px; }
	
</style>
<script type="text/javascript" src="moo1.2.js"></script>

<script type="text/javascript">

	/* when the dom is ready */
	window.addEvent('domready', function() {
		
		//create the message slider
		var fx = new Fx.Slide('message', {
			mode: 'horizontal'
		}).hide();
		
		//make the ajax call to the database to save the update
		var request = new Request({
			url: '/dw-content/nettuts-twitter.php',
			method: 'post',
			onRequest: function() {
				$('submit').disabled = 1;
			},
			onComplete: function(response) {
				$('submit').disabled = 0;
				$('message').removeClass('success').removeClass('failure');
				(function() { fx.slideOut(); }).delay(2000);
			},
			onSuccess: function() {
				//update message
				$('message').set('text','Status updated!').addClass('success');
				fx.slideIn();
				
				//store value, clear out box
				var status = $('status').value;
				$('status').value = '';
				
				//add new status to the statuses container
				var element = new Element('div', {
					'class': 'status-box',
					'html': status + '<br /><span class="time">A moment ago</span>'
				}).inject('statuses','top');
				
				//create a slider for it, slide it in.
				var slider = new Fx.Slide(element).hide().slideIn();
				
				//place the cursor in the text area
				$('status').focus();
				
			},
			onFailure: function() {
				//update message
				$('message').set('text','Status could not be updated.  Try again.').addClass('failure');
				fx.slideIn();
			}
		});
		
		//when the submit button is clicked...
		$('submit').addEvent('click', function(event) {
			
			//stop regular form submission
			event.preventDefault();
			
			//if there's anything in the textbox
			if($('status').value.length && !$('status').disabled) {
				
				request.send({
					data: {
						'status': $('status').value,
						'ajax': 1
					}
				});
				
			}
			
		});
		
	});
	
</script>
</head>
<body>

<h3>What are you doing?</h3>

<form action="/dw-content/nettuts-twitter.php" method="post">
	<textarea name="status" id="status"></textarea><br />
	<input type="submit" value="Update Status" id="submit" />
	<div id="message"></div>
</form>

<div class="clear"></div>
<p> </p>
<h3>Recent Updates</h3>

<div id="statuses">
	<?php
		//get the latest 20
		$query  = 'SELECT status, DATE_FORMAT(date_set,\'%M %e, %Y @ %l:%i:%s %p\') AS ds FROM nettuts1 ORDER BY date_set DESC LIMIT 20';
		$result = mysql_query($query,$link) or die(mysql_error().': '.$query);
		while($row = mysql_fetch_assoc($result))
		{
			echo '<div class="status-box">',stripslashes($row['status']),'<br /><span class="time">',$row['ds'],'</span></div>';
		}
	?>
</div>
</body>
</html>

	

Updates & Enhancements

While the above code provides a slick, functional interface, by no means would you consider this a completed system. Here are a few ideas for enhancements you can implement:

  • Updated time displays upon new status submission
  • Friend status integration
  • Javascript-based status length limiter
  • Anything else you can think of!

What are you thoughts? Have any suggestions? Please share them below!


Related Posts

Check out some more great tutorials and articles that you might like

Enjoy this Post?

Your vote will help us grow this site and provide even more awesomeness

Plus Members

Source Files, Bonus Tutorials and
More for $9 a month for all TUTS+
sites in one subscription.

Join Now

User Comments

( ADD YOURS )
  1. PG

    Richard N. November 11th

    This looks awesome, would love to see a ruby on rails tutorial done for it as well since that’s the language/framework it’s built upon.

    http://twitter.com/richanomix

    ( Reply )
  2. PG

    Kevin May November 11th

    Awesome work, not sure I would use it.. but still very good information to show how easy it is.

    ( Reply )
  3. PG

    pickupjojo November 11th

    OK, I’m going to buy yet another Twitter-like. :D
    Just kidding, great tut’ !

    ( Reply )
  4. PG

    Chris Loftus November 11th

    Nice, haven’t actually used MooTools much, cool app.

    ( Reply )
  5. PG

    Jhay November 11th

    Awesome! Can’t wait to try! Thanks!!

    ( Reply )
  6. PG

    iLoveMacApps November 11th

    That is really interesting. I thought it was simple. I might try this out one day for laughs.

    ( Reply )
  7. PG

    Chris Coyier November 11th

    Hey there is a live demo of this too, check it out.

    ( Reply )
  8. PG

    Ben Reid November 11th

    This seems to be very light weight compared to using Twitter’s badges, nice work. Could be some nice hacks for this and Twitter’s service!

    :)

    ( Reply )
  9. PG

    Furley November 11th

    Pretty slick tutorial. Well done.

    ( Reply )
  10. PG

    Shane November 11th

    Very nice tutorial David. Don’t ya just wish you thought of Twitter first?

    :)

    ( Reply )
  11. PG

    David Walsh November 11th

    @Shane: Don’t rub it in! ;)

    Thank you to everyone for the nice comments!

    ( Reply )
  12. PG

    Christian Mejia November 11th

    Rub Rub Rub. Kidding! This is great David.

    ( Reply )
  13. PG

    Eric Wendelin November 11th

    Looks fantastic, David! Well done!

    ( Reply )
  14. PG

    insic November 11th

    wow. nice one. keep it up nettuts.

    ( Reply )
    1. PG

      talar April 28th

      ;]

      ( Reply )
  15. PG

    James November 11th

    Nice tut David, and thanks for the link Chris! I especially like the subtle animations.

    Maybe you could turn this into a series and talk about how you would develop it further, with reciprocal following and replies built in and perhaps even creating an API for your emulation!

    ( Reply )
  16. PG

    David Walsh November 11th

    @James: That’s my plan. Next would be deletion/animation and time updating.

    ( Reply )
  17. PG

    Buzu November 11th

    nice tutorial, No doubt many people will start building their own teitter-like app.

    ( Reply )
    1. PG

      jorge August 3rd

      Let´s do it

      ( Reply )
  18. PG

    Dr.Death November 11th

    Nice! Bookmarked ;)

    ( Reply )
  19. PG

    Sebastian November 11th

    Could this be used for a comment system on a news site?

    ( Reply )
  20. PG

    Shane November 11th

    @James – the building of an API would make an excellent tutorial, whether it’s built on top of this, or a separate example?

    Anyone up for it? :)

    ( Reply )
  21. PG

    Mayur Somani November 11th

    Good Work!

    ( Reply )
  22. PG

    Moksha November 11th

    great tutorial, thanks

    ( Reply )
  23. PG

    MK Owens November 11th

    I did a similar project like this using very similar code. You even use jEdit (my favorite text editor).

    This is a very well done tutorial; great to see another person using Mootools succinctly like this.

    ( Reply )
  24. PG

    T Pham November 11th

    good job man, thank for great tut.

    ( Reply )
  25. PG

    Vincent D'Amico November 11th

    great job

    ( Reply )
  26. PG

    Arthur November 11th

    I would love to see the addition of Gravatars and perhaps a version using Rails (would be a grrrrreat learning experience). Great tutorial!!!

    ( Reply )
  27. PG

    limbo November 12th

    why u allways cant include the sql dump in the damn zip?

    ( Reply )
  28. PG

    Zair Abbas November 12th

    wow!
    Good tutorial

    ( Reply )
  29. PG

    oh gosh November 12th

    yer man, cool! can you also do a tutorial on how to switch a computer on? it would be awesome! you can then do another tut on how to breathe.

    ( Reply )
  30. PG

    Swapnil Sarwe November 12th

    Very good tutorial. Leanrt couple of more things about MooTools. Will follow this to make up a good plugin on sites for site followers.

    ( Reply )
  31. PG

    Peter November 12th

    Good, you could add some cache feature in order no to kill MySQL ;)

    ( Reply )
  32. PG

    sean steezy November 12th

    what is so damn great about twitter? everybody’s on it…

    thanks for the tut, but some of these social networking site just annoy me after a while.

    ( Reply )
  33. PG

    Jason Calleiro November 12th

    A good project would be to get the OOP blog tutorial, the user management tutorial, this tutorial and combine them to build a version with a user registration function in an object oriented manner. Also a good little challenge would be to convert the moo-tools js to jquery.

    ( Reply )
  34. PG

    Francisco Gálvez November 12th

    Just work, and simple… nice code, thanks. I will use for my mini-company comunication to separate internal comunication, to external comunication (twitter)

    ( Reply )
  35. PG

    Jhay November 12th

    Did it! Check it here Clicky

    ( Reply )
  36. PG

    Takumi86 November 14th

    I got only 1 word to say about this: NICE!!
    More tweaking would be absolutely welcome

    ( Reply )
  37. PG

    Ben Johnson November 15th

    Very nice, for sure need to try this one out!

    ( Reply )
  38. PG

    T-Shirts Girl November 15th

    great script – thanks for sharing ;-)
    it would be great though, if the script would also check if others on different computers updated their status and update that on the screen too.

    ( Reply )
  39. PG

    Fabryz November 19th

    Nice tut, tagged for further modifications and integration in my app =)

    ( Reply )
  40. PG

    Simon November 19th

    Fantastic – Loads of food for thought

    ( Reply )
  41. PG

    Casper December 11th

    okay i tried almost everything no matter what i do i cant get it to show æøå properly.. it ocmes out as æøå… or actually it works while posted with ajax and so, but when refreshing the page and all the content is fetched from the db it comes out as æøå..

    any advice?? searched google the whole day and tried all kind of solutions

    ( Reply )
  42. PG

    Isaac December 15th

    Haha… the web blunders of 2008 post complains about the huge number of Twitter clones… Slightly ironic…

    ( Reply )
  43. PG

    joe6pack January 3rd

    this code almost works. unfortunately “sliding” doesn’t work with the source provided.

    ( Reply )
  44. PG

    James January 5th

    Its working on this site they have implemented it to the web appplication.

    http://www.kweech.com

    ( Reply )
  45. PG

    Commander K January 21st

    Great tutorial. Just one question, does anyone know how I can add message deletion?

    ( Reply )
  46. PG

    Jason February 16th

    Ok so I ran through all the code….
    everything works but….
    the $_POST array is always empty even after submitting
    I hard coded a dummy insert status and it enters the table just fine but
    I can’t seem to get anything out of the $_POST array
    I even get a 200 from firebug with all the right post data
    any help???

    ( Reply )
  47. PG

    intekhab khan March 9th

    very good tutorial

    ( Reply )
  48. PG

    Muhammad Ali Mansoor March 17th

    Good Work!

    ( Reply )
  49. PG

    moo-newbie March 25th

    Good Work! I wonder if the “tweets” deletion could be make using another post I have found in your blog.

    http://davidwalsh.name/animated-ajax-record-deletion-mootools

    ¿Could you tell me how to integrate this?

    ( Reply )
    1. PG

      Pratik July 12th

      even im looking for the same.. plz let me know when this is possible

      ( Reply )
  50. PG

    Kepler April 10th

    BUG REPORT :

    Fx.Slide is not a constructor
    (?)()()nettuts-…-file.php (ligne 29)
    returns()mootools…ore-nc.js (ligne 607)
    create()mootools…ore-nc.js (ligne 612)
    fireEvent()mootools…ore-nc.js (ligne 2015)
    fireEvent()mootools…ore-nc.js (ligne 2016)
    domready()mootools…ore-nc.js (ligne 2830)
    defn()mootools…ore-nc.js (ligne 1958)

    [Break on this error] mode: ‘horizontal’

    What kind of mootools do you use ?

    ( Reply )
  51. PG

    Kepler April 10th

    This error appear with the latest Mootools : mootools-1.2.1-core-nc.js

    ( Reply )
  52. PG

    zaanrider May 22nd

    wow nice tutorial

    ( Reply )
  53. PG

    Myfacefriends June 24th

    this is im looking thanks.

    ( Reply )
  54. PG

    Pablo DiCiacco June 29th

    To: Someone with complete success with this,

    Can you clarify the correct naming in the db section? Where this tut shows “nettuts1″ on line 11, what does that name reflect? The php-file name?
    The db name? ???

    ( Reply )
    1. PG

      Pratik July 7th

      its the table name… replace it by ’statuses’, if u are using the SQL query above

      ( Reply )
      1. PG

        Pablo DiCiacco October 25th

        Thanks, Had this set up for some time, but just cleared bunches of nonsense messages from the db.
        ?: Is this setup intended to show any username when entered? That part isn’t working for me. (?)
        Pablo

  55. PG

    Jim July 3rd

    Check greenbor.com – it has twitter like micro blogging built in .NET and also it has other social networking features. I like a full blown social network like facebook rather than twitter

    ( Reply )
  56. PG

    Pratik July 7th

    gr8 tutorial.. just 1 question is this architecture scalable ?

    ( Reply )
  57. PG

    king July 19th

    buen tutorial

    ( Reply )
  58. PG

    jorge August 3rd

    Vamos implementarlo es un web service que estamos desarrollando

    ( Reply )
  1. Arrow
    Gravatar

    Your Name
    August 3rd