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

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

ساخت نوتیفیکیشن با php – خیلی از مواقع در فیسبوک شاهد این بوده ایم که رویداد های مختلفی را با استفاده از زنگوله بالا سمت راست متوجه شده ایم و کاربران را اطلاع رسانی کرده است .

در این آموزش همان زنگوله را قرار هست که نمونه آن را بسازیم آن هم به صورت AJAX .
این آموزش شامل 6 فایل می باشد برای ساخت برنامه نوتیفیکیشن با php :

  1. اسکریپت index.php : صفحه نمایش نوتیفیکیشن ها
  2. اسکریپت notify.php : این صفحه معمولا در صفحه ادمین می باشد که 2 فیلد پیام و لینک را دریافت و به کاربر ارسال می کند
  3. اسکریپت db.php : همان طور که از اسمش پیداست برای تعامل با دیتابیس به کارمان می آید
  4. اسکریپت api.php : از آنجایی که قرار است درخواست های ajax بفرستیم نیازمند یک آدرس استاندارد شده برای ارسال و دریافت اطلاعات آن هم به صورت json هستیم که با دیتابیس اطلاعات ذخیره و خوانده شود
  5. اسکریپت app.js : درخواست های http و رویداد و رفتار های برنامه را با این اسکریپت javascript کنترل می کنیم .
  6. استایل 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 کنید .

دانلود برنامه نوتیفیکیشن PHP و AJAX

نوتیفیکیشن با php

ارسال نظر

جهت استفاده از کد حتما از تگ pre استفاده نمایید .

contact us