ارسال نوتیفیکیشن به مرورگر با لاراول

نوتیفیکیشن یک جز جدا نشدنی از فروشگاه های اینترنتیه !

خیلی خوب میشه وقتی سفارش مشتری آماده به ارسال میشه یه نوتیفیکیشن کوچیک هم براش ارسال بشه و بگه سفارش شما ارسال شد !

اینجوری هم مشتری راضی میشه هم اینکه کلی وقت و انرژی از اپراتور و مشتری هدر نمیره

مشاهده ی این آموزش توی یوتوب :‌ https://www.youtube.com/watch?v=F5I5mfVZKxI

خب حالا چطور باید اینکارو با لاراول انجام بدیم ؟

خیلی ساده

کلا داستان نوتیفیکیشن توی مرورگر ها چه دسکتاپ چه موبایل با یه تابع انجام میشه !

new Notification('salam');

همین الان همینو توی کنسول مروگر وارد کنید میبینید که هیچی نمیشه 😅

چون که به احتمال ۹۹ درصد هنوز اجازه ی ارسال نوتیفیکیشن به مرورگر رو ندادید

برای اینکه اجازه بدید ، توی آدرس بار کنار اسم سایت روی علامت قفل یا همون ssl کلیک کنید و توی site setting نوتیفیکیشن allow کنید و دوباره همون کد بالا رو بزنید میبینید که شد

خب درسته هیچ سایتی اینجوری نوفیکیشن ارسال نمیکنه ، اصلش اینجوریه که نوتیفیکیشن باید وقتی کاربر توی سایتمون نیست ارسال بشه تا دوباره بیاریمش توی سایتمون !

مراحل عملکرد پوش نوتیفیکیشن :

  1. کاربر وارد سایت میشه
  2. از کاربر درخواست میشه اجازه بده که براش نوتیفیکیشن ارسال بشه
  3. اگر کاربر اجازه داد pushManager.subscribe اجرا میشه و یک سری اطلاعات و آدرس اندپوینت مروگر به ما داده میشه مثلا برای کروم همچین چیزیه fcm.googleapis.com/fcm/send یا برای فایرفاکس updates.push.services.mozilla.com و ما باید این مقادیرو به صورت جی‌سان توی دیتابیسمون برای یوزر ذخیره کنیم
  4. ما از طریق سرورمون به این اندپوینت درخواست میفرستیم و اون درخواستو به کاربر میفرسته !

بریم که انجامش بدیم

شروع

اول از همه یه پروژه با لاراول استارت میزنیم

توی فولدر پابلیک یا هرجایی که میشه یه فایل js درست کرد و بهش دست رسی داریم یه فایل درست میکنیم یعنی اینجوری مثلا https://www.site.com/file.js

اسمشو معمولا میذارن sw.js که همون service worker هست ، هر اسم دیگه ای هم میتونید بذارید

داخل این فایل اینو بنویسید :

self.addEventListener('push', (e) => {
    let data = e.data.json();
    const options = {
        body: data.body,
        icon: "https://www.shopid.ir/shopid.png",
        data: {
            url: data.url
        }
    }
    e.waitUntil(self.registration.showNotification(data.title, options));
});
self.addEventListener('notificationclick', function(e) {
    e.waitUntil(clients.openWindow(e.notification.data.url))
});


درست حدس زدید icon همون آیکونیه که کنار نوتیفیکشنتون قراره بیاد

(بقیه داکیومنتاش اینجا هست )

شما الان یه سرویس ورکر برای نوتیفیکیشن نوشتید !

که وقتی کاربر روش کلیک میکنه میره به آدرسی که ما میخوایم !

ولی هنوز یه سری کارای دیگه باید انجام بدیم

مرحله ای بعدی باید یه پکیج php روی لاراولمون نصب کنیم


composer require minishlink/web-push


خب حالا ما نیاز داریم که دوتا کلید بگیریم یکی پابلیک و یکی پرایویت

و اینکارو یک بار نیاز داریم بکینم

پس هرجا که میتونید از پیکیج minishlink/web-push استفاده کنید اینکارو میتونید بکنید

بهترین جایی که میشه اینکارو کرد web.php هست همونجایی که route هارو تعریف میکنیم

پس همونجا این کد بنویسید :

use Minishlink\WebPush\VAPID;
dd(VAPID::createVapidKeys());

یه بار سایتو ران کنید یه همچین چیزی میبینید

array:2 [▼
"publicKey" => "BJjPvcv9QQu3SVxvjP_WpQNeuyhWKpGozAXkab2rl6VTaMv18R4avQy5dKcudeEbCbRNMR_kRkbPxV957fxXbP8"
 "privateKey" => "oy468fjwBBSf_3OifCNsyHisxyf4FCgQBgM77Jd8uGs"
]


خب اینو یه جا ذخیره کنید و web.php به حالت اولش برگردونید یعنی اون dd(VAPID::createVapidKeys()) پاک کنید

