أنشأنا في الدرس السابق بنية قاعدة البيانات الخاصة بمشروع Larashop. نكمل في هذا الدرس حديثنا عن قواعد البيانات في Laravel بشرح كيفية إدراج تسجيلات في قواعد البيانات وكيفية استخراجها منها باستخدام إطار العمل Eloquent.
هذا الدرس جزء من سلسلة تعلم Laravel والتي تنتهج مبدأ "أفضل وسيلة للتعلم هي الممارسة"، حيث ستكون ممارستنا عبارة عن إنشاء تطبيق ويب للتسوق مع ميزة سلة المشتريات. يتكون فهرس السلسلة من التالي:
- مدخل إلى Laravel 5.
- تثبيت Laravel وإعداده على كلّ من Windows وUbuntu.
- أساسيات بناء تطبيق باستخدام Laravel.
- إنشاء روابط محسنة لمحركات البحث (SEO) في إطار عمل Laravel.
- نظام Blade للقوالب.
- تهجير قواعد البيانات في Laravel.
- استخدام Eloquent ORM لإدخال البيانات في قاعدة البيانات، تحديثها أو حذفها. (هذا الدرس)
- إنشاء سلة مشتريات في Laravel.
- الاستيثاق في Laravel.
- إنشاء واجهة لبرمجة التطبيقات API في Laravel.
- إنشاء مدوّنة باستخدام Laravel.
- استخدام AngularJS واجهةً أمامية Front end لتطبيق Laravel.
- الدوّال المساعدة المخصّصة في Laravel.
- استخدام مكتبة Faker في تطبيق Laravel لتوليد بيانات وهمية قصدَ الاختبار.
سنغطي في هذا الدرس المواضيع التالية:
- نماذج Eloquent
- أعراف التسمية.
- أسماء الجداول والمفاتيح الخارجية.
- الأختام الزمنية.
- إطار العمل Eloquent
- قراءة البيانات READ.
- تحديث البيانات UPDATE.
- حذف البيانات DELETE.
- إدراج البيانات INSERT.
- تنفيذ استعلامات SQL في Laravel.
- نماذج Eloquent لمشروع Larashop.
- استخدام النماذج في المتحكمات.
- إظهار بيانات النماذج في العروض.
إطار عمل Eloquent
يأتي Laravel مضمنًّا بإطار عمل Eloquent الذي يُستخدَم للتخاطب مع قاعدة البيانات وتنفيذ عمليات مثل الإدراج Insert، التحديث Update أو الحذف Delete على الجداول. Eloquent هو إطار عمل لربط كائنات التطبيق بعلاقات (جداول) قاعدة البيانات Object Relational Mapper, ORM.
يقوم مبدأ ربط العلاقات بالكائنات على تنفيذ نمط ActiveRecord (التسجيلة النشطة)؛ الذي هو وسيلة للوصول إلى البيانات في قاعدة البيانات، حيث يضمن جدول بيانات (أو علاقة في قاعدة البيانات بصفة عامة) في صنف Class وتُربط كل تسجيلة من جدول البيانات بكائن Object من الصنف. بهذه الطريقة يُصبح التعامل مع الصنف مماثلا للتعامل مع الجدول في قاعدة البيانات. مثلا، لإدراج تسجيلة جديدة في جدول قاعدة البيانات ننشئ - في التطبيق - كائنا جديدا من الصنف المربوط بالجدول. وإذا أردنا أن نحدّث بيانات تسجيلة من الجدول نحدّث خاصيّات الكائن المربوط بها.
نماذج Eloquent
تُستخدَم النماذج في بنية MVC للتفاعل مع مصادر البيانات (قواعد البيانات، ملفات نصية، … إلخ). تُعرَّف النماذج في Laravel بتمديد الصنف Illuminate\Database\Eloquent\Model
الذي يوفّر دوال جاهزة للاستخدام من أجل التفاعل مع مصدر البيانات.
أعراف التسمية في Eloquent
توجد بعض الأعراف التي يفترض إطار العمل Eloquent مبدئيا اتّباعها، مع وجود إمكانية لتغييرها حسب الرغبة. في العرف أن اسم النموذج يكون كلمة مفردة تبدأ بحرف كبير Upper case، بينما اسم الجدول في قاعدة البيانات كلمة للجمع بأحرف صغيرة Lower case. يعتمد Laravel هذا العرف فيربط تلقائيا بين النموذج ذي الاسم المفرد والجدول الذي يكون اسمه جمعا لاسم النموذج.
نستخدم أداة Artisan لإنشاء نموذج لجدول التصنيفات categories
باسم Category
(مفرد categories
):
php artisan make:model Category
ينشئ الأمر ملفا للنموذج على المسار app/Category.php
. نفتح الملف لرؤية محتواه:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Category extends Model { // }
- نعرف فضاء الأسماء الذي يتبع له النموذج:
;namespace App
. - نستورد فضاء الأسماء الخاص بـ Eloquent بالتعليمة:
use Illuminate\Database\Eloquent\Model;
. - يمدّد الصنفُ
Category
صنفَ نماذج Eloquent الذي تحدثنا عنه أعلاه:class Category extends Model
.
من المتعارف عليه أيضا أن حقل المفتاح الرئيس للجدول يُسمّى id
، ومن الممكن مثل ما هو الحال مع اسم الجدول، تحديدُ اسم مغاير لحقل المفتح الرئيس.
يمكن تحديد اسم الجدول في النموذج بغض النظر عن المتعارف عليه بإعطاء قيمة للمتغير table$
، نفس الشيء بالنسبة للمفتاح الرئيس مع المتغير primaryKey$
:
protected $primaryKey = 'id'; protected $table = 'categories';
تسجيلات الأختام الزمنية
يفترض Laravel إضافةَ الحقلين created_at
وupdated_at
إلى جداول قاعدة البيانات. تُدرج قيمتا الحقلين عند إنشاء تسجيلة جديدة في الجدول، وتحدّث قيمة updated_at
عند تحديث قيمة التسجيلة. إن لم تضف هذين الحقلين في جداول قاعدة البيانات فيمكن تعطيل الإعداد المبدئي على النحو التالي:
public $timestamps = false;
استخدام Eloquent
نكمل كتابة الشفرة المصدرية للنموذج Category
ليصبح كما يلي:
<?php namespace App; class Category extends Model { protected $primaryKey = 'id'; protected $table = 'categories'; }
- عرّفنا حقل المفتاح الرئيس للجدول: ;'
$primaryKey = 'id
. ليس هذا ضروريا هنا ما دام اسم الحقل يوافق العُرْف (id
). نفس الملحوظة تنطبق على تعريف اسم الجدول في التعليمة الموالية.
قراءة محتوى جدول في قاعدة البيانات
سنرى، في هذه الفقرة، كيفية العثور على جميع التصنيفات التي نحتفظ بها في جدول التصنيفات.
ملحوظة: نطبع النتائج في الأمثلة أدناه مباشرة من الشيفرة المصدرية للمسار دون الاستعانة بالمتحكم رغم أن ذلك مخالف لمبدأ MVC، إلا أن الغرض هنا هو رؤية عمل Eloquent. سنعود لترتيب الأمور في ما بعد.
افتح ملف المسارات routes.php
وأضف المسار التالي:
Route::get('/read', function() { $category = new App\Category(); $data = $category->all(array('name','id')); foreach ($data as $list) { echo $list->id . ' ' . $list->name . '</br>'; } });
ننشئ كائنا جديدا من صنف النموذج:
$category = new App\Category();
التعليمة التالية تستدعي الدالة
all
في الكائن الذي أنشأناه للتو، وتمرر له مصفوفة تحدد الحقول التي نود الحصول عليها؛ في حالتنا حدّدنا الحقلينid
(المعرِّف) وname
(اسم التصنيف). إن لم تُحدَّد معطيات للمصفوفة فستُرجع الدالة جميع الحقول.- نستخدم حلقة تكرارية
foreach
لإظهار النتائج التي تحصلنا عليها.
احفظ التعديلات ثم افتح الرابط http://larashop.dev/read
في المتصفح. ستحصُل على لائحة شبيهة بالتالي (قد تختلف النتيجة لديك قليلا):
5 CLOTHING 4 FASHION 3 KIDS 1 MEN 2 WOMEN
تحديث تسجيلات
سنحدّث في هذه الفقرة تسجيلة في جدول التصنيفات باستخدام المعرِّف id
. نختار معرّف إحدى التصنيفات الظاهرة في نتيجة المثال السابق، مثلا التصنيفKIDS
ذو المعرِّف 3
.
نعيد فتح ملف المسارات routes.php
ونضيف مسارا جديدا كما يلي:
Route::get('/update', function() { $category = App\Category::find(4); $category->name = 'KIDS 2'; $category->save(); $data = $category->all(array('name','id')); foreach ($data as $list) { echo $list->id . ' ' . $list->name . '</br>'; } });
- نستدعي الدالة
find
الموجودة في النموذج مع تمرير معرّف التصنيف إليها. ترجع الدالة كائنا من صنفCategory
يحوي بيانات التصنيف المستقاة من تسجيلة في الجدولcategories
. - نعيّن الاسم الجديد للتصنيف بالتعديل على الخاصية
name
في الكائنcategory$
. - لتُعتمَد التعديلات وتخزَّن في السجل نستدعي الدالة
save
من الكائنcategory$
. - نستخدم حلقة تكرارية
foreach
لإظهار النتائج التي تحصلنا عليها.
احفظ الملف ثم افتح الرابط http://larashop.dev/update
في المتصفح. لاحظ تغيّر اسم التصنيف من KIDS
إلى KIDS 2
.
حذف تسجيلة من جدول في قاعدة البيانات
نختار أحد التصنيفات لحذفه (مثلا، التصنيف CLOTHING
ذو المعرف 5
). افتح ملف المسارات routes.php
وأضف المسار التالي:
Route::get('/delete', function() { $category = App\Category::find(5); $category->delete(); $data = $category->all(array('name','id')); foreach ($data as $list) { echo $list->id . ' ' . $list->name . '</br>'; } });
يوجد شبه كبير بين تحديث تسجيلة وحذفها. الفرق هو أنه بعد إيجاد التصنيف (الدالة find) نستدعي الدالة delete في الكائن category$ لحذف التسجيلة المربوطة به.
افتح الرابط http://larashop.dev/delete
ولاحظ أن التصنيف لم يعد موجودا.
إدراج تسجيلة
لإدراج تسجيلة جديدة في جدول قاعدة البيانات ننشئ كائنا جديدا من صنف Category
ونعيّن خواصّه ثم نخزنه في الجدول، كما يلي:
Route::get('/insert', function() { $category= new App\Category; $category->name='Music'; $category->save(); return 'category added'; });
بالذهاب إلى الرابط http://larashop.dev/insert تظهر رسالة category added دلالةً على إضافة التصنيف. يمكن أيضا التحقق من إضافة التصنيف من سطر أوامر MySQL أو بالذهاب إلى الرابط http://larashop.dev/read.
توجد طريقة أخرى لاستخدام سطر برمجي واحد لإدراج تصنيف في تسجيلة. نستخدم لهذا الغرض الدالة create
من نموذج التصنيف Category
؛ ولكن يجب قبل ذلك التعديل على النموذج Category
لتمكين الإسناد الشامل Mass assignment (تحديد قيم معطيات عدّة مرة واحدة).
نفتح ملف النموذج لتحريره ثم نضيف السطر التالي:
protected $fillable = array('name', 'created_at_ip', 'updated_at_ip');
يعرف المتغير fillable$
مصفوفة بالحقول التي يمكن تعيين قيمها دفعة واحدة. احفظ ملف النموذج ثم افتح ملف المسارات routes.php
وعدل المسار insert/
ليصبح التالي:
Route::get('/insert', function() { App\Category::create(array('name' => 'New Music')); return 'category added'; });
نستدعي الدالة create
من الصنف Category
ونمرر لها مصفوفة بحقول التسجيلة الجديدة مع قيمها. ينبغي أن تكون الحقول الممررة قابلة للإسناد الشامل، أي مذكورة في المتغير fillable$
. لاحظ أن إنشاء الكائن وتعيين قيم خاصياته ثم تخزين محتوياته في قاعدة البيانات كل هذا تم من خلال استدعاء الدالة create
.
ملحوظة 1: عند طلب الرابط http://larashop.dev/insert
في المرة الأولى تظهر الرسالة category added
ولكن عند طلب نفس الرابط مرة أخرى دون تعديل المسار تظهر صفحة بيضاء دلالة على عدم إدراج التسجيلة. يعود السبب في ذلك إلى أننا أثناء تعريف الجدول categories
في الدرس السابق علّمنا الحقل name
بالدالة unique
أي أنه لا يمكن لتسجيلتين من هذا الجدول أن يكون لهما نفس الاسم.
ملحوظة 2: الإسناد الشامل غير ممكّن مبدئيا لأسباب أمنية ويجب عند تمكينه اختيار حقول fillable$
بعناية حتى لا تمثل خطرا أمنيا. يجب دائما تطهير Sanitize البيانات التي يستقيها التطبيق من المستخدم.
تنفيذ استعلامات SQL مباشرة
قد ترغب، لسبب أو آخر، في التعامل المباشر مع قاعدة البيانات باستخدام استعلامات SQL؛ يوفر Laravel صنف DB
لهذا الغرض.
- استعلامات SELECT
تُستخدَم دالة select
لتنفيذ استعلامات القراءة من قاعدة البيانات على النحو التالي:
$category = DB::select('SELECT name FROM categories WHERE id = ?', [1]);
تأخذ الدالة DB::select
معطيين: الأول هو استعلام القراءة المراد تنفيذه، والثاني معطيات نود استخدامها في الاستعلام. بالنسبة للمعطيات المراد استخدامها في الاستعلام فتُحدّد أماكنها بعلامة ?
وتكون قيمتها في مصفوفة تمرّر في المعطى الثاني للدالة DB::select
. ينفذ السطر أعلاه استعلام SQL التالي:
SELECT name FROM categories WHERE id = 1;
نفرض أننا نريد تنفيذ استعلام SQL التالي:
SELECT name FROM categories WHERE id BETWEEN 1 AND 4;
يطلب الاستعلام قيمتين (1
و4
). يُترجم الاستعلام في النموذج على النحو التالي:
$category = DB::select('SELECT name FROM categories WHERE id BETWEEN ? AND ?', [1,4]);
تُبدل أول علامة ?
في الاستعلام بأول عنصر من المصفوفة في معطى الدالة الثاني، وعلامة ?
الثانية بالمعطى الموالي وهكذا.
يمكن أيضا استخدامُ متغيرات بدلا من ?
على النحو التالي:
$category = DB::select('SELECT name FROM categories WHERE id BETWEEN :id1 AND :id2', ['id1' => 1, 'id2' => 4]);
لاحظ استخدام :
قبل أسماء المتغيرات في استعلام SQL.
- استعلامات INSERT
تُستخدم دالة DB::insert
لإدراج تسجيلات في الجدول بنفس طريقة استخدام DB::select
للقراءة منه:
$inserted=DB::insert('INSERT INTO categories (name) VALUES (?)', ['TEST']);
ترجع الدالة DB::insert
عدد التسجيلات التي أدرجها الاستعلام.
- استعلامات UPDATE
لتحديث تسجيلة في جدول بقاعدة البيانات نستخدم الدالة DB::update
:
$affected = DB::update('UPDATE categories SET name = 'TEST UPDATE' WHERE name = ?', ['TEST']);
ترجع الدالة DB::update
عدد التسجيلات التي حدثها الاستعلام.
- استعلامات DELETE
توفر الدالة DB::delete
إمكانية تنفيذ استعلامات DELETE
على النحو التالي:
$deleted = DB::delete('DELETE FROM categories WHERE id = ?', [13]);
ترجع الدالة DB::delete
عدد التسجيلات التي حذفها الاستعلام.
- استعلامات عامة
بالنسبة للاستعلامات التي لا ترجع أية قيمة فيمكن استخدام الدالة DB::statement
:
DB::statement('drop tables drinks');
نماذج Eloquent لمشروع Larashop
نشرع الآن بعد أن رأينا آلية عمل Eloquent ببناء بقية نماذج مشروع Larashop.
نفذ الأوامر التالية لإنشاء نماذج للعلامات التجارية، المنتجات ومنشورات المدونة على التوالي:
php artisan make:model Brand php artisan make:model Product php artisan make:model Post
نعرّف في كل ملف نموذج حقل المفتاح الرئيس، اسم الجدول وأسماء الحقول المسموح بإسنادها fillable
.
ملف
Brand.php
:<?php namespace App; class Brand extends Model { protected $primaryKey = 'id'; protected $table = 'brands'; protected $fillable = array('name', 'created_at_ip', 'updated_at_ip'); }
ملف
Product.php
:<?php namespace App; class Product extends Model { protected $primaryKey = 'id'; protected $table = 'products'; protected $fillable = array('name', 'title', 'description','price','category_id','brand_id','created_at_ip', 'updated_at_ip'); }
ملف
Post.php
:
<?php namespace App; class Post extends Model { protected $primaryKey = 'id'; protected $table = 'posts'; protected $fillable = array('url', 'title', 'description','content','blog','created_at_ip', 'updated_at_ip'); }
استخدام النماذج في المتحكمات
تقضي بنية MVC وعادات البرمجة الصحيحة أن تكون المتحكمات هي من يتعامل مع النماذج. سنعدّ متحكم المشروع Front.php
للعثور على البيانات من النماذج وتمريرها إلى العروض لتظهر لدى المتصفح.
عدّل ملف Front.php
كالتالي:
<?php namespace App\Http\Controllers; use App\Brand; use App\Category; use App\Product; use App\Http\Controllers\Controller; class Front extends Controller { var $brands; var $categories; var $products; var $title; var $description; public function __construct() { $this->brands = Brand::all(array('name')); $this->categories = Category::all(array('name')); $this->products = Product::all(array('id','name','price')); } public function index() { return view('home', array('title' => 'Welcome','description' => '','page' => 'home', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products)); } public function products() { return view('products', array('title' => 'Products Listing','description' => '','page' => 'products', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products)); } public function product_details($id) { $product = Product::find($id); return view('product_details', array('product' => $product, 'title' => $product->name,'description' => '','page' => 'products', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products)); } public function product_categories($name) { return view('products', array('title' => 'Welcome','description' => '','page' => 'products', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products)); } public function product_brands($name, $category = null) { return view('products', array('title' => 'Welcome','description' => '','page' => 'products', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products)); } public function blog() { return view('blog', array('title' => 'Welcome','description' => '','page' => 'blog', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products)); } public function blog_post($id) { return view('blog_post', array('title' => 'Welcome','description' => '','page' => 'blog', 'brands' => $this->brands, 'categories' => $this->categories, 'products' => $this->products)); } public function contact_us() { return view('contact_us', array('title' => 'Welcome','description' => '','page' => 'contact_us')); } public function login() { return view('login', array('title' => 'Welcome','description' => '','page' => 'home')); } public function logout() { return view('login', array('title' => 'Welcome','description' => '','page' => 'home')); } public function cart() { return view('cart', array('title' => 'Welcome','description' => '','page' => 'home')); } public function checkout() { return view('checkout', array('title' => 'Welcome','description' => '','page' => 'home')); } public function search($query) { return view('products', array('title' => 'Welcome','description' => '','page' => 'products')); } }
في المتحكم:
نستورد النماذج:
use App\Brand; use App\Category; use App\Product;
نعرف متغيرات لتحميل بيانات النماذج:
var $brands; var $categories; var $products;
يعرف المتغيران
title$
وdescription$
على التوالي عنوانا ووصفا لأغراض التحسين لمحركات البحث.نُعرّف باني Constructor صنف المتحكم:
public function __construct(){…}
تحمّل هذه الدالة البيانات من النماذج إلى المتغيرات المعرّفة سابقا.
- نحمّل في الدالة
index
العرضhome
مع تمرير مصفوفة معطيات تمثل بيانات النماذج مع بيانات أخرى لغرض التحسين لمحركات البحث.
عرض بيانات النماذج في العروض
نستخدم الحلقات التكرارية في قوالب Blade من أجل عرض بيانات النماذج التي مررها المتحكم إلى العروض. على سبيل المثال:
@foreach ($brands as $brand) <li><a href='{{url("products/brands/$brand->name")}}'> <span class="pull-right">(50)</span>{{$brand->name}}</a></li> @endforeach
توجد عروض المشروع في الملف المرفق.
خاتمة
من السهل إنشاء نماذج Eloquent واستخدامها؛ كل ما عليك فعله هو تمديد صنف Model
والبدء باستخدام النموذج. يوفر Laravel أيضا إمكانية تخصيص استعلامات SQL دون المرور بـEloquent لمن يرغب في ذلك.
ملف مرفق: عروض المشروع.
ترجمة -وبتصرف- لمقال Laravel 5 Eloquent ORM لصاحبه Rodrick Kazembe.
أفضل التعليقات
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.