ساخت نوتیفیکیشن با php و Ajax ( اطلاع رسانی کاربران )

ساخت نوتیفیکیشن با php – خیلی از مواقع در فیسبوک شاهد این بوده ایم که رویداد های مختلفی را با استفاده از زنگوله بالا سمت راست متوجه شده ایم و کاربران را اطلاع رسانی کرده است .
در این آموزش همان زنگوله را قرار هست که نمونه آن را بسازیم آن هم به صورت AJAX .
این آموزش شامل 6 فایل می باشد برای ساخت برنامه نوتیفیکیشن با php :
- اسکریپت index.php : صفحه نمایش نوتیفیکیشن ها
- اسکریپت notify.php : این صفحه معمولا در صفحه ادمین می باشد که 2 فیلد پیام و لینک را دریافت و به کاربر ارسال می کند
- اسکریپت db.php : همان طور که از اسمش پیداست برای تعامل با دیتابیس به کارمان می آید
- اسکریپت api.php : از آنجایی که قرار است درخواست های ajax بفرستیم نیازمند یک آدرس استاندارد شده برای ارسال و دریافت اطلاعات آن هم به صورت json هستیم که با دیتابیس اطلاعات ذخیره و خوانده شود
- اسکریپت app.js : درخواست های http و رویداد و رفتار های برنامه را با این اسکریپت javascript کنترل می کنیم .
- استایل main.css : برای زیبایی برنامه مان و رعایت چیدمان المان های html
دموی برنامه ساخت نوتیفیکیشن با PHP
1- اسکریپت index.php
<!DOCTYPE html>
<html lang="fa">
<head>
<meta charset="UTF-8">
<title>Rapidcode.iR - سورس کد</title>
<link rel="stylesheet" href="static/css/main.css">
</head>
<body style="height: 3000px">
<div class="container">
<h1>رپید کد</h1>
<header id="menu-1" class="menu">
<nav>
<ul>
<li><a href="https://rapidcode.ir">رپید کد </a></li>
<li><a href="#">صفحه 1</a></li>
<li><a href="#">صفحه 2</a></li>
<li><a href="#">صفحه 3</a></li>
<li><a href="#">صفحه 4</a></li>
<li><a href="#">صفحه 5</a></li>
<div class="notification-wrapper">
<div class="notification-bell"></div>
<div class="notification-num">0</div>
<div class="notification-messages">
<span class="arrow"></span>
<ul class=""></ul>
</div>
</div>
<div class="clearfix"></div>
</ul>
</nav>
</header>
</div>
<script src="static/js/app.js"></script>
</body>
</html>
2- اسکریپت notify.php
<!DOCTYPE html>
<html lang="fa">
<head>
<meta charset="UTF-8">
<title>Rapidcode.iR - سورس کد</title>
<link rel="stylesheet" href="static/css/main.css">
</head>
<body>
<div class="container">
<h1>رپید کد</h1>
<form id="form-wrapper">
<textarea name="notify-message" id="notify-message" cols="30" rows="10" placeholder="پیام"></textarea>
<input name="notify-link" id="notify-link" type="text" dir="ltr" placeholder="لینک">
<input id="notify-send" type="button" value="ارسال">
</form>
</div>
<script src="static/js/app.js"></script>
</body>
</html>
3- اسکریپت db.php
<?php
$mysqli = new mysqli("localhost", "root", "", "cms");
if ($mysqli->connect_error) {
die("MYSQL ISSUE : " . $mysqli->connect_error);
}
$mysqli->set_charset("utf8");
$stmt = $mysqli->stmt_init();
function getData($uquery,$data=[]){
global $mysqli;
global $stmt;
$query = $uquery;
$rows = [];
$stmt->prepare($query);
if(sizeof($data))
$stmt->bind_param(str_repeat("s",count($data)), ...$data);
if ($stmt->execute() && $res = $stmt->get_result()) {
if ($stmt->affected_rows || $res->num_rows) {
while ($row = $res->fetch_assoc()) {
$rows[] = $row;
}
}
}
return $rows;
}
function setData($uquery , $data){
global $mysqli;
global $stmt;
$query = $uquery;
$stmt->prepare($query);
$type = str_repeat("s",count($data));
$stmt->bind_param($type, ...$data);
$inserted_id=0;
if ($stmt->execute()) {
$inserted_id = $stmt->affected_rows ? $stmt->insert_id : 0;
}
return $inserted_id;
}
function check_posted_data($posted_data , $list_post_keys){
$is_valid_posted_data = array_map(function($name){
global $posted_data;
return in_array($name , array_keys($posted_data)) && !empty($posted_data[$name]);
}, $list_post_keys);
return in_array(false,$is_valid_posted_data);
}
?>
4- اسکریپت api.php
<?php
header("Content-Type: application/json");
$action = @$_GET['action'];
$valid_actions = ['snd' , 'rcv'];
if(!in_array($action, $valid_actions)){
die('{"msg" : "action نامعتبر", "status" : 0}');
}
require_once "db.php";
$posted_data = @$_POST;
$posted_data_name_list = [
"snd" => ['notify-message' , 'notify-link'],
"rcv" => ['offset'],
];
$posted_data_name = $posted_data_name_list[$action];
$is_false = check_posted_data($posted_data , $posted_data_name);
if($is_false)
die('{"msg" : "فیلد های اجباری وارد نشده است", "status" : 0}');
if($action == "snd"){
$tmp_posted_data = [
$posted_data['notify-message'],
$posted_data['notify-link'],
date("Y/m/d")
];
$res = setData("INSERT INTO `notification` (`message` , `link` , `created_at`) VALUES (? , ? , ?)" , $tmp_posted_data);
if($res)
die('{"msg" : "با موفقیت ذخیره شد" , "status" : 1}');
else
die('{"msg" : "خطایی در هنگام ذخیره اطلاعات رخ داده" , "status" : 0}');
}else if($action == "rcv"){
$special_statement = [
'all' => 'SELECT * FROM `notification`',
'limit' => 'SELECT * FROM `notification` ORDER BY id DESC LIMIT 1'
];
$offset = @$special_statement[$posted_data['offset']];
if(!$offset)
$offset = $special_statement['all'];
$rows = getData($offset , []);
$response_list = ['message' => 'اطلاعات با موفقیت از دیتابیس بارگذاری شد', 'status' => 1 , 'data' => []];
foreach ($rows as $row) {
array_push($response_list['data'], $row);
}
die(json_encode($response_list));
}
?>
5- اسکریپت app.js
// DOM
const notifyFormDOM = document.getElementById('form-wrapper');
const notifyMessageDOM = document.getElementById('notify-message');
const notifyLinkDOM = document.getElementById('notify-link');
const notifySendDOM = document.getElementById('notify-send');
const messagesNumDOM = document.getElementsByClassName('notification-num')[0];
const messagesBellDOM = document.getElementsByClassName('notification-bell')[0];
const messagesMessagesDOM = document.getElementsByClassName('notification-messages')[0];
if (messagesMessagesDOM) {
var messagesMessagesUlDOM = messagesMessagesDOM.querySelector('ul');
var messagesArrowDOM = messagesMessagesDOM.querySelector('.arrow');
}
// property
const lastNotificationID = 0;
if (messagesMessagesDOM) {
setTimeout(function() { window.scrollTo(0, 105) }, 100)
var audio = new Audio("static/audio/recive.mp3");
}
// set handlers
if (notifySendDOM)
notifySendDOM.onclick = notifySendDOMClicked;
if (messagesMessagesDOM) {
messagesBellDOM.onclick = messagesBellDOMClicked;
checkMessageEvery5Sec();
}
// handlers
function notifySendDOMClicked() {
if (notifyMessageDOM.value == "" || notifyLinkDOM.value == "") {
alert("لطفا هر دو فیلد را پر کنید");
return false;
}
notifySendDOM.setAttribute("disabled", "disabled");
const params = new FormData(notifyFormDOM);
initXHR({
method: "POST",
action: "snd",
onload: function() {
const response = this.response;
const message = response.msg;
const status = response.status;
if (status == 1) {
alert(message);
} else {
alert(message);
console.error(message);
}
notifySendDOM.removeAttribute("disabled");
},
onerror: function() {
console.warn("XHR ERROR")
notifySendDOM.removeAttribute("disabled");
},
data: params
});
}
function messagesBellDOMClicked() {
messagesMessagesUlDOM.classList.toggle("active");
messagesArrowDOM.classList.toggle("active");
checkMessageListOpen();
}
// helpers
function initXHR(config) {
const xhr = new XMLHttpRequest();
xhr.responseType = "json";
xhr.open(config['method'], "http://localhost/app/api.php?action=" + config['action']);
xhr.onload = config['onload'];
xhr.onerror = config['onerror'];
xhr.send(config['data']);
}
function checkMessageEvery5Sec() {
setTimeout(function() {
const lastIdHTML = getNotificationLastHtmlID();
const params = new FormData();
let offset = "limit";
if (lastIdHTML == false) {
offset = 'all';
}
params.append("offset", offset);
initXHR({
method: "POST",
action: "rcv",
onload: function() {
const response = this.response;
const data = response.data;
checkMessageEvery5Sec();
if (data.length == 0 || data[data.length - 1]['id'] == lastIdHTML) {
return false;
}
data.forEach(function(dta) {
const html = generateLiHtml(dta);
messagesMessagesUlDOM.innerHTML += html;
});
if (!localStorage.getItem('notificationLastID'))
changeNotificationNumbersContent(messagesMessagesUlDOM.querySelectorAll("li").length);
else {
changeNotificationNumbersContent(checkUnreadMessageNumbers(data));
}
},
onerror: function() {
console.warn("XHR ERROR")
alert('خطایی در ارتباط با دیتابیس پیش آمده');
},
data: params,
});
}, 5000)
}
function generateLiHtml(data) {
data['created_at'] = new Intl.DateTimeFormat('fa-IR').format(new Date(data['created_at']));
const html = `<li id="li-${data['id']}" data-id="${data['id']}"><small id="date">${data['created_at']}</small><p id="message">${data['message']}</p><a href="${data['link']}" id="more">مشاهده</a></li>`;
return html;
}
function getNotificationLastHtmlID() {
const allLiDOM = messagesMessagesUlDOM.querySelectorAll("li");
if (allLiDOM.length == 0)
return false;
const lastLiDOM = allLiDOM[allLiDOM.length - 1];
const lastId = lastLiDOM.getAttribute("data-id");
return lastId;
}
function checkMessageListOpen() {
if (messagesMessagesUlDOM.classList.contains("active")) {
localStorage.setItem('notificationLastID', getNotificationLastHtmlID());
changeNotificationNumbersContent(checkUnreadMessageNumbers([]))
}
}
function checkUnreadMessageNumbers(data) {
let unreadMessagesNumber = 0;
data.forEach(function(dta) {
const id = dta['id'];
const liDOM = document.getElementById('li-' + id);
if (liDOM) {
const elementID = liDOM.dataset.id;
if (parseInt(localStorage.getItem('notificationLastID')) < parseInt(elementID))
liDOM.setAttribute('data-unread', 'true');
}
});
unreadMessagesNumber = document.querySelectorAll('li[data-unread]').length;
if (0 < unreadMessagesNumber && messagesMessagesUlDOM.classList.contains("active")) {
localStorage.setItem('notificationLastID', getNotificationLastHtmlID());
markReadedLi();
unreadMessagesNumber = document.querySelectorAll('li[data-unread]').length;
audio.play();
} else if (0 < unreadMessagesNumber && !messagesMessagesUlDOM.classList.contains("active")) {
audio.play();
}
return unreadMessagesNumber;
}
function markReadedLi() {
const readedDOM = document.querySelectorAll('li[data-unread]');
readedDOM.forEach(function(element) {
element.removeAttribute('data-unread');
});
}
function changeNotificationNumbersContent(number) {
messagesNumDOM.innerHTML = number;
if (messagesNumDOM.innerHTML != 0) {
audio.play();
}
}
6- استایل main.css
.container {
margin: 0 auto;
width: 80%;
text-align: center;
direction: rtl;
}
/* Page Style */
body {
margin: 0 !important;
}
.clearfix {
clear: both;
}
#menu-1 {
background-color: #607d8b;
border-bottom: 6px dotted transparent;
transition: 0.3s all;
}
#menu-1 ul {
list-style: none;
padding: 0;
margin: 0;
}
#menu-1 ul li {
float: right;
}
#menu-1 ul li a {
text-decoration: none;
display: block;
color: white;
font-weight: bold;
padding: 15px;
margin-left: 25px;
}
.active {
display: block !important;
}
.notification-wrapper {
float: left;
position: relative;
z-index: 100;
}
.notification-bell {
background-image: url("../img/bell.svg");
width: 30px;
height: 30px;
margin: 12px 0 0 12px;
cursor: pointer;
}
.notification-num {
display: block;
position: absolute;
top: 4px;
right: -23px;
width: 17px;
height: 17px;
color: #1a1a1a;
text-align: center;
border: 2px solid #ff562b;
background-color: #ffbe11;
border-radius: 50%;
padding: 2px;
}
.notification-messages {
position: relative;
}
.notification-messages ul {
position: absolute;
top: 17px;
left: -40px;
background-color: #ffffff;
box-shadow: 0 0 5px #d4d4d4;
width: 300px;
height: 400px;
border-radius: 4px;
overflow-y: auto;
overflow-x: hidden;
display: none;
z-index: -3;
}
.arrow {
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid #ffffff;
position: absolute;
left: 17px;
top: 7px;
display: none;
}
.notification-messages ul li {
padding: 5px;
border-bottom: 1px solid #f2f2f2;
width: 100%;
}
.notification-messages ul li small {
color: #b7b7b7;
display: block;
text-align: right;
}
.notification-messages ul li p {
font-weight: bold;
color: #757575;
text-align: right;
}
.notification-messages>ul>li>a#more {
text-decoration: none;
color: #00bcd4;
display: inline-block;
padding: 5px;
margin: 0;
}
/* notify */
#notify-message,
#notify-link {
resize: none;
width: 100%;
border: 1px solid #2196f3;
padding: 5px;
font-size: large;
outline: none;
margin-bottom: 25px;
}
#notify-send {
background-color: #2196f3;
border: none;
outline: none;
padding: 10px 35px;
font-weight: bold;
color: white;
font-size: 15px;
border-radius: 6px;
cursor: pointer;
}
#notify-send:disabled {
opacity: 0.5;
}
قبل از اجرای برنامه وارد پوشه database شده و فایل cms.sql را import کنید .
ارسال نظر