إطار العمل Yii2: التعرّف على الـ models

جولة في ملفات الـ models لمعرفة محتواها وكيفية التعامل معها والأمور التي يجب تعريفها في هذه الملفات

عدنان توتنيمنذ 5 سنوات

توفّر إطارات العمل المبنية على معمارية MVC -مثل إطار العمل Yii- سهولة في التعامل مع قواعد البيانات دون الحاجة لكتابة استعلامات الإدخال والتعديل والحذف والعرض (CRUD operations) واستعلامات أخرى أكثر تعقيداً.

بالطبع يوجد كلاسات جاهزة تمكّنك من كتابة تعليمات SQL بشكلٍ يدوي، ولكنّنا في هذا المقال سنحاول التعمّق في الـ models التي نستطيع من خلالها الوصول لجداول قاعدة البيانات وتنفيذ ما نريده بسهولةٍ وبساطة.

دعونا نتجاهل الملفات الموجودة في مجلد models وننشئ ملفاً جديداً بداخله ونقوم بكتابة كلِّ شيءٍ بأنفسنا حتى نفهم خطوةً خطوة طبيعة التعامل مع الـ models.

أنشئ ملفاً بلاحقة php وليكن اسمه Person مثلاً.

نكتب الترويسة الأساسية لكلاس الـ model كالتالي:

<?php

namespace app\models;

use Yii;

class Person extends \yii\db\ActiveRecord {
	…
}

نلاحظ أنّ الـ model يرث من الكلاس ActiveRecord، حيث يمكننا القول أنّ هذا الكلاس يمكّننا من التعامل مع قاعدة البيانات بشكلٍ ديناميكي.

فالـ model الواحد يمثّل جدولاً في قاعدة البيانات، وبما أنّ الجداول ترتبط ببعضها عن طريق علاقاتٍ مثل one-to-one أو one-to-many، فإنّ هذه الـ models أيضاً ترتبط ببعضها -عن طريق توابع نقوم بتعريفها- فتتحول هذه الارتباطات إلى علاقاتٍ في قاعدة البيانات.

وكل object من الـ model يعبّر عن سطرٍ في جدول، وهكذا.

إذا عدنا لمثالنا الذي أنشأناه، فإنّ الـ model الذي أسميناه Person سيمثّل جدولاً في قاعدة البيانات اسمه persons مثلاً.

وإذا قمنا بإنشاء object من الـ Person فإنّ هذا الـ object يمثّل سطراً في جدول persons.

 

في الملف السابق سنقوم بكتابة التابع التالي حتى نقومَ بالربط بين الـ model الذي أنشأناه مع جدول persons في قاعدة البيانات الذي سنقوم بإنشائه:

public static function tableName(){
	return ‘persons’;
}

هذا التابع سيقوم بإعادة اسم الجدول persons، لذلك قم بإنشاء قاعدة بيانات باستخدام برامج مثل WAMP أو XAMPP، وأنشئ فيها الجدول persons كالتالي:

CREATE TABLE persons (
	id int NOT NULL AUTO_INCREMENT,
	first_name varchar(50) NOT NULL,
	last_name varchar(50) NOT NULL,
	age int(5),
	country varchar(50),
	PRIMARY KEY (id)
);

وبما أنّنا تطرّقنا لقواعد البيانات فقد حان الوقت لإدخال إعدادات قاعدة البيانات التي أنشأناه لكي يتعرّف عليها تطبيقنا دون الحاجة لكتابة الكود البرمجي لإجراء عملية الاتصال في كل مرة نريد التعامل فيها مع قاعدة البيانات.

سنذهب للملف config/db.php، وسنقوم بوضع اسم قاعدة البيانات واسم المستخدم وكلمة المرور -إن وُجدت- ثمّ نقوم بحفظ الملف والعودة إلى ملف الـ model كي نُكمل العمل فيه.

