Social Network Style Posting with PHP, MongoDB and jQuery – part 1

This entry is part 1 of 2 in the series Social Network Style Posting with PHP, MongoDB and jQuery

Social Network Style Posting with PHP, MongoDB and jQuery

Post mechanisms similar to Facebook are nowadays very common within any application. The concept of Post-Like-Comment is familiar to everyone who ever used a social network. In this article, we will learn how to create a similar working model wherein the user will be able to post his status, like posts and comment on them. What’s more interesting is that after learning things from this article, going forward you will be able to implement a lot of other features on your own.

I’ll be using PHP as the coding language, MongoDB as the database and jQuery for JavaScript operations. It is assumed the reader is familiar with the basics of PHP and MongoDB to understand the article thoroughly. However, note that the article is written in a very generic and newbie-friendly manner and there is a lot to learn from it. So, even if you are not using Mongo DB or jQuery, you can easily adapt to or replace those technologies with those you prefer.

There are two articles in this series. In the first article, we will learn the database architecture, design the stream and think up the application flow structure that we are going to adapt. In the next article of this series, we will write the code which will eventually allow us to create a new post, like/unlike a post and comment on it. Understand that the first article is more of a base for the functionalities we are going to implement in the next part. As such, there will be cases where you will not understand the actual implementation logic of certain things until you find out more in the second article. The articles are accompanied by downloadable code for your reference. Please note that this code is written in procedural form and as such definitely not a best practice. This simplified form was chosen to make the implementation itself more newbie-friendly, and this post mechanism will be re-implemented in an object oriented manner in a more complex article down the line.

Before getting started, let us look at the final work we will develop (Fig 1 ).

Download Code:

Download the code provided with the article from its Github repo. You will find the following files in it:

index.php : This is the main file which displays all the posts and from where the user will interact.

mongo_connection.php : This file contains the common MongoDB connection code. If you have made any changes to the MongoDB connection credentials, you should specify them here. In all other PHP files, we will include this file to use the common MongoDB connection code. Make sure MongoDB is installed by following the installation instructions and installing the PHP drivers.

session_variables.php : The article expects that we have some common fields such as user id, user name and user’s profile picture stored in session variables as they are very frequently used throughout the application. Generally such information is set when the user logs in but for the purpose of the article, we will set it in this file.

script.js and style.css contains all the Javascript and CSS code. jquery.js is the standard jQuery library file.

php_scripts : This folder contains all the PHP files that we will call through AJAX to do PHP operations for inserting the post, performing like/unlike on the post and inserting comments.

images : This folder contains profile pictures of all the users.

Database Structure:

The users collection will be a simple one containing user id, user name and profile picture of the user as shown below:

{
   "_id": ObjectId("5222f8e0d85242c01100000d"),
   "name": "Richard Garry",
   "profile_pic": "profile_pic_3.jpg" 
}

Another collection posts_collection will maintain all the information related to posts as shown in the sample entry below:

{
   "_id": ObjectId("5222f885d85242c011000009"),
   "post_author_id": ObjectId("5146bb52d8524270060001f4"),
   "post_text": "Let's get started !!!\t\t\t",
   "timestamp": "Sun, 01-Sep-2013",
   "total_comments": NumberInt(3),
   "total_likes": NumberInt(2) ,
   "likes_user_ids": {
     "0": ObjectId("52147f70d85242cc12000010"),
     "1": ObjectId("5222f8e0d85242c01100000d") 
  },
   "comments": {
     "0": {
       "comment_id": ObjectId("5222f897d85242c01100000a"),
       "comment_user_id": ObjectId("52147f70d85242cc12000010"),
       "comment_text": "This is comment 1 !!" 
    },
     "1": {
       "comment_id": ObjectId("5222f8a7d85242c01100000b"),
       "comment_user_id": ObjectId("5222f8e0d85242c01100000d"),
       "comment_text": "This is comment 2 !!" 
    } 
  }
}

