البحث في الموقع
المحتوى عن 'تسجيلات'.
-
أنشأنا في الدرس السابق بنية قاعدة البيانات الخاصة بمشروع 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.