الآن ولإجراء تجربةٍ بسيطة سنقوم بالذهاب إلى ملف SiteController ونقوم بكتابة تابعٍ بسيطٍ لإدخال قيمة person جديد.

في ملف الـ controller يجب أولاً أن نقوم بعمل use للـ model الذي أنشأناه، فكل model نريد استخدامه في controller يجب أن نقوم بعمل use له.

سنقوم بكتابة التابع التالي:

public function actionAddNewPersonTest(){
    $person = new Person();
    $person->first_name = 'Your first name';
    $person->last_name = 'Your last name';
    $person->age = 24;
    $person->country = 'Your country';
    $person->save();
}

في هذا التابع (والذي اتّفقنا أنّه يُدعى action عندما نتعامل مع إطار العمل Yii) قمنا بإنشاء object من الكلاس Person، وبذلك سنتمكّن من الوصول لحقول جدول persons الذي عرّفناه في قاعدة البيانات، فقمنا بإسناد كلٍّ من first_name و last_name و age و country، وأخيراً قمنا باستدعاء التابع save من أجل تخزين هذا الـ object (الذي يمثّل سطراً في الجدول كما اتّفقنا) في جدول persons.

لتجربة ذلك قم بتشغيل الـ server عن طريق التعليمة التالية (بعد أن تقوم بفتح الـ CMD والتوجّه للمجلد الذي فيه ملفات المشروع):

yii serve

ومن ثمّ قم بفتح المتصفح وادخل إلى المسار التالي (بالنسبة لي فالتطبيق يعمل على الـ port رقم 8080، ربما يختلف بالنسبة لك، فتأكد من ذلك):

localhost:8080/site/add-new-person-test

ملاحظة: لا تنسَ ما قمنا بالحديث عنه في المقال السابق عن طريقة تعامل الـ Yii2 مع الـ url وكيفية تحويل أسماء الـ controllers والـ actions.

عند الضغط على زر enter ستظهر لك شاشة بيضاء، وذلك لأنّنا لم نقم بإعادة أي صفحة view في التابع، ولكن لا بأس بذلك، فالذي نريد التأكد منه هو جدول persons، لذلك قم بفتح phpMyAdmin (أو المكان الذي تستعرض فيه قاعدة البيانات تبعاً للبرنامج الذي تستخدمه) وسترى أنّ حقلاً جديداً قد أضيف إلى الجدول.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

الـ rules الخاصّة بالـ model:

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

نرى كثيراً من هذه القواعد أو الشروط عندما نتعامل مع مواقع عديدة ونريد ملء form فيها، فنشاهد مثلاً أنّ حقولاً معيّنةً يجب ألّا تكون فارغة، وحقل الـ email يجب أن يحوي قيمة email صحيح بصيغة محددة وليس مجموعة محارف عشوائية، ونرى مثلاً أنّ حقل كلمة المرور يجب أن يحوي 5 محارف على الأقل، بعضها أرقاماً وبعضها حروفاً، وما إلى ذلك من القواعد.

كل هذه الأمور يمكننا وضعها في ملف الـ model من خلال التابع rules الذي يقوم بإعادة مصفوفة، كل عنصر فيها يمثّل قاعدة تضم حقلاً أو عدّة حقول سويةً.

لنقم بإضافة التابع rules إلى الـ model الذي قمنا بإنشائه:

public function rules(){
	return [
		[[‘first_name’, ‘last_name’, ‘age’, ‘country’], ‘required’],
		[[‘age’], ‘integer’],
		[‘first_name’, ‘string’, ‘min’ => 5, ‘max’ => 15]
    ];
} 

نرى مثلاً في السّطر الأول من المصفوفة التي يعيدها التابع rules أنّ الحقول كلّها required، أي أنّها مطلوبة ولا ينبغي تركها فارغة.

في السّطر الثاني حددنا أنّ الحقل age هو من نمط integer.

