اذهب إلى المحتوى

إنشاء إشعارات عبر الويب باستخدام لارافيل وقنوات Pusher


سامر سليمان

تتضمن العديد من تطبيقات الويب نظام إشعارات مبني ضمن التطبيق والذي يُعلِم المستخدم فورًا عندما يحاول شخص ما تنفيذ فعل ما مرتبط به، إذ يتلقى المستخدم في شبكة التواصل الاجتماعي فيسبوك مثلًا إشعارًا عندما يُعجَبُ أحدهم بحالة ما قد نشرها المستخدم أو عندما يضع أحدهم تعليقًا على صفحة المستخدم الشخصية. ننشئ في هذه المقالة آليةً شبيهةً بذلك عن طريق تطوير منظومة إشعارات معتمدة على الويب باستخدام بيئة لارافيل ومكتبة Pusher.

المتطلبات الأساسية

يجب التأكد من تثبيت خادم PHP ولارافيل Laravel على الحاسوب لإكمال المشروع كما يجب امتلاك حساب على منصة بوشر Pusher.

ما الذي سنبنيه؟

يتضمن العمل إنشاء تطبيق ويب بسيط يعرض الإشعارات باستخدام لارافيل وقنوات بوشر Pusher. يبين العرض التالي ما الذي نريد بناءه:

Create_notification_system_GIF

إعداد تطبيق Pusher

ننشئ بدايةً حسابًا على منصة Pusher ومن ثم نضبط التطبيق كما هو موضح بالصورة أدناه:

Pusher_account

إعداد تطبيق لارافيل

ننشئ تطبيق لارافيل جديد بإجراء الأمر التالي في موجه الأوامر:

laravel new laravel-web-notifications

نثبت حزمة التطوير البرمجي الخاصة بمنصة بوشر Pusher والمعتمدة على لغة PHP بتنفيذ الأمر التالي:

composer require pusher/pusher-php-server

نوجّه لارافيل لاستخدام منصة بوشر على أنها وسيلة لإرسال رسائل بث broadcast تُستخدم لإعلام أجزاء البرنامج بوقوع حدثٍ ما، ولتحقيق ذلك نفتح الملف "‎.env" الموجود في المجلد الجذر Root الخاص بلارافيل، ثم نحدّث محتواه ليوافق الضبط التالي مع ضرورة استبدال الرموز X بالقيم الخاصة بالحساب الخاص بك:

PUSHER_APP_ID=322700
BROADCAST_DRIVER=pusher

// ‫نضيف المعلومات المتعلقة بالحساب الخاص بنا من منصة بوشر
PUSHER_APP_ID=XXXXX
PUSHER_APP_KEY=XXXXXXX
PUSHER_APP_SECRET=XXXXXXX

ملاحظة مهمة: يتصل لارافيل افتراضيًا بخوادم الولايات المتحدة الأمريكية، وعند الحاجة لاستخدام خوادم أخرى، يجب تحديث مصفوفة الخيارات الموجودة ضمن الملف "config/broadcasting.php"، كما يُعدّل الملف "config/app.php" ويحذف التعليق الموجود في الجزء:

App\Providers\BroadcastServiceProvider::class 

إنشاء التطبيق الذي يدمج لارافيل مع Pusher

ننشئ التطبيق بعد الانتهاء من العمليات السابقة، حيث نبدأ بإنشاء صنف الحدث Event، الذي يبث الرسائل إلى قنوات بوشر Pusher انطلاقًا من تطبيق لارافيل. تُستدعى الأحداث من أي مكان نرغب به ضمن التطبيق.

php artisan make:event StatusLiked

ينشئ هذا السطر صنفًا باسم StatusLiked ضمن مجلد "app/Events". نفتح هذا الملف ونحدّث محتواه كما يلي:

<?php

