It's pretty simple to create notifications for one type, but if you are in the same position as I was, you need multiple such as mail, comment, friend requests and probably more. I created a quiz site (http://www.presskick.com) that requires a manifold of notifications and it had me stumped for a while.
In the beginning, I just had mail and friend request notifications. What I had set up effectively notified the user, but it wasn't really a notification system per se. It was just pulling data from a friend table and mail table where the friend request hasn't been handled and the mail hasn't been read. That would work, but it wasn't very flexible.
What I needed to do was make notifications its own entity. A good place to start is to get the database structure squared away so that you'll know what needs to be retrieved and inserted.
Database Schema
CREATE TABLE IF NOT EXISTS `notification` (<br />
`id` int(10) NOT NULL AUTO_INCREMENT,<br />
`to_user` int(10) NOT NULL DEFAULT '0',<br />
`from_user` int(10) NOT NULL DEFAULT '0',<br />
`reference` int(10) NOT NULL DEFAULT '0',<br />
`type` enum('friend_request','mail','profile_comment','photo_comment') NOT NULL,<br />
`seen` tinyint(4) NOT NULL DEFAULT '0',<br />
`timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,<br />
PRIMARY KEY (`id`),<br />
KEY `to_user` (`to_user`),<br />
KEY `from_user` (`from_user`),<br />
KEY `reference` (`reference`)<br />
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Most of these fields should be self explanatory except for maybe "reference." The "reference" will be the key from another table like mail id or comment id. It could also be a profile id, but that would be ambiguous since that would most likely be the same as "from_user." "timestamp" is obviously to indicate when the notification was added. "seen" is used to determine if the notification has been seen. If it's been seen, it can still be shown, but they won't be alerted about it on the site. I would also like to add, you can replace the "reference" field with a field that contains all the html of the notification. This would be the easiest, most laziest method, however, it would be a waste of disk space.
Next, we should probably create a Notification class. This class will handle retrieving, adding and deleting notifications.
Notification Class
<?php
class Notification {
var $type;
var $to_user;
var $from_user;
var $reference;
var $timestamp;
var $newcount;
public function getAllNotifications() {
$this->newcount = Notification::newCount($this->to_user);
$sql = "SELECT n.*,u.defaultpic,u.username FROM notification n INNER JOIN user u ON u.user_id = n.to_user ORDER BY `timestamp` DESC LIMIT 10";
$result = mysql_query($sql);
echo mysql_error();
if ($result) {
return $result;
}
return false; //none found
}
public function Add() {
$sql = "INSERT INTO notification (to_user,from_user,reference,type) VALUES ({$this->to_user},{$this->from_user},{$this->reference},'{$this->type}')";
mysql_query($sql);
}
static function Seen($id) {
if (!isset($_SESSION['id'])) exit;
$sql = "UPDATE notification SET seen = 1 WHERE id = {$id} AND to_user = {$_SESSION['id']}";
mysql_query($sql);
}
static function newCount($user) {
$sqlcnt = "SELECT count(*) FROM notification WHERE to_user = {$user} AND seen = 0";
$result = mysql_query($sqlcnt);
$row = mysql_fetch_row($result);
return $row[0];
}
static function deleteNotification($id) {
if (!isset($_SESSION['id'])) exit;
$sql = "DELETE FROM notification WHERE id = {$id} AND to_user = {$_SESSION['id']}";
mysql_query($sql);
}
}
?>
The getAllNotifications method returns everything in the notification table and the default user pic and username. Of course, the structure of your own user table may be different, so edit it as needed.
The Seen method will be called to update all the notifications "seen" field when the notifications menu is opened.
The deleteNotification method will delete the notification permanently, and it will never be displayed again.
Now, we need a file that will utilize this class. This file will be called asynchronously and output the html for recent notifications.
The Add notification adds a new notification. To add a notification for a new mail message, for example, do the following:
$notification = new Notification();
$notification->to_user = $to_user_id;
$notification->from_user = $from_user_id;
$notification->type = "mail";
$notification->reference = $mail_id;
$notification->Add();
The Notifications Ajax File
<?php
if (!isset($_SESSION['id'])) exit;
include("Notification.php");
$id = $_SESSION['id'];
$notification = new Notification();
$notification->to_user = $id;
$notifications = $notification->getAllNotifications();
if ($notifications) {
echo $notification->newcount . "|";
$unseen_ids = array();
while ($object = mysql_fetch_object($notifications)) {
if ($object->seen == 0) $unseen_ids[] = $object->id;
switch($object->type) {
case "friend_request":
?>
<li id="notification_<?=$object->id;?>">
<div style="width:350px;padding:5px;">
<a href="profile.php?id=<?=$object->from_user;?>"><img src="<?=$object->defaultpic;?>" style="float:left;" width="50px" height="50px"/> <?=$displayName;?></a> would like to be your friend!<br />
<a href="#" onclick="HandleRequest('accept','<?=$object->from_user;?>');">Accept</a> <a href="#" onclick="HandleRequest('deny','<?=$object->from_user;?>');">Deny</a>
</div><br style="clear:both;"/>
</li>
<?php
break;
case "mail":
?>
<li id="notification_<?=$object->id;?>">
<div style="width:350px;padding:5px;">
<a href="profile.php?id=<?=$object->from_user;?>"><img src="<?=$object->defaultpic;?>" width="50px" height="50px"/></a> <a href="message.php?id=<?=$object->reference;?>"><?=$displayName;?> has sent you a message!</a>
<a href="javascript:void(0)" onclick="DeleteNotification(<?=$object->id;?>)" style="float:right;"><i class="icon-trash"></i></a>
</div>
</li>
<?php
break;
//TODO: add cases for other notifications
}
}
echo "|".json_encode($unseen_ids);
}
?>
This file retrieves the notifications and outputs the html. The count of new notifications is outputted followed by a pipe (|) symbol. I'm using the pipe to separate the count and the html of the notifications, and our js file will handle parsing it. After the count is outputted, we loop through the notifications, check the type and output the html appropriately. Finally, after the loop, we output the possible unseen ids.The Update Notifications Ajax file
<?php
if (!isset($_SESSION['id'])) exit;
include("Notification.php");
$id = $_SESSION['id'];
$action = $_REQUEST['action'];
switch($action) {
case "seen":
if (isset($_REQUEST['notifications'])) {
$notifications = json_decode($_REQUEST['notifications']);
foreach ($notifications as $notification) {
if (is_numeric($notification)) Notification::Seen($notification);
}
}
break;
case "delete":
$notification = $_REQUEST['notification'];
if (is_numeric($notification)) Notification::deleteNotification($notification);
break;
}
?>
Depending on the action sent, this file deletes a notification or updates the "seen" field of a list of notifications.
The JS File
var unseen_id_array = new Array();
function CheckUpdates()
{
jQuery.ajax({url:"ajax/notifications.php", dataType:"html", success: function(msg) {
if (msg != "") {
var result = msg.split("|");
var unseen = parseInt(result[0]);
var notifications = result[1];
var unseen_ids = result[2];
if (unseen > 0) {
$('#notification-badge').css("display", "inline");
$('#notification-badge').html(unseen);
for (i = 0; i < unseen_ids.length; i++) {
unseen_id_array.push(unseen_ids[i]);
}
}
jQuery('#notifications').html(notifications);
} else {jQuery('#notifications').html("No notifications...");}
}
})
}
function DeleteNotification(id) {
jQuery.ajax({url:"ajax/update_notifications.php", data:"notification="+id+"&action=delete", dataType:"html", success: function(msg) {
$("#notification_"+id).hide();
}
});
}
function SeenNotification() {
jQuery.ajax({url:"ajax/update_notifications.php", data:"notifications="+JSON.stringify(unseen_id_array)+"&action=seen", dataType:"html", success: function(msg) {
setTimeout(function() {
$('#notification-badge').css("display", "none");
$('#notification-badge').html("");
},1000);
}
});
}
$(document).ready(function() {
$('.notifications').click(function() {
//TODO: stop CheckUpdates interval and restart menu closes
SeenNotification();
});
$('.dropdown-menu').click(function(event){
event.stopPropagation();
});
})
CheckUpdates();
var intervalId = setInterval(CheckUpdates,60000);
function CheckUpdates()This function utilizes the very useful jquery ajax method to call our ajax file and store its results in the "msg" variable. At the start, you can see that the result variable is an array that contains the unseen count, notifications and unseen ids. We then check if "unseen" is greater than 0, and if it is, we display our badge (Labels and Badges) with the count. The global array, unseen_id_array
is populated after. "#notifications" is the id of an empty UL element, which we embed the list of LI elements - containing our notifications - in it.
function DeleteNotification(id)
This function makes a call to our update_notifications (or whatever you call it) file and passes the notification id and action type. Then it simply removes that li tag using it's id.
function SeenNotification()
Like DeleteNotification, this function makes a call to update_notifications, passing a list of ids and the action type. If it's successful, it will remove the html from the badge and hide it. I placed that in a setTimeout function so the badge is remove a second after. That is a personal preference of mine.
I binded a couple of events to trigger after the document is loaded. SeenNotification() is called when the notifications link is clicked. Then I prevented the menu from closing by using event.stopPropagation(). Without that, the menu will close when you click anywhere in the menu like the trash icon.
Relevant HTML
<ul class="nav">
<li class="dropdown"><a class="dropdown-toggle notifications" data-toggle="dropdown" href="#"><strong>Notifications</strong> <span id="notification-badge" class="badge badge-important"></span></a>
<ul id="notifications" class="dropdown-menu" role="menu" aria-labelledby="dLabel"></ul>
</li>
</ul>
As the title mentions, I'm using twitter bootstrap, so the bulk of the front-end comes from it. I just added an empty menu item to server as our notifications. Visit the Bootstrap Navigation section to learn more about it. I also added the trash icon (See bootstrap icons) and when clicked, it calls DeleteNotification(id).
Hi,
ReplyDeleteYou have did some awesome work.
But i need this to make it work with Codeigniter.Please send me if you have code in codeigniter.
Thanks & Regards,
Zeeshan.
Sorry, I'm not familiar with codeigniter.
Deletehey i can't get about the use of twitter bootstrap here in this code.can u please mention me where it is used and how it is implemented?
ReplyDeleteHave you added the bootstrap framework? http://getbootstrap.com/
DeleteI believe that I have for the most part everything setup correctly. The issue that I have is that "No notifications…" is displayed even though I have a test notification in my database. What would you recommend?
ReplyDeleteThe "ajax/notification.php" file assumes you are a logged in user. $_SESSION['id'] is supposed to be your user id. I think that may be the problem. Try going directly to that page and see if what errors you get. You probably already resolved this, but I'm responding anyway.
DeleteSorry if I misunderstood your class. But in the getAllNotifications function, in your select query, don't you need a 'where' clause to list only notification to a given user?
ReplyDeleteThanks,
Yes, you're correct, there needs to be a where clause. Also, the "echo mysql_error()" was debugging code I left in there and should be removed.
ReplyDeleteSorry for extremely late response. I'm just getting back to the grind, and will be around more often now.
thanks for your update,
ReplyDeleteHow will i get this working on adminlte?
ReplyDeleteHow will i get this working on adminlte?
ReplyDelete