يكثُر استخدام المخططات البيانيّة في مواقع الويب لتقريب المعلومة للزّائر. سنرى في هذا الدرس كيفية إنشاء مخطّطات بيانية في تطبيق Laravel باستخدام مكتبة Morris.js.
سنفترض أن لديك مشروع Laravel معدًّا وجاهزًا للعمل، وقاعدة بيانات مضبوطة. تتلخّص خطوات هذا الدرس في ما يلي:
- إنشاء نموذج Model للتطبيق إضافة للتهجيرات Migrations المرتبطة به.
- إنشاء متحكّمات Controllers وعروض Views لإظهار البيانات للزائر.
- إنشاء معمل نماذج Model Factory لإضافة بيانات إلى قاعدة البيانات من أجل عرضها على هيئة مخططات بيانية.
- استخدام Mirror.js لتنسيق البيانات وعرضها في مخطّطات.
النموذج
نبدأ بإنشاء نموذج نُسمّيه Widget
؛ نستخدم الخيار m-
لإنشاء تهجير في نفس الوقت:
php artisan make:model Widget -m
نفتح ملف التهجير ونعدّله ليصبح على النحو التالي:
class Widget extends Model { // protected $fillable = ['widget_name']; public $timestamps = false; }
أعطينا القيمة false
للمتغيّر timestamps$
حتى لا يُملأ الحقلان created_at
وupdated_at
تلقائيا في جدول البيانات. سنتستخدم معمل النماذج في ما بعد لملْء هذين الحقلين.
ننتقل لملف التهجير ونضيف حقلا جديدا باسم widget_name
إلى الجدول في قاعدة البيانات؛ تصبح الدالة up
على النحو التالي:
public function up() { Schema::create('widgets', function (Blueprint $table) { $table->increments('id'); $table->string('widget_name')->unique(); $table->timestamps(); }); }
انتهينا الآن من العمل على النموذج والتهجير. يمكنك مراجعة مقال كيف تنشئ نموذجا Model في Laravel للمزيد.
نفذ التهجير بالأمر التالي في مجلد المشروع:
php artisan migrate
تهيئة التطبيق
سنحتاج لمسارات للوصول إلى صفحات الموقع، عروض لإظهار البيانات، ومتحكمات للربط بين المسارات، النماذج والعروض. سنستخدم الأمر التالي الذي يُنشئ نموذجا للمستخدمين مع مسارات وعروض جاهزة لتسجيل المستخدمين في قاعدة البيانات وللولوج إلى التطبيق:
php artisan make:auth
نفتح ملفّ العرض app.blade.php
الموجود في المجلّد resources/views/layouts
لتحريره. هذا هو المخطّط الرئيس لعروض Blade الذي أنشأه الأمر السابق.
ابحث عن التعليق التالي:
<!-- Left Side Of Navbar -->
والذي توجد أسفله العناصر اليسرى من القائمة العلوية. سنضيف رابطين جديدين إلى القائمة، الأول للائحة بجميع التسجيلات الموجودة في الجدول widgets
(كما فعلنا في درس استخدام الاستثناءات Exceptions المخصَّصة في Laravel) والثاني لصفحة المخطَّط البياني:
<li><a href="{{ url('/widgets') }}">Widgets</a></li> <li><a href="{{ url('/charts') }}">Chart</a></li>
المسارات
أضفنا أعلاه رابطين؛ إلا أن المسارات التي تتولى الإجابة عنهما غير موجودة حتى الآن. نفتح ملف routes.php
الموجود على المسار app\Http
لإضافتها:
Route::group(['middleware' => 'web'], function () { Route::auth(); Route::get('/charts', 'HomeController@charts'); }); Route::group(['middleware' => 'web'], function () { Route::auth(); Route::get('/widgets', 'HomeController@widgets_list'); }); Route::group(['middleware' => 'web'], function () { Route::auth(); Route::get('/widget/{id}', 'HomeController@widget_detail'); });
جعلنا المسارات محمية لكي يصل إليها المستخدمون المسجّلون فقط. الجديد هنا (مقارنة مع درس الاستثناءات المخصّصة المُشار إليه أعلاه) هو إضافة المسار charts/
الذي تتولى دالة charts
في المتحكّم HomeController
الإجابة عنه. بالنسبة للمسارين الآخرين فأحدهما (widgets/
) يعرض أسماء جميع التسجيلات في الجدول widgets
والآخر ({widget/{id/
) يعرض اسم التسجيلة ذات المعرّف id
.
المتحكم
يوجد لدينا متحكّم واحد ناتج عن تطبيق أمر artisan
الأخير. هذا المتحكّم هو HomeController
. نفتح ملف المتحكّم HomeController.php
الموجود على المسار app/Http/Controllers
لتحريره. نضيف الدوال المذكورة في ملف المسارات السابق:
public function widgets_list() { $widgets = Widget::Paginate(10); return view('widgets.widgets',array('widgets' => $widgets)); } public function widget_detail($id) { $widget = Widget::find($id); return view('widgets.widget_detail', array('widget_name' => $widget->widget_name)); } public function charts() { $yearCounts = Widget::select(DB::raw('year(created_at) as year'), DB::raw('count(widget_name) as `count`')) ->groupBy('year')->get(); $chartData = (sizeof($yearCounts) > ) ? $yearCounts : null; if ($chartData) { return view('widgets.widgets_chart',array('chartData' => $chartData)); } else { return view('widgets.nowidgets'); } }
تبحث الدالة widgets_list
عن محتويات الجدول widgets
باستخدام دوال النموذج Widget
. لاحظ أننا نريد تقسيم النتائج إلى صفحات بحيث تحوي كل صفحة عشرة نتائج، لذا استدعينا الدالة Paginate
في الصّنف (النموذج) Widget
. نُرسل ما عثرنا عليه إلى العرض widgets
الموجود في مجلّد widgets
ضمن مجلد العروض resources/views
. في الدالة widget_detail
نبحث عن التسجيلة ذات المعرّف id
ونظهرها في العرض widget_detail
ضمن مجلد widgets
السابق.
نأتي الآن للدالة charts
التي تهيّئ البيانات لعرضها على هيئة مخطط بياني. نريد أن نعثُر على عدد التسجيلات المدرجة في جدول قاعدة البيانات مجموعة حسب السنة؛ على النحو التالي مثلا:
year-|--count 2016 | 16 2015 | 12 2014 | 15 2013 | 11
يمكّننا استعلام SQL التالي من الحصول على ما نريد:
SELECT COUNT(widget_name) AS count, YEAR(created_at) AS year FROM widgets GROUP BY year;
يمكن أن ننفّذ هذا الاستعلام باستخدام الواجهة DB
كالتالي:
DB::select('SELECT COUNT(widget_name) AS count, YEAR(created_at) AS year FROM widgets GROUP BY year;');
أو على النحو التالي:
Widget::select(DB::raw('year(created_at) as year'), DB::raw('count(widget_name) as `count`')) ->groupBy('year')->get();
نحصُل في كلتا الحالتين على مصفوفة Array من عمودين بناتج تنفيذ الاستعلام. راجع مقال استخدام Eloquent ORM للتعامل مع قاعدة البيانات في Laravel 5 حول الموضوع.
لا تنس استيراد النموذج Widget
والواجهة DB
قبل استخدامهما في المتحكّم:
use App\Widget; use DB;
نفحص المصفوفة المتحصَّل عليها؛ في حال وجود نتائج نرسلها إلى العرض widgets_chart
ضمن المجلد resources/views/widgets
؛ وإلا نظهر العرض nowidgets
الموجود في نفس المجلّد. سنؤخّر الحديث عن العروض إلى أن ننشئ معمل نماذج لملْء جدول قاعدة البيانات.
معمل النماذج وبذر قاعدة البيانات
تحدثنا في درس عن استخدام معمل النماذج Model factory لتوليد بيانات الاختبار، سنعمل بنفس المبدأ هنا. ننشئ ملفا باسم WidgetFactory.php
على المسار database/factories
ونضيف إليه المحتوى التالي:
<?php $factory->define(App\Widget::class, function (Faker\Generator $faker){ return [ 'widget_name' => $faker->unique()->word, 'created_at' => $faker->dateTimeBetween($startDate = '-4 years', $endDate = 'now') ]; });
تنشئ الشفرة السابقة تسجيلا في جدول البيانات widgets
بتوليد محتويات الحقلين widget_name
وcreated_at
. لاحظ أننا نستخدم مكتبة Faker. حدّدنا تاريخ created_at
بالمجال من قبل أربع سنوات إلى الآن.
يأتي الآن دور بذر البيانات. ننشئ صنفا لبذر الجدول widgets
:
php artisan make:seeder WidgetTableSeeder
نفتح الملف database/seeds/WidgetTableSeeder.php
ونحرّر الدالة run
ليصبح كالتالي:
public function run() { Widget::truncate(); factory(Widget::class, 50)->create(); }
نحذف محتوى الجدول لكي لا يحصُل تعارض مع البيانات الجديدة التي سنولّدها؛ ثم نستخدم معمل النماذج لإدراج 50 تسجيلة. لا تنس استيراد النموذج Widget
:
use App\Widget;
يمكننا الآن تنفيذ الأمر التالي لبذر البيانات في الجدول:
php artisan db:seed --class=WidgetTableSeeder
توجد طريقة أخرى هي التي سنستخدمها. نحرّر الملف DataBaseSeeder.php
الموجود على نفس مسار WidgetTableSeeder.php
ونضيف ما يلي إلى الدالة run
:
Widget::unguard(); $this->call(WidgetTableSeeder::class); Widget::reguard();
نطلُب نزع الحماية عن الجدول قبل بذره ثم نعيدها إليه. يؤدي النداء call
مهمّة البذر.
يتيح الملف DataBaseSeeder
إمكانية تنفيذ أصناف بذر عدّة بنفس الأمر التالي:
php artisan db:seed
بقيت الخطوة الأخيرة وهي إنشاء العروض.
العروض
نأتي الآن للعروض التي ورد ذكرها في المتحكّم دون أن تكون أنشئت. ستجد العروض في الملف المرفق. نشير إلى استخدام التعليمة التاليّة في العرض widgets
:
{!! $widgets->render() !!}
تظهر هذه التعليمة روابط للتنقل بين التسجيلات إذا كان عددها يفوق العشرة. هذا ناتج عن استخدامنا للدالة Paginate
في المتحكم أعلاه.
نأتي الآن لشرح العرض widgets_chart
الذي يتضمن المخطّط البياني الذي نهدف لإنشائه في هذا الدرس.
@extends('layouts.app') @section('content') <div class="container"> <div class="row"> <div class="col-md-10 col-md-offset-1"> <div class="panel panel-default"> <div class="panel-heading">Chart</div> <div class="panel-body"> <div id="chart" style="height: 250px;"> </div> </div> </div> </div> </div> </div> <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css"> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script> <script> var data = <?php echo json_encode($chartData) ?>; Morris.Line({ element: 'chart', data: data, xkey: 'year', ykeys: ['count'], labels: ['widgets created'] }); </script> @endsection
يمدّد العرضُ العرض الرئيس layouts.app
ويعرّف محتوى المقطع content
. يوجد في هذا المقطع العنصُر التالي ذي المعرّف chart
:
<div id="chart" style="height: 250px;"> </div>
سيُعرَض المخطّط البياني في هذا العنصُر. تأتي بعد ذلك اعتماديات مكتبة Morris:
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css"> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
وأخيرا السكربت الذي سيرسُم المخطّط:
<script> var data = <?php echo json_encode($chartData) ?>; Morris.Line({ element: 'chart', data: data, xkey: 'year', ykeys: ['count'], labels: ['widgets created'] }); </script>
يستقبل السكربت البيانات التي أرسلتها الدالة charts
المعرّفة في المتحكم HomeController
ويحوّلها إلى صيغة JSON التي تستخدمها مكتبة Morris:
var data = <?php echo json_encode($chartData) ?>;
ثم نستدعي الدالة Line
في مكتبة Morris:
Morris.Line({ element: 'chart', data: data, xkey: 'year', ykeys: ['count'], labels: ['widgets created'] });
نمرّر للدالة معرّف العنصُر الذي سنرسم في المخطّط (المعطى element
)، البيانات التي نودّ رسمها (data
)، عنصُر المصفوفة الذي سيكون في المحور الأفقي للمخطّط (xkey
) والعنصُر-أ و العناصر- الذي سيكون على المحور العمودي (ykeys
)؛ إضافة للصيقة Label تظهر في المخطّط.
نذهب الآن لرابط المشروع؛ بالنسبة لي الرابط هو:
http://laraveltips.dev
نسجّل مستخدما جديدا (register
) ثم نضغط على الرابط Chart
لنجد النتيجة التالية (قد يختلف المخطّط البياني عن الموجود في الصورة بسسب أن البيانات مولَّدة عشوائيا).
الملف المرفق: laravelchart.zip
ترجمة -وبتصرّف- للمقال Charts in Laravel 5.1 with Morris.js لصاحبه Bill Keck.
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.