Most of the fields in the document are self-explanatory. likes_user_ids contains user ids of all the users
who have liked the post. comments is an array which contains sub-documents to store the comments information.

Understanding Stream Design:

Let us first place a text area where the user will enter the post text and a button Create New Post (as shown in Fig 2 ) using the following code:

<div id="div_post_content">
    <textarea id="post_textarea">
    </textarea>
</div>
<div class="div_post_submit">
    <input type="button" value="Create New Post" id="btn_new_post" class="button_style"/>
</div>

Our index page displays all the posts in a stream-like view. Looking at the code in index.php, we will explain how this data is fetched and displayed one step at a time:

We first fetch all the posts from the database and sort them with descending _id so as to get the last inserted posts first.

$posts_cursor=$collection->find()->sort(array('_id'=>-1));

Now, we iterate over all the fetched posts using foreach and get the post id, post text and user id of post author:

foreach($posts_cursor as $post)
{
    $post_id=$post['_id'];
    $post_text=$post['post_text'];
    $post_author_id=$post['post_author_id'];
    …

Using the user id, we can now get the user name and profile picture link from the users collection.

$collection = $database->selectCollection('users');
$post_author_details = $collection->findOne(array('_id' =>$post_author_id));
$post_author = $post_author_details['name'];
$post_author_profile_pic = $post_author_details['profile_pic'];

Understand that on our page, we will have many posts displayed together. Each post will have its Like button, like count, comments and comments count. We need to uniquely identify each of these things for each individual post so that when the user does any operation on any post (liking, commenting), we know which post to update. For this, we will initialize some variables which we will use as id of corresponding HTML elements later:

$post_like_unlike_id=$post_id.'_like_unlike';
$post_like_count_id=$post_id.'_like_count';
$post_comment_count_id = $post_id.'_comment_count';
$post_self_comment_id=$post_id.'_self_comment';
$post_comment_text_box_id=$post_id.'_comment_text_box';

We now check whether the current user has already liked the post or not. For this, we check if the user id of the current user is present in likes_user_ids array fetched from database. If the user has already liked the post, we set $like_or_unlike variable to display Unlike. But if the user has still not liked the post, we need to display Like option. We will use $like_or_unlike later in our HTML to display Like or Unlike option.

if(in_array($_SESSION['user_id'],$post['likes_user_ids']))
{
   $like_or_unlike='Unlike';
}
else
{
 $like_or_unlike='Like';
}

// you can also write this in short ternary form:

$like_or_unlike = (in_array($_SESSION['user_id'],$post['likes_user_ids'])) ? 'Unlike' : 'Like';

We will now use the variables initialized above and create the HTML structure to display the post. Each post_wrap div will display one post and the id of this post_wrap will be the post id drawn from the database.

<div class="post_wrap" id="<?php echo $post['_id'];?>">
<div class="post_wrap_author_profile_picture">
    <img src="images/<?php echo $post_author_profile_pic;?>" />
</div>  
<div class="post_details">  
          <div class="post_author">
        <?php echo $post_author ?> 
    </div>
    <div class="post_text">
        <?php echo $post_text; ?>
    </div>
</div>

Now let's show the number of likes and comments. Note that we had initialized some variables in the previous steps. Those variables are going to be used here. $like_or_unlike displays the Like or Unlike text set earlier while $post_like_count_id and $post_comment_count_id are used to give ids of spans displaying the likes and comments count.

You will understand the use of assigning these ids in the next article, where we will have to get data and update it for each individual post.

<div class="comments_wrap">
    <span>
        <span><img src="images/like.png" /></span>     
        <span class="post_feedback_like_unlike" id="<?php echo $post_like_unlike_id;?>"><?php                  echo $like_or_unlike; ?></span>                    
        <span class="post_feedback_count" id="<?php echo $post_like_count_id; ?>"><?php echo                    $post_like_count;?></span>
    </span>
    <span>
        <span class="post_feedback_comment"> <img src="images/comment.png" /> Comment</span>               <span class="post_feedback_count" id="<?php echo $post_comment_count_id; ?>"><?php echo                     $post_comment_count;?></span>
    </span>
</div>

The above code would result in something like this (Fig 3):

Now, to show the comments related to each post, we iterate over the comments array retrieved from database and initialize variables to be used in HTML. For each comment, we perform a find query on the users collection to fetch the comment author’s name and profile picture link.

for($i=0;$i<$post_comment_count;$i++)
{                                           
    $comment_id=$post['comments'][$i]['comment_id'];                           
    $comment_text=$post['comments'][$i]['comment_text'];                   
    $comment_author_id=$post['comments'][$i]['comment_user_id'];
    $collection = $database->selectCollection('users');
    $comment_author_details = $collection->findOne(
    array('_id' => new MongoId($comment_author_id))
    );                             
    $comment_author = $comment_author_details['name'];
    $comment_author_profile_pic = $comment_author_details['profile_pic'];

Following is the HTML code to display a single comment using the variables initialized above: (Output: Fig 4)

<div class="comment" id="<?php echo $comment_id; ?>">                 
    <div class="comment_author_profile_picture">
        <img src="images/<?php echo $comment_author_profile_pic; ?>"/>
    </div>
    <div class="comment_details">
        <div class="comment_author" >
            <?php echo $comment_author; ?>
        </div>
        <div class="comment_text" >
            <?php echo $comment_text; ?>
        </div>
    </div>
</div>

After displaying all the comments fetched form the database, we want to show a blank comment box wherein the current user can comment.

<div class="comment" id="<?php echo $post_self_comment_id; ?>">
    <div class="comment_author_profile_picture">
        <img src="images/<?php echo $_SESSION['user_profile_pic']; ?>" />
    </div>
    <div class="comment_text">
        <textarea placeholder="Write a comment..." id="<?php echo $post_comment_text_box_id;?>" >
        </textarea>
    </div>
</div>

Application Flow Structure:

Let us now understand how the application flow will work by taking an example of inserting a new post. Don’t worry about the coding part for now as we are going to see that in the next article.

  • The first PHP file (index.php) is the main page from where the user will enter the post text and click on Create New Post button. For simplicity's sake, we'll put a JavaScript function new_post as the click handler of this button and pass the post_text and user_id as its parameters.

  • The JavaScript function will receive these parameters and send an AJAX POST request to a PHP file (insert_new_post.php) passing forward the same parameters it received.

  • This PHP file does the work of inserting the received post text into the database. After doing the database operation, it prepares HTML content containing the new
    post which will be sent as output of the JavaScript function. This output will later be rendered on the main page. In reality, the PHP function should return JSON to reduce bandwidth, and the view file should just include the data into a prepared template, but seeing as our app is just a demonstration, this will do.

  • The JavaScript function receives the output sent by the PHP file and inserts it into the main page HTML.

The flow for all other operations (like commenting, liking/unliking post) is going to be similar to what we just saw for the example of inserting a post.

Conclusion:

In this article, we explained the database architecture, post stream design and application flow. In the next part of this series, we are going to use these structures and develop our functionality of inserting posts, like/unlike and commenting.

Social Network Style Posting with PHP, MongoDB and jQuery

Social Network Style Posting with PHP, MongoDB and jQuery – part 2 >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Alex Fraundorf

    Very nice article. As you said, the code should be very easy to customize. I’m looking forward to part 2.

  • Taylor Ren

    Hope to see the “interactivity” part of it.

  • Anonymous

    Social Giant made a huge mistake of using mongoDB for social network. Read this before you even think about using it. http://www.sarahmei.com/blog/2013/11/11/why-you-should-never-use-mongodb/

  • Ashish

    @Nilesh: There are plus and minus of every technology you use. We have our application built on this technology since last 2 years and we are quite satisfied with the results. Though some of the points in the article makes sense, it is surely a matter of specific application.

  • comrd

    very good

  • levani01

    “findOne” in a loop? No thanks! I’d rather collect all user ids in a separate array and fetch all users in one query using the $in operator.