namespace App\Events;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class StatusLiked implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $username;

    public $message;

    /**
     * إنشاء عينة جديدة من الحدث
     *
     * @return void
     */
    public function __construct($username)
    {
        $this->username = $username;
        $this->message  = "{$username} liked your status";
    }

    /**
     * تحديد القنوات التي سيُبث الحدث عليها
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return ['status-liked'];
    }
}

تخبر الواجهة ShouldBroadcast لارافيل أن الحدث الحالي يجب أن يُرسل وفقًا للطريقة المحدّدة في ملف الضبط الذي أعددناه مسبقًا.

يوجد تابع باني يأخذ وسيطين، هما: اسم المستخدم وفعل verb، إذ تُسند قيم المتغيرات إلى خصائص الصنف التي تأخذ نفس الأسماء، ويجب أن تكون هذه الخصائص عامة public وإلا فسيجري تجاهلها.

إنشاء واجهة عرض التطبيق

نستخدم واجهة عرض وحيدة تحتوي شريط تنقّل وأيقونة للإشعارات، إذ تُحدّث الأيقونة عند وجود إشعارات جديدة دون الحاجة لإعادة تحميل الصفحة. لن تستمر الإشعارات طويلًا في التجربة الحالية ولكن يمكن زيادة المدة الزمنية لاستمرار الإشعار حسب الحاجة.

نفتح الملف "welcome.blade.php" ونستبدل محتواه بما يلي:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Demo Application</title>
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
    <link rel="stylesheet" type="text/css" href="/css/bootstrap-notifications.min.css">
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
  </head>
  <body>
    <nav class="navbar navbar-inverse">
      <div class="container-fluid">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-9" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Demo App</a>
        </div>

        <div class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="dropdown dropdown-notifications">
              <a href="#notifications-panel" class="dropdown-toggle" data-toggle="dropdown">
                <i data-count="0" class="glyphicon glyphicon-bell notification-icon"></i>
              </a>

              <div class="dropdown-container">
                <div class="dropdown-toolbar">
                  <div class="dropdown-toolbar-actions">
                    <a href="#">Mark all as read</a>
                  </div>
                  <h3 class="dropdown-toolbar-title">Notifications (<span class="notif-count">0</span>)</h3>
                </div>
                <ul class="dropdown-menu">
                </ul>
                <div class="dropdown-footer text-center">
                  <a href="#">View All</a>
                </div>
              </div>
            </li>
            <li><a href="#">Timeline</a></li>
            <li><a href="#">Friends</a></li>
          </ul>
        </div>
      </div>
    </nav>

    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src="//js.pusher.com/3.1/pusher.min.js"></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

    <script type="text/javascript">
      var notificationsWrapper   = $('.dropdown-notifications');
      var notificationsToggle    = notificationsWrapper.find('a[data-toggle]');
      var notificationsCountElem = notificationsToggle.find('i[data-count]');
      var notificationsCount     = parseInt(notificationsCountElem.data('count'));
      var notifications          = notificationsWrapper.find('ul.dropdown-menu');

      if (notificationsCount <= 0) {
        notificationsWrapper.hide();
      }
// ‫تأهيل وظيفة السجلات الخاصة بمكتبة بوشر Pusher 
// لا نستخدم هذه الوظيفة في بيئة الإنتاج الفعلية
// Pusher.logToConsole = true;

      var pusher = new Pusher('API_KEY_HERE', {
        encrypted: true
      });
// الإشتراك بالقناة التي حددناها في صنف الحدث المعرّف في لارافيل
      var channel = pusher.subscribe('status-liked');
// ربط تابع ما بالحدث الذي عرّفناه في الصنف 
    channel.bind('App\\Events\\StatusLiked', function(data) {
        var existingNotifications = notifications.html();
        var avatar = Math.floor(Math.random() * (71 - 20 + 1)) + 20;
        var newNotificationHtml = `
          <li class="notification active">
              <div class="media">
                <div class="media-left">
                  <div class="media-object">
                    <img src="https://api.adorable.io/avatars/71/`+avatar+`.png" class="img-circle" alt="50x50" style="width: 50px; height: 50px;">
                  </div>
                </div>
                <div class="media-body">
                  <strong class="notification-title">`+data.message+`</strong>
                  <!--p class="notification-desc">Extra description can go here</p-->
                  <div class="notification-meta">
                    <small class="timestamp">about a minute ago</small>
                  </div>
                </div>
              </div>
          </li>
        `;
        notifications.html(newNotificationHtml + existingNotifications);

        notificationsCount += 1;
        notificationsCountElem.attr('data-count', notificationsCount);
        notificationsWrapper.find('.notif-count').text(notificationsCount);
        notificationsWrapper.show();
      });
    </script>
  </body>
</html>

نركّز على الجزء الخاص بلغة جافا سكريبت JavaScript، الذي يتضمن الأجزاء الخاصة بمكتبة Pusher التي ترسل الإشعارات. وفيما يلي الجزء المهم من الكتلة البرمجية الخاصة بعملية الإرسال:

// ‫تأهيل وظيفة السجلات الخاصة بمكتبة بوشر 
//لا نستخدم هذه الوظيفة في بيئة الإنتاج الفعلية
// Pusher.logToConsole = true;
// نهيئ مكتبة جافا سكريبت الخاصة بقنوات بوشر
var pusher = new Pusher('API_KEY_HERE', {
    encrypted: true
});
// الإشتراك بالقناة التي حددناها في صنف الحدث المعرّف في لارافيل
var channel = pusher.subscribe('status-liked');
// ربط تابع ما بالحدث الذي عرّفناه في الصنف 
channel.bind('App\\Events\\StatusLiked', function(data) {
// يُستدْعى هذا التابع عند استلام إشعار بالحدث
});

ملاحظة: ستبث لارافيل الحدث افتراضيًا تحت اسم الصنف الخاص بالحدث ولكن يمكن تخصيص هذا الاسم عن طريق تعريف طريقة خاصة للبث الخاص بالحدث:

public function broadcastAs() {
  return 'event-name';
}

تهيئ الشيفرة السابقة مكتبة الجافا سكريبت الخاصة بالتعامل مع بوشر Pusher وتؤسس قناة اتصال لإرسال واستقبال الأحداث المختلفة، وتتضمن أيضًا طريقةً خاصةً تُستَدعى عند استلام عملية بث تخص حدث ما عن طريق القناة.

اختبار التطبيق

ننشئ الاتجاه Route اللازم لتنفيذ الحدث والذي يسبب وصول الإشعار إلى المستخدم، إذ يظهر الإشعار عند الوصول إلى هذا الاتجاه ما لم يوجد خطأ ما في الخطوات السابقة. نضيف الاتجاه الجديد كما يلي:

Route::get('test', function () {
    event(new App\Events\StatusLiked('Someone'));
    return "Event has been sent!";
});

نشغّل خادم PHP عن طريق لارافيل لاختبار نجاح عمل البرنامج الذي كتبناه:

php artisan serve

الخاتمة

أنشأنا نظام إشعارات خاص بالويب باستخدام عددٍ قليل نسبيًا من السطور البرمجية بالاعتماد على قنوات بوشر Pusher، وما أنجزناه يُعد مثالًا بسيطًا على قدرات هذه المكتبة وأداةً يمكن تضمينها في عدة مشاريع، ولا شك أنه يمكن الاعتماد على مكتبة بوشر Pusher في تطوير كثيرٍ من الأدوات المفيدة أيضًا والتي يمكن تضمينها ضمن مختلف المشاريع البرمجية.

ترجمة -وبتصرف- للمقال How To Create Web Notifications Using Laravel and Pusher Channels لصاحبه Neo Ighodaro.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...