حالا میریم و مدل و مایگریشن و کنترلمونو میسازیم

php artisan make:model push -mcr

توی فیلد های مایگریشن pushes یه فیلد تکست اضافه میکنیم

$table->text('pushdata');

و بعدش :

php artisan migrate


حالا میریم توی کنترلرمون (PushController.php) و متد های store و index و send اینجوری میسازییم


public function store(Request $request) {
    push::create([
            "pushdata"=>$request->push
        ]);
}
    public function index()
    {
        return view("admin",["pushes"=>push::latest()->limit(10)->get()]);
    }
    
        public function send(Request $req , push $push)
    {
        $webPush = new WebPush([
            "VAPID" => [
                "subject" => "http://localhost",
                "publicKey" => 'BJjPvcv9QQu3SVxvjP_WpQNeuyhWKpGozAXkab2rl6VTaMv18R4avQy5dKcudeEbCbRNMR_kRkbPxV957fxXbP8',
                "privateKey" => "oy468fjwBBSf_3OifCNsyHisxyf4FCgQBgM77Jd8uGs"
            ]
        ]);
        $subscription = Subscription::create(json_decode($push->pushdata,true));
        $response = $webPush->sendOneNotification($subscription,json_encode(["title"=>"new notif","body"=>$req->text,"url"=>$req->url]));
            if ($response->isSuccess()) {
                 echo "ok"
            } else {
                $response->getReason();
            }
}

دقت کنید که privateKey و publicKey از همونی که قبلا گرفتیم و سیو کردیم باید استفاده کنیم


توی مدلمون (push.php) fillable برای فیلد pushdata تعریف میکنیم


    protected $fillable = [
        'pushdata'
   ];

حالا روت هامونو تعریف میکنیم

api.php

    Route::post('admin/savepush',"App\Http\Controllers\PushController@store");

web.php

    Route::get('/admin', "App\Http\Controllers\PushController@index");
    Route::post('/admin/sendpush/{push}', "App\Http\Controllers\PushController@send");


حالا ویوهامونو میسازیم

ویوو اصلی یا هرجایی که میخوایم از کاربر اجازه بگیریم یه همچین چیزی میشه (اینجا همون welcome.blade.php) :

پارامتر applicationServerKey همون کلید عمومی هست که بالاتر گرفتیم و همینجا میذاریمش و مشکلی هم نداره که در سورسمون باشه

اون قسمت XMLHttpRequest هم خیلی ساده نوشتم که مقداری که میگیریم بفرستیم به بک‌اندمون اگه توی پروژتون جور دیگه ای ارسال میکنید با همون روش ارسال کنید

(فقط باید push به صورت جی‌سان شده ذخیره بشه)

welcome.blade.php :


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    
        navigator.serviceWorker.register('sw.js');
async function subscribe() {
    let sw = await navigator.serviceWorker.ready;
    let push = await sw.pushManager.subscribe({
        userVisibleOnly:true,
        applicationServerKey:"BJjPvcv9QQu3SVxvjP_WpQNeuyhWKpGozAXkab2rl6VTaMv18R4avQy5dKcudeEbCbRNMR_kRkbPxV957fxXbP8"
    });
      let xhr = new XMLHttpRequest()
 
xhr.open('POST', '/api/admin/savepush', true)
xhr.setRequestHeader('Content-type', 'application/json; charset=UTF-8')
xhr.send(JSON.stringify({"push":JSON.stringify(push)}));
}
        function enableNotif() {
            Notification.requestPermission().then(function (permission) {
               if (Notification.permission === 'granted') {
 subscribe()
}
            });
        }
    
    <button ="enableNotif();" style="font-size: 5rem">enableNotif</button>
</body>
</html>


یه ویو دیگه هم میسازیم به اسم admin.blade.php که همونجاییه که میخوایم به کاربرامون نوتیفیکیشن ارسال کنیم میتونید به صورت api هم انجام بدید اینجا به ساده ترین حالت داریم اینجام میدیم


admin.blade.php :


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    @foreach($pushes as $push)
    <form method="post" action="/admin/sendpush/{{$push->id}}" target="_blank">
        push {{$push->id}}    
        <input name="text" type="text" placeholder="text">
        <input name="url" type="text" placeholder="https://">
        <input type="submit" value="send"/>
        {{ csrf_field() }}
    </form>
    @endforeach
</body>
</html>


خب تموم شد الان فقط باید پروژمونو ران کنیم و هر کاربری بیاد توی سایتمون و enableNotif بزنه یه توکن و اندپوینت براش توی دیتابیس ما ذخیره میشه و اگه ما بریم توی قسمت ادمین (http://127.0.0.1:8000/admin) میتونیم براش نوتیفیکیشن ارسال کنیم !

مقدار text همون متنه و url هم برای وقتیه که کاربر روی نوتیفیکشن کلیک میکنه و وارد همون url میشه