# Hands-on Project: Twitter Clone - Tweet, User Feed and Profile

September 20, 2018

Welcome to the second part of the three part PHP Twitter Clone project. In this part, you will add the following functionalities to the Twitter Clone: (a) user can post tweets, (b) the tweets will be displayed in the user feed, and (c) profile page for the users. Let’s begin!

# Step 1: Tweets table

We have to create a table to store all the tweet information. Go ahead and create the tweets table in phpMyAdmin inside the twitter database.

What columns should the table have? What attributes should each column have? What should be the max-length for the tweet text?

Solution to this section can be found in section Solution: Tweets table below.

# Step 2: Submitting tweets

In home.php, you will see a text area in which a user can type in a tweet.

Once the user presses the Tweet button, we want to do the following:

1. Check if the tweet is empty or if the tweet length exceeds 140 characters and display appropriate error messages by setting the $error variable. 2. If the tweet has no issues, insert the tweet information into the tweets table you just created. Hint: For the time at which the tweet was posted, use the date("Y-m-d H:i:s") function to get the date and time in the form yyyy-mm-dd hr-min-sec (this is the datetime format accepted in SQL). Your browser might re-submit a form when you refresh. This might cause duplicate tweets if you refresh your browser right after tweeting. Checkpoint: Refresh the page and write a tweet (for example: “Hello there!”) and click the Tweet button. You won’t see anything happening in the home page. But if you go to phpMyAdmin and check the tweets table, you will find your tweet there: Also check if you get the appropriate error when you submit an empty tweet or a tweet which exceeds the maximum limit of 140 characters. Solution to this section can be found in section Solution: Submitting tweets below. Amazing! You created the tweets table and wrote PHP code to insert the tweet information into this table. # Step 3: User Feed Okay, so the tweets are stored in the tweets table. What next? We want to: 1. Display the tweets on the home page. Thus, home.php will now contain the user feed. 2. Display the number of tweets posted by the user in the user feed page. Open the project-resources folder and replace the HTML in home.php (that you copied from home_1.html) with the HTML inside home_2.html. Let’s create two methods in the Tweet class which will perform the above tasks. Here’s the code template to get you started: <!-- tweet.php --> <?php class Tweet extends Base { function __construct($pdo) {
$this->pdo =$pdo;
}
public function tweets($user_id) { // Your code here ... // Fetch tweets authored by user_id. Sorted by newest first. // (You will need to do a JOIN query on users and tweets tables. // Store the result in$tweets
foreach ($tweets as$tweet) {
echo '<div class="all-tweet">
<div class="t-show-wrap">
<div class="t-show-inner">
<div class="t-show-popup">
<div class="t-show-img">
<img src="'. $tweet->profileImage .'"/> </div> <div class="t-s-head-content"> <div class="t-h-c-name"> <span><a href="profile.php?username='.$tweet->username .'">'. $tweet->fullname .'</a></span> <span>@'.$tweet->username .'</span>
<span>'. $tweet->postedOn .'</span> </div> <div class="t-h-c-dis"> '.$tweet->status .'
</div>
</div>
</div>
</div>
<div class="t-show-footer">
<div class="t-s-f-right">
<ul>
<li><button><a href="#"><i class="fa fa-retweet" aria-hidden="true"></i></a></button></li>
<li><button><a href="#"><i class="fa fa-heart-o" aria-hidden="true"></i></a></button></li>
<li>
<a href="#" class="more"><i class="fa fa-ellipsis-h" aria-hidden="true"></i></a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>';
}
}
public function countTweets($user_id) { // Your code here ... // echo count of number of tweets by user user_id. } } ?> Here, the tweets() method displays the tweets enclosed in HTML tags using a foreach-loop and echo statement. The HTML assumes that the columns of the tweet table have specific names — such as status, postedOn, etc. Edit the PHP inside the above HTML if you chose different column names. Note: Later on (in part 3 of the project), you will need to update the query inside the tweets() method to also display tweets from users that the current user is following. Checkpoint: That’s all! Refresh the home page and you will see something like this: User Feed in home page You will see the tweet you just posted and on the left hand side, the tweets count has been updated to one! Post a couple tweets to ensure that everything is working as expected. Solution to this section can be found in section Solution: User Feed below. # Step 4: User Profile When you click on the profile image icon on the top right of the page, you see two options: one is the username of the user (let’s call it profile) and the other is logout. On clicking the first option, we want the user to be redirected to the profile page. Create a profile.php page inside the main directory (i.e. the twitter folder) with open and close PHP tags. <!-- profile.php --> <?php ?> Now open the project-resources folder and copy the HTML from profile_1.html and paste it below the PHP tags. The way the profile page works is, when the user clicks on the profile link, we send the username of the user through a GET request. The URL is of the form http://localhost/twitter/profile.php?username=jamesmat. In profile.php, you need to get the username from the GET request. After you get the username, you need to: 1. Get the user_id from the username. For this, you have to create a method userIdByUsername() in the User class which given a username, returns the user_id. Assign this to a new variable called profileId. 2. If the username doesn’t exist in the users table or if someone is trying to access profile.php without logging in (you have already implemented this in home.php), redirect to index.php. 3. Get the profile data like fullname, username and profileImage using the profileId that you just got in the previous step. (Tip: Use the userData() method in the User class). 4. Get all the tweets of the current user. Create another method in the Tweet class which fetches all tweets for a given profileId. Call it getUserTweets(). 5. Go over the php tags in the HTML and make sure you have instantiated all the variables that are needed for the page — $user, $profileId, and $profileData.

Create the methods as explained above and then call them in profile.php.

Take a look at the HTML code (it includes some PHP code). You will see which variables and properties are expected to be there.

Sometimes, there might be errors in your PHP code but you might not be able to see it because the error message is hidden somewhere in the page HTML. The easiest way to spot any error message is to right click anywhere on Chrome when you have the site open, and go to View Page Source. That gives you access to the raw HTML of the page, which will include any error messages or notice PHP may have encountered.

Solution to this section can be found in section Solution: User Profile below.

Checkpoint: Refresh the home page and click on the profile link on the top right of the screen. This is what you should see:

User profile

Try to change the username in the URL and see if the corresponding user’s profile is displayed.

You just created the user profile page! Awesome!

# Summary

In this part of the project, you created the user feed and the user profile page.

Users can now post tweets and look at other users’ tweets by visiting their profile page through the URL (we will add the search feature in the next part to directly search for a user instead of having to type out the URL).

If you lost track, got stuck, or just need to double check, the solution for each of the above steps is included below. You can also find all the code we have written so far here (that is, what your current code should look like).

In the next (and last) part of the project, you will work add the follow / unfollow feature and the ability to search for a user.

# Solution: Tweets table

The tweets table should have 4 columns:

tweets table

Here, status refers to the tweet text which has a limit of 140 characters (just like in Twitter!). tweetBy stores the user_id of the user who tweeted the message. postedOn stores the date and time when the tweet was created which has a type of DATETIME.

Here is what the table should look while creating:

Make sure A_I is checked for tweetId and it is the PRIMARY key in the table.

# Solution: Submitting tweets

home.php after making the changes to accept tweet:

<!-- home.php -->
<?php
include 'core/init.php';
$user_id =$_SESSION['user_id'];
$user =$getFromU->userData($user_id); if ($getFromU->loggedIn() === false) {
}
if (isset($_POST['tweet'])) {$status = $getFromU->checkInput($_POST['status']);
if (!empty($status) && strlen($status) < 140) {
$getFromU->create('tweets', array('status' =>$status, 'tweetBy' => $user_id, 'postedOn' => date("Y-m-d H:i:s"))); } elseif (!empty($status) && strlen($status) > 140) {$error = "The post is too long! Limit: 140 characters";
}
else {
$error = "Please type something to post!"; } } ?> <!-- html goes here --> You should be familiar with most of this code. The only new thing here is the postedOn field is set to date("Y-m-d H:i:s"). This simply says we want the date and time in the form yyyy-mm-dd hr-min-sec as this is the datetime format accepted in SQL. # Solution: User feed <!-- tweet.php --> <?php class Tweet extends Base { function __construct($pdo) {
$this->pdo =$pdo;
}
public function tweets($user_id) {$stmt = $this->pdo->prepare("SELECT * FROM tweets, users WHERE (tweetBy = user_id AND user_id = :user_id) ORDER BY tweetId DESC");$stmt->bindParam(":user_id", $user_id, PDO::PARAM_INT);$stmt->execute();
$tweets =$stmt->fetchAll(PDO::FETCH_OBJ);
foreach ($tweets as$tweet) {
echo '<div class="all-tweet">
<div class="t-show-wrap">
<div class="t-show-inner">
<div class="t-show-popup">
<div class="t-show-img">
<img src="'. $tweet->profileImage .'"/> </div> <div class="t-s-head-content"> <div class="t-h-c-name"> <span><a href="profile.php?username='.$tweet->username .'">'. $tweet->fullname .'</a></span> <span>@'.$tweet->username .'</span>
<span>'. $tweet->postedOn .'</span> </div> <div class="t-h-c-dis"> '.$tweet->status .'
</div>
</div>
</div>
</div>
<div class="t-show-footer">
<div class="t-s-f-right">
<ul>
<li><button><a href="#"><i class="fa fa-retweet" aria-hidden="true"></i></a></button></li>
<li><button><a href="#"><i class="fa fa-heart-o" aria-hidden="true"></i></a></button></li>
<li>
<a href="#" class="more"><i class="fa fa-ellipsis-h" aria-hidden="true"></i></a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>';
}
}
public function countTweets($user_id) {$stmt = $this->pdo->prepare("SELECT COUNT(tweetId) AS totalTweets FROM tweets where tweetBy = :user_id");$stmt->bindParam(":user_id", $user_id, PDO::PARAM_INT);$stmt->execute();
$count =$stmt->fetch(PDO::FETCH_OBJ);
echo $count->totalTweets; } } ?> # Solution: User Profile We first create the userIdByUsername() method inside the User class. public function userIdByUsername($username) {
$stmt =$this->pdo->prepare("SELECT user_id FROM users WHERE username = :username");
$stmt->bindParam(":username",$username, PDO::PARAM_STR);
$stmt->execute();$user = $stmt->fetch(PDO::FETCH_OBJ); return$user->user_id;
}

Then create the getUserTweets() method inside the Tweet class.

public function getUserTweets($user_id) {$stmt = $this->pdo->prepare("SELECT * FROM tweets, users where tweetBy = user_id AND user_id = :user_id");$stmt->bindParam(":user_id", $user_id, PDO::PARAM_INT);$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_OBJ); } And finally, we will call these functions in profile.php. <!-- profile.php --> <?php if (isset($_GET['username']) === true && empty($_GET['username']) === false) { include 'core/init.php';$username = $getFromU->checkInput($_GET['username']);
$profileId =$getFromU->userIdByUsername($username);$profileData = $getFromU->userData($profileId);
$tweets =$getFromT->getUserTweets($profileId);$user_id = $_SESSION['user_id'];$user = $getFromU->userData($user_id);
if(!$profileData || ($getFromU->loggedIn() === false)) {
<!-- html goes here -->