في السّطر الثالث وضعنا شرطاً كي يكون حقل first_name ما بين 5 محارف و 15 محرفاً.

 

الآن، وبعد وضع القواعد السابقة لنقم بتغييرٍ بسيطٍ على التابع الذي أنشأناه في الـ controller وهو التابع add-new-person-test، فسنقوم بوضع الحقل last_name فارغاً، وسنضع قيمة الحقل age هي قيمة نصّية وليست رقمية، وسنضع قيمة first_name هي محرفَين فقط، ومن ثمّ نحفظ الملف ونقوم بتنفيذ التابع.

public function actionAddNewPersonTest(){
    $person = new Person();
    $person->first_name = 'XYZ';
    $person->last_name = '';
    $person->age = 'This is a string, not integer!';
    $person->country = 'Your country';
    $person->save();
}

سنلاحظ أيضاً أنّه قام بإعادة صفحة بيضاء، ولكن هذه المرّة لم يقم بإضافة حقل جديد إلى قاعدة البيانات!

تفسير ما حصل هو أنّ التابع save يقوم بتخزين سطر جديد في قاعدة البيانات ولكن في حال تحقق جميع الشروط والقواعد الموجودة في تابع rules في الـ model ذاته، ولكن عندما قمنا بتنفيذ التابع save في المرّة الأولى لم يكن لدينا أي قاعدة، فلم نكن قد عرّفنا تابع rules بعد، لذلك قام بحفظ السّطر الجديد في قاعدة البيانات، أمّا بعد إضافة القواعد فأصبح التابع save يُعيد قيمة false عند عدم تحقق القواعد.

للتأكد من ذلك سنقوم بتعديل التابع add-new-person-test وسنضع التابع save ضمن تعليمة شرطية، فبما أنّه يُعيد قيمة false عند عدم تحقق القواعد فنستطيع إعادة الأخطاء الناتجة عن عدم تحقق القواعد حتى نعرف تماماً ما هي الحقول التي لم تحقق القواعد الموجودة في التابع rules.

public function actionAddNewPersonTest(){
    $person = new Person();
    $person->first_name = 'XYZ';
    $person->last_name = '';
    $person->age = 'This is a string, not integer!';
    $person->country = 'Your country';
    if(! $person->save()){
        return '<pre>' . var_export($person->getErrors(), true) . '</pre>';
    }
}

ملاحظة: التابع save ضمن التعليمة الشرطية سيتم تنفيذه، فإذا تمّت عملية الحفظ في قاعدة البيانات سيُعيد قيمة true، لذلك وضعنا إشارة النّفي في بداية التعليمة الشرطية حتى يتم تنفيذ محتواها فقط في حال إعادة التابع save للقيمة false؛ أي أنّ الـ object لم يتم تخزينه في قاعدة البيانات، وأنّ بعض القواعد لم تتحقق في البيانات المُدخلة.

ملاحظة: في لغة PHP إذا أردنا قطع تنفيذ التابع لعرض معلومات متغيّر أو object معيّن فيمكننا استخدام التابع var_dump أو var_export.

الآن عند تنفيذ التابع add-new-person-test سنرى الأخطاء الناتجة عن طريق التابع getErrors المُستدعى من الـ object المُعرّف من الـ model.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

سيناريوهات!

ما يميّز إطار العمل Yii هو ما يُسمّى السيناريوهات التي يتم تعريفها في الـ models، ولكن ماذا تعني؟ وأين تكمن فائدتها؟

لتبسيط ذلك فلنفرض أنّه لدينا form لتسجيل حساب مستخدم جديد (Signup)، و form آخر لتسجيل الدخول (Login).

 في form تسجيل حساب جديد ينبغي على المستخدم إدخال بياناته كلّها، مثل اسمه وبريده الإلكتروني وكلمة السر وتأكيد كلمة السر، وما إلى ذلك، بينما في form تسجيل الدخول فغالباً ما يتم الاعتماد على حقلَين فقط، البريد الإلكتروني وكلمة المرور.

