Status Stream [ 192 ] 'stream-' . $data['ID'], 'stream/types/' . $data['type_reference'] . '-fromself.tpl.php', $datatags ); } elseif( $data['poster'] == $data['profile'] ) { $this->registry->getObject('template')->addTemplateBit( 'stream-' . $data['ID'], 'stream/types/' . $data['type_reference'] . '-user.tpl.php', $datatags ); } else { // network updates $this->registry->getObject('template')->addTemplateBit( 'stream-' . $data['ID'], 'stream/types/' . $data['type_reference'] . '.tpl.php', $datatags ); } } } else { If there were no updates, we display the none template. $this->registry->getObject('template')->buildFromTemplates( 'header.tpl.php', 'stream/none.tpl.php', 'footer.tpl.php'); } } So, if we had our templates in place at this stage, bringing all of this together, adding the controller to our database, and viewing /stream what would we see? Download from Wow! eBook <www.wowebook.com> Chapter 6 [ 193 ] This is the basics of our status stream. We can now look at adding comments, likes, and dislikes to the stream. Comments, likes, and dislikes The functionality behind adding comments, likes, and dislikes is very similar to the work we did in Chapter 5 in developing the statuses on a user's prole. The concept to adding these to the stream is fairly straightforward. Firstly, we create an empty array of comments / likes / dislikes for each status. This way, if a post has no comments, likes, or dislikes, then by caching the empty array and sending it to the template, we don't see a blank list where comments should be. If there are comments, they are added to the empty array (making it non-empty), cached, and sent to the template. Although the method is the same for the three aspects, comments require one database query, whereas likes and dislikes combined require another, so let's add in support one at a time. Comments Take the status IDs we retrieved earlier, and create empty arrays for each of them. $status_ids = implode( ',', $IDs ); $start = array(); foreach( $IDs as $id ) { $start[ $id ] = array(); } Copy our new array of empty arrays to be used for comments. // comments $comments = $start; Query the database for comments. $sql = "SELECT p.name as commenter, c.profile_post, c.comment FROM profile p, comments c WHERE p.user_id=c.creator AND c.approved=1 AND c.profile_post IN ({$status_ids})"; $this->registry->getObject('db')->executeQuery( $sql ); if( $this->registry->getObject('db')->numRows() > 0 ) { Download from Wow! eBook <www.wowebook.com> Status Stream [ 194 ] If there are comments, iterate through them and add them to the appropriate bucket (or array) for the status it relates to. while( $comment = $this->registry->getObject('db')->getRows() ) { $comments[ $comment['profile_post'] ][] = $comment; } } For each of the comments arrays, we cache the data and send them to the template engine. foreach( $comments as $status => $comments ) { $cache = $this->registry->getObject('db')->cacheData( $comments ); $this->registry->getObject('template')->getPage()->addTag( 'comments-' . $status, array( 'DATA', $cache ) ); } Likes and dislikes Likes and dislikes are stored in the same table. So we query it, and depending on the result, put the result in a different array. $likes = $start; $dislikes = $start; $sql = "SELECT i.status, p.name as iker, i.iker as iker_id, i.type as type FROM profile p, ikes i WHERE p.user_id=i.iker AND i.status IN ({$status_ids}) "; $this->registry->getObject('db')->executeQuery( $sql ); if( $this->registry->getObject('db')->numRows() > 0 ) { while( $ike = $this->registry->getObject('db')->getRows() ) { if( $ike['type'] == 'likes' ) { $likes[ $ike['status'] ][] = $ike; } else { $dislikes[ $ike['status'] ][] = $ike; } } } foreach( $likes as $status => $likeslist ) { Download from Wow! eBook <www.wowebook.com> Chapter 6 [ 195 ] $cache = $this->registry->getObject('db')->cacheData( $likeslist ); $this->registry->getObject('template')->getPage()->addTag( 'likes-' . $status, array( 'DATA', $cache ) ); } foreach( $dislikes as $status => $dislikeslist ) { $cache = $this->registry->getObject('db')->cacheData( $dislikeslist ); $this->registry->getObject('template')->getPage()->addTag( 'dislikes-' . $status, array( 'DATA', $cache ) ); } Views Now we need our template les to provide us with the view. Main template The main template contains an outer template loop, which is populated based on the cached status IDs. Once this is populated, we have a unique template tag for each status, ready for the status template to be inserted. It also generates three inner loops—one for comments, one for likes, and one for dislikes. <div id="main"> <div id="rightside"> </div> <div id="content"> <h1>Updates in your network</h1> <! START stream > {stream-{status_id}} <! START comments-{status_id} > <p> {comment} by {commenter}</p> <! END comments-{status_id} > <! START likes-{status_id} > <p>{iker} likes this</p> <! END likes-{status_id} > <! START dislikes-{status_id} > <p>{iker} dislikes this</p> <! END dislikes-{status_id} > <! END stream > </div> </div> Download from Wow! eBook <www.wowebook.com> Status Stream [ 196 ] Status type templates For each status type (we have only one at the moment) we need a template for each context (user's own status, user posting on someone else's prole, someone posting on a user's prole, and two users in a contact's network posting on their prole). These templates are stored in the views/default/templates/stream/types folder. Below is the template used when showing the status the logged-in user posted on the prole of another user: <p>You posted on {statusprofile_name}'s profile: {statusupdate}</p> <p class="postedtime">Posted {statusfriendly_time}</p> In action Now that we have the model, controller, and views in place, all that leaves us to do is create a controller record in the database for stream, and visit /stream as a logged-in user. Room for improvement We have developed a powerful status stream in this chapter, but as with anything there is always room for improvement. Let's discuss how this might be improved and extended. Download from Wow! eBook <www.wowebook.com> Chapter 6 [ 197 ] Firstly, there is some overlap in terms of logic and queries with the user's prole and the status' delegator within the prole. It may be possible for us to centralize this functionality and use it in both these instances, perhaps generating a stream for a prole or generating a stream for a network, depending on methods called in the object. Secondly, we are doing a few small queries in the controller. This is generally best avoided, and instead models should be used to generate the comments, likes, and dislikes. We could create models for these, which this and the user's prole could make use of. Plural names are currently hardcoded within the templates. So if Bill posts on Ben's prole, the text is generated by adding 's to the user's name. Some names may only require an apostrophe, so we could look at making this more dynamic. The nal obvious area for improvement is the data it pulls in; it currently pulls in the 20 most recent updates, taking an offset into account. We may wish to detect more recent updates since then, so a user could load in (perhaps through AJAX) more recent status updates that have occurred while they have been viewing the stream page. A system stream for administrators While not as straightforward, as these events won't be centrally stored, we could also create a stream of system events for the administrator. These could include: • Logins / logouts / signups • Statuses • Relationship formations • When passwords are sent • When e-mails are sent via the site, and so on This isn't an essential feature, but could be a nice feature to have depending on the size of the network and the size of the administrative team running the site. Download from Wow! eBook <www.wowebook.com> Status Stream [ 198 ] Summary In this chapter we have taken the statuses that users were able to update and post thanks to our work in Chapter 5 and created a stream of these statuses that the user can see, based on the activity and contacts within their network on Dino Space. This includes: • Status updates • Posting on other user's proles • Comments relating to these updates • Likes / dislikes related to these updates We've also looked at potential ways to improve and enhance this feature, as well as what we might wish to consider adding for administrators to see an overview of the system. With all of this now in place, let's move on to supporting new types of media on the site, with images, videos, and links being posted onto the proles of our users! Download from Wow! eBook <www.wowebook.com> Public and Private Messages On Dino Space, we have a new status stream for our users to display the activity happening in their network. One of the things included in the stream was postings on other users' wall posts, something which our database supports, but at the moment, our site doesn't! In this chapter, you will learn: • How to allow users to post messages on each other's proles • How to allow users to post private messages to each other 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 proles for other users to see. Let's get started with extending our proles and the status stream! Public messages Our status stream from Chapter 6 fully supports public messages and streaming them to the Dino Space members. What we don't yet have, however, is support for users to post messages on the proles of other users, so, let's add that in now. Controller A user should only be able to post a message on another user's prole if they are connected. The post message form should only be displayed if the users are connected. Similarly, a public message post should only be processed if the two users are connected. The controller also needs to display messages that have been posted on a user's prole too. Download from Wow! eBook <www.wowebook.com> Public and Private Messages [ 200 ] Displaying prole messages If we look at our Profilestatusescontroller (controllers/profile/ profilestatusescontroller.php ), in the listRecentStatuses method, we have our query for listing recent prole statuses: $sql = "SELECT t.type_reference, t.type_name, s.*, p.name as poster_name FROM statuses s, status_types t, profile p WHERE t.ID=s.type AND p.user_id=s.poster AND p.user_id={$user} ORDER BY s.ID DESC LIMIT 20"; At the moment, this query pulls in any posts on a user's prole by the user whose prole it is. If that user has made a post on someone else's prole, the message instead shows on the user's own prole, which we don't want. We need to change this to pull in the proles table twice, once for the user who made the post, and again for the user whose prole is being viewed. We will also want to only pull in posts made on the user's prole, and not posts made by the user on another user's prole (though this is something we can expand on in the future, perhaps to indicate that a user has made a post on the prole of another user). The following query should meet our requirements nicely: $sql = "SELECT t.type_reference, t.type_name, s.*, pa.name as poster_name FROM statuses s, status_types t, profile p, profile pa WHERE t.ID=s.type AND p.user_id=s.profile AND pa.user_id=s.poster AND p.user_id={$user} ORDER BY s.ID DESC LIMIT 20"; Now, if we view a user's prole, we see their own status updates, and messages posted on their prole by other users, as shown in the following screenshot: Download from Wow! eBook <www.wowebook.com> Chapter 7 [ 201 ] Displaying the post message box The listRecentStatuses method we were just editing is the method we need to edit to display the post message box. This box should only be displayed if the user is logged in, and is connected to the user. If the user is viewing their own prole, then they should see a box to update their own status: // post status / public message box if( $this->registry->getObject('authenticate')->isLoggedIn() == true ) { $loggedInUser = $this->registry->getObject('authenticate')- >getUser()->getUserID(); If the logged in user is viewing their own prole, then we add the update template to the view, so they can update their status: if( $loggedInUser == $user ) { $this->registry->getObject('template')->addTemplateBit( 'status_ update', 'profile/statuses/update.tpl.php' ); } else { If the user isn't viewing their own prole, but is logged in, we get any connections the user has: require_once( FRAMEWORK_PATH . 'models/relationships.php' ); $relationships = new Relationships( $this->registry ); $connections = $relationships->getNetwork( $user, false ); if( in_array( $loggedInUser, $connections ) ) { If the user is connected to the user whose prole they are viewing, then we allow them to post a message on the users prole with the post template: $this->registry->getObject('template')->addTemplateBit( 'status_update', 'profile/statuses/post.tpl.php' ); } else { Download from Wow! eBook <www.wowebook.com> . $cache = $this->registry->getObject('db' )-& gt;cacheData( $comments ); $this->registry->getObject('template' )-& gt;getPage( )-& gt;addTag( 'comments-' . $status,. "; $this->registry->getObject('db' )-& gt;executeQuery( $sql ); if( $this->registry->getObject('db' )-& gt;numRows() > 0 ) { while( $ike = $this->registry->getObject('db' )-& gt;getRows(). $cache = $this->registry->getObject('db' )-& gt;cacheData( $likeslist ); $this->registry->getObject('template' )-& gt;getPage( )-& gt;addTag( 'likes-' . $status,