إذا قمنا باستخدام model مثل Users مثلاً، وقمنا بتعريف تابع rules كالتالي:

public function rules(){
	return [
	    [[‘first_name’, ‘last_name’, ‘email’, ‘password’, ‘confirm_password’], ‘required’]
    ];
}

نلاحظ أنّ الحقول كلّها مطلوبة ولا ينبغي تركها فارغة، وهذا "السيناريو" يصلح في حالة تسجيل حساب جديد، أمّا لو كان "السيناريو" هو تسجيل الدخول فلن نكون مضطرين لإدخال حقل تأكيد "كلمة المرور" مثلاً، أو حقل last_name.

ولكن بما أنّنا نتعامل مع نفس الـ model للحالتَين فسيتم تطبيق التابع rules عليهما، لذلك لا بد من طريقةٍ لجعل بعض القواعد تُطبّق في سيناريو معيّن ولا يتم تطبيقها في سيناريو آخر، وهذا ما نستطيع تحقيقه من خلال تابع scenarios الذي نقوم بتعريفه كالتالي:

const FIRST_SCENARIO = ‘first_scenario’;
const SECOND_SCENARIO = ‘second_scenario’;

public function scenarios(){
	$our_scenarios = [];
	$our_scenarios[self::FIRST_SCENARIO] = [‘age’];
	$our_scenarios[self::SECOND_SCENARIO] = [‘first_name’, ‘second_name’];
	return $our_scenarios;
}

قمنا بتعريف متغيّرَين const لنقوم باستخدامهما في التابع scenarios، وفي التابع قمنا بتعريف مصفوفة أسميناها our_scenarios، واستخدمنا فيها سيناريوهَين، وضعنا في الأول حقل age فقط، أي أنّه عند استخدام هذا السّيناريو سيتم تطبيق القواعد (الموجودة في تابع rules) والخاصّة فقط بحقل age دوناً عن غيره.

أمّا لو استخدمنا السّيناريو الثاني فسيتم تطبيق القواعد الخاصّة بالحقلَين first_name و last_name فقط دوناً عن غيرهما.

وبذلك نستطيع عند تعريف أو استخدام object من model أن نحدّد السّيناريو الخاص به لنضمن تطبيق القواعد التي نريدها، ويتم ذلك في الـ controller، فبالعودة إلى مثالنا السّابق، وباختيار FIRST_SCENARIO وتنفيذ التابع في المتصفح سنلاحظ أنّ رسائل الخطأ التي ستظهر هي فقط الرسائل الخاصّة بالحقل age.

public function actionAddNewPersonTest(){
    $person = new Person();
    $person->scenario = Person::FIRST_SCENARIO; 
    $person->first_name = 'XYZ';
    $person->last_name = '';
    $person->age = 'This is a string, not integer!';
    $person->country = 'Your country';
    if(! $person->save()){
        return '<pre>' . var_export($person->getErrors(), true) . '</pre>';
    }
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

قمنا بجولةٍ سريعة في ملف الـ model، وبالطبع يوجد أمور أخرى نستطيع القيام بها في ملفات الـ model مثل تعريف العلاقات بين الـ models وتوابع أخرى مهمّة.

الآن يمكننا الاستفادة ممّا سبق في التعامل مع الـ forms لنقوم بإجراء validation على البيانات التي يُدخلها المستخدم في حقول الـ form قبل تخزينها في قاعدة البيانات.

كلمات دليلية: models mvc yii yii2
0
إعجاب
1057
مشاهدات
0
مشاركة
1
متابع

التعليقات (0)

لايوجد لديك حساب في عالم البرمجة؟

تحب تنضم لعالم البرمجة؟ وتنشئ عالمك الخاص، تنشر المقالات، الدورات، تشارك المبرمجين وتساعد الآخرين، اشترك الآن بخطوات يسيرة !