[ما هو MVC؟ 】
MVC هو مفهوم يسمح لك بالجمع بشكل متناغم بين "ثلاثة أجزاء (أي الاسم الكامل لـ MVC والنموذج والعرض ووحدة التحكم)" لتشكيل تطبيق معقد. تعد السيارة مثالًا جيدًا جدًا لـ MVC في الحياة الواقعية. عندما ننظر إلى السيارة، فإننا ننظر إلى جزأين (عرض): داخلي وخارجي. كلاهما لا ينفصلان عن المتحكم: السائق. يمثل نظام الفرامل وعجلة القيادة وأنظمة التحكم الأخرى النموذج: فهم يأخذون أساليب التحكم من السائق (المتحكم) ويطبقونها على الداخل والخارج (عرض).
[MVC on the Web]
المفاهيم التي يغطيها إطار عمل MVC بسيطة للغاية ومرنة للغاية. المفهوم الأساسي هو أن لديك وحدة تحكم واحدة (مثل ملف Index.php) تتحكم في جميع التطبيقات ضمن إطار العمل والتي تعتمد على طلبات المعلمات. تحتوي وحدة التحكم هذه عادةً (على الحد الأدنى) على معلمة تحدد النموذج والحدث ومعلمة GET. بهذه الطريقة يمكن لوحدة التحكم الإقرار بجميع الطلبات وتشغيل الأحداث المناسبة. على سبيل المثال، من المحتمل أن يتم استخدام طلب مثل هذا /index.php?module=foo&event=bar لتحميل فئة تسمى foo، ثم تشغيل foo::bar()[وهو bar( )function]. مزايا ذلك هي:
الحفاظ على واجهة لجميع التطبيقات
مع الحفاظ على عدد لا يحصى من الرموز في التطبيق أمر مزعج للغاية، لأن كل جزء من التعليمات البرمجية له مسار نسبي خاص به، ورابط قاعدة البيانات، والتحقق، وما إلى ذلك. يؤدي القيام بذلك إلى توفير المتاعب ويسمح لك بدمج التعليمات البرمجية وإعادة استخدامها
[لماذا تقوم بإنشاء إطار عمل MVC الخاص بك؟ 】
حتى الآن، لم أر العديد من أطر عمل MVC مكتوبة بلغة PHP. في الواقع، أنا أعرف شيئًا واحدًا فقط - Solar، وهو مكتوب بالكامل بلغة PHP5. والآخر هو Cake، الذي يحاول أن يكون RoR (Ruby on Rails - إطار عمل شبكي مفتوح المصدر للغة Ruby) من PHP. أنا شخصيا أشعر بعدم الرضا عن كلا الإطارين: فهما لا يستفيدان من التعليمات البرمجية الموجودة في PEAR وSmarty وما إلى ذلك؛ ولا تزال الكعكة الحالية فوضوية نسبيًا؛ وأخيرًا، Solar عبارة عن إطار مكتوب في الغالب بواسطة شخص واحد (لدي وليس هناك نية للقول إن مؤلفه، بول، ليس شخصًا صالحًا أو مبرمجًا جيدًا). ربما لا تجعلك هذه الأسئلة تنكرها، ومن المحتمل أنك لا تهتم بها على الإطلاق. ولكن لهذا السبب، أطلب منك أن تنظر إليهم قدر الإمكان.
[الطريقة القديمة]
إذا عدت إلى عام 2001 وألقيت نظرة على الكود الذي كتبته، فقد يجد المؤلف ملفًا يسمى template.txt، والذي يشبه هذا: www.phpv.net يرجى الإشارة إلى مصدر إعادة الطباعة
<?php
require_once('config.php'); // يتطلب معلومات أخرى، معلومات قاعدة البيانات، وما إلى ذلك.
$APP_DB = 'mydb';
$APP_REQUIRE_LOGIN = false; // اضبط على "صحيح" إذا كان البرنامج النصي يتطلب تسجيل الدخول
$APP_TEMPLATE_FILE = 'foo.php';
$APP_TITLE = 'تطبيقي'؛
إذا ($APP_REQUIRE_LOGIN == صحيح) {
إذا (!isset($_SESSION['userID'])) {
header("الموقع: /path/to/login.php");
مخرج()؛
}
}
$db = DB::connect('mysql://'.$DB_USER.':'.$DB_PASS.'@localhost/'.$APP_DB);
إذا (! الكمثرى::isError($db)) {
$db->setFetchMode(DB_FETCHMODE_ASSOC);
} آخر {
يموت($db->getMessage());
}
// ضع منطقك هنا
// إخراج القالب
include_once(APP_TEMPLATE_PATH.'/header.php');
include_once(APP_TEMPLATE_PATH.'/'.$APP_TEMPLATE_FILE);
include_once(APP_TEMPLATE_PATH.'/footer.php');
?>
يا إلهي، مجرد النظر إلى هذا الرمز يجعلني أشعر بالإحباط. الهدف من هذا الرمز هو التأكد من إمكانية تكييف كل تطبيق مع طريقة المعالجة هذه. على سبيل المثال، يمكنني ببساطة نسخ template.txt إلى myapp.php، وتغيير بعض المتغيرات، وسيتم تشغيله. ومع ذلك، فإن هذا النهج المنظم للغاية له بعض العيوب الخطيرة: ماذا
لو أراد مديري من المؤلف أن يستخدم myapp.php لإخراج PDF في بعض الحالات، وHTML في بعض الحالات، وSOAP في بعض الحالات (يتم إرسال طلبات XML مباشرة)، فماذا أفعل؟ يفعل؟
ماذا علي أن أفعل إذا كان هذا التطبيق يتطلب مصادقة IMAP أو LDAP؟
كيف أتعامل مع الأنواع المختلفة من التعليمات البرمجية (بما في ذلك عمليات التحرير والترقيات والحذف)؟
كيف أتعامل مع المصادقة متعددة المستويات (المسؤول مقابل غير المسؤول)؟
كيف يمكنني تمكين التخزين المؤقت للمخرجات؟ www.phpv.net يرجى الإشارة إلى مصدر إعادة الطباعة
[طريقة جديدة]
ضع كل شيء في إطار عمل MVC هذا، وستجد أن الحياة بسيطة للغاية. الرجاء مقارنة الكود التالي:
<?php
يمتد فئة myapp إلى FR_Auth_User
{
الوظيفة العامة __construct()
{
الأصل::__construct();
}
الوظيفة العامة __default()
{
// افعل شيئًا هنا
}
حذف الوظيفة العامة ()
{ }
الوظيفة العامة __destruct()
{
الأصل::__destruct();
}
}
?>
لاحظ أنه من الواضح أن هذا الرمز لا يُستخدم للارتباط بقاعدة بيانات، أو تحديد ما إذا كان المستخدم قد قام بتسجيل الدخول، أو إخراج أي معلومات أخرى. وحدة التحكم لديها كل شيء.
إذا كنت أرغب في المصادقة على LDAP، فيمكنني إنشاء FR_Auth_LDAP. يمكن لوحدة التحكم التعرف على طرق إخراج معينة (مثل $_GET['output']) ويمكنها التحويل إلى PDF أو SOAP في أي وقت. معالج الحذف مسؤول فقط عن الحذف ولا يهتم بأي شيء آخر. نظرًا لأن هذه الوحدة تحتوي على مثيل لفئة FR_User، فيمكنها ببساطة تحديد ما إذا كان المستخدم قد قام بتسجيل الدخول، وما إلى ذلك. Smarty، كمحرك قالب، يتحكم بشكل طبيعي في ذاكرة التخزين المؤقت، ولكن يمكن لوحدة التحكم أيضًا التحكم في جزء من ذاكرة التخزين المؤقت.
قد يكون الانتقال من الطريقة القديمة المذكورة سابقًا إلى طريقة MVC مفهومًا جديدًا وغير مألوف للعديد من الأشخاص، ولكن بمجرد التبديل إلى مثل هذا المفهوم، سيكون من الصعب جدًا الرجوع مرة أخرى.
[قم ببناء الطبقة السفلية]
أنا من محبي PEAR، وخاصة فئة PEAR_Error. يقدم PHP5 فئة جديدة مدمجة "Exception" والتي تحل محل PEAR_Error. لكن PEAR_Error لديه بعض الميزات العملية أكثر من الاستثناء. ولذلك، فإن أمثلة إطار عمل MVC في هذه السلسلة من المقالات ستستخدمه لمعالجة الأخطاء. على أي حال، لا يزال يتعين علي استخدام الاستثناء للحصول على الخطأ من المنشئ، حيث لا يمكنهم إرجاع الخطأ بأنفسهم.
الغرض من تصميم هذه الفئات الأساسية هو كما يلي:
استخدم PEAR لإضافة وظائف بسرعة إلى الفئات الأساسية
لإنشاء فئات مجردة صغيرة وعملية بشكل متكرر حتى يتمكن المستخدمون من تطوير التطبيقات بسرعة في هذا الإطار.
استخدم
phpDocumentor لإنشاء مستندات لجميع الفئات الأساسية
سيبدو التسلسل الهرمي للفئة كما يلي:
- سيوفر FR_Object الوظائف الأساسية لجميع الكائنات الأخرى المراد استخدامها (بما في ذلك التسجيل، وsetFrom() العامة، وtoArray())
- FR_Object_DB عبارة عن طبقة صغيرة توفر روابط قاعدة بيانات للفئات الفرعية والوظائف الأخرى
- FR_Module هو الفئة السفلية لجميع التطبيقات (وتسمى أيضًا الوحدات والنماذج وما إلى ذلك)
- FR_Auth هي الفئة السفلية لجميع آليات التحقق
· FR_Auth_User هي فئة تحقق تستخدم للتحقق من جميع الوحدات التي تحتاج إلى التحقق مما إذا كان المستخدم قد قام بتسجيل الدخول أم لا
· FR_Auth_No هو جميع "فئات التحقق الزائفة" للوحدات النمطية التي لا تتطلب التحقق من الصحة
- FR_Presenter هي الفئة الأساسية لجميع التطبيقات التي تتعامل مع التحميل والعرض
- FR_Presenter_Smarty هي طبقة العرض التقديمي التي تتضمن القدرة على تحميل محركات أقراص مختلفة. Smarty عبارة عن فئة قوالب جيدة جدًا، وهي تحتوي على آلية تخزين مؤقت ومجموعة تطوير نشطة (ملاحظة المترجم: من الواضح أن هذا إعلان ~)
· FR_Presenter_debug هي طبقة العرض لجزء التصحيح. بالاعتماد عليه، يمكن للمطورين تصحيح أخطاء التطبيقات وتصحيحها
. FR_Presenter_rest هي طبقة عرض REST تسمح للمطورين بإخراج التطبيقات بتنسيق XML،
من بنية الفئة الأساسية المذكورة أعلاه، يجب أن تكون قادرًا على رؤية أجزاء مختلفة من إطار عمل MVC. يوفر FR_Module كل ما تحتاجه الوحدة، بينما يوفر FR_Presenter طرق عرض مختلفة. في المقالة التالية من هذه السلسلة، سأقوم بإنشاء وحدة تحكم تربط جميع الفئات الأساسية المذكورة أعلاه معًا.
[معايير الترميز]
قبل أن تكتب التعليمات البرمجية بشكل رسمي، يجب عليك الجلوس ومناقشة (أو التفكير في) معايير الترميز مع شركائك (أو نفسك). تدور الفكرة العامة لبرمجة MVC حول نقطتين: إمكانية إعادة استخدام الكود (تقليل الصدفة) وتوحيد الكود. أوصي على الأقل بمراعاة النقاط التالية:
أول شيء يجب مراعاته هو معايير التسمية والاختصارات المتغيرة. لا تدخل في معركة كبيرة مع شركائك بسبب هذا، ولكن بمجرد وضع المعايير، يجب اتباعها من البداية إلى النهاية، خاصة عند كتابة تعليمات برمجية منخفضة المستوى (الفئات الأساسية).
قم بتخصيص بادئة قياسية لاستخدامها في جميع الوظائف والفئات والمتغيرات العامة. لسوء الحظ، PHP لا يدعم "مساحة الاسم (مساحة الاسم)". لذا لتجنب الارتباك والتعارض مع أسماء المتغيرات، فمن الحكمة استخدام بادئة. سأستخدم "FR_" كبادئة خلال هذه المقالة.
[كتابة الطبقة السفلية]
التخطيط على مستوى الملف مهم جدًا. التخطيط الهرمي الأساسي بسيط ومحدد إلى حد ما:
/
التكوين.php
Index.php
يشمل/
مصادقة.php
المصادقة/
رقم PHP
المستخدم.php
Module.php
Object.php
هدف/
DB.php
مقدم.php
المقدم/
common.php
debug.php
Smarty.php
سمارتي/
الوحدات/
مثال/
التكوين.php
example.php
tpl/
example.tpl
tpl/
تقصير/
مخبأ/
التكوين /
قوالب/
templates_c/
قد تعتقد أن مثل هذا التسلسل الهرمي للملفات يجب أن يمثل قدرًا كبيرًا من التعليمات البرمجية! صحيح، ولكن يمكنك القيام بذلك. بحلول نهاية السلسلة، ستجد أن برمجتك ستصبح أسهل وستتحسن سرعة تطويرك بشكل كبير.
في التسلسل الهرمي للملف، توجد جميع الفئات الأساسية في المجلد المتضمن. تستخدم كل وحدة وظيفية ملف تكوين وملف وحدة نمطية واحدًا على الأقل وملف قالب واحدًا. جميع الوحدات موجودة في مجلد الوحدات. لقد اعتدت على وضع ملفات القوالب في مجلد خارجي منفصل، وهو مجلد tpl.
config.php - ملف التكوين المركزي، الذي يحتوي على كافة متغيرات التكوين العامة.
Index.php - وحدة التحكم، سيتم وصفها بالتفصيل في المقالة التالية.
object.php - الفئة الأساسية لجميع الفئات الأساسية، مما يوفر معظم الوظائف التي تحتاجها الفئة. يرث FR_Object_DB هذه الفئة ويوفر روابط قاعدة البيانات.
المفهوم الأساسي للهيكل هو أن ترث جميع الفئات الفرعية من فئة مركزية بحيث تشترك جميعها في بعض الميزات المشتركة. يمكنك وضع وظيفة الارتباط بقاعدة البيانات في FR_Object، ولكن ليست كل الفئات بحاجة إلى هذه الوظيفة، لذا فإن FR_Object_DB لديه سبب للوجود، وسيناقشه المؤلف لاحقًا.
<?php
'
Log.php');
*FR_Object
*
* فئة الكائن الأساسية لمعظم الفئات التي نستخدمها في إطار عملنا.
* يوفر التسجيل الأساسي ووظيفة الضبط/الحصول.
*
* @author جو ستامب < [email protected] >
* @packageFramework
*/
فئة مجردة FR_Object
{
/**
* سجل $
*
* @var مختلط $log مثيل لسجل PEAR
*/
سجل $ محمي ؛
/**
*$أنا
*
* @var مختلط بمثيلات ReflectionClass $me
*/
محمي $me;
/**
* __ البناء
*
* @author جو ستامب < [email protected] >
* @الوصول العام
*/
الوظيفة العامة __construct()
{
$this->log = Log::factory('file',FR_LOG_FILE);
$this->me = new ReflectionClass($this);
}
/**
* تعيين من
*
* @author جو ستامب < [email protected] >
* @الوصول العام
* @param مختلط بصفيف بيانات $ من المتغيرات لتعيينه للمثيل
* @العودة فارغة
*/
مجموعة الوظائف العامة من($data)
{
إذا (is_array($data) && العد($data)) {
$valid = get_class_vars(get_class($this));
foreach ($صالح كـ $var => $val) {
إذا (إيسيت($data[$var])) {
$this->$var = $data[$var];
}
}
}
}
/**
* إلى المصفوفة
*
* @author جو ستامب < [email protected] >
* @الوصول العام
* @return مصفوفة مختلطة من متغيرات الأعضاء المرتبطة باسم المتغير
*/
الدالة العامة toArray()
{
$defaults = $this->me->getDefaultProperties();
$return = array();
foreach ($الافتراضيات كـ $var => $val) {
إذا ($هذا->$var مثيل FR_Object) {
$return[$var] = $this->$var->toArray();
} آخر {
$return[$var] = $this->$var;
}
}
إرجاع $return;
}
/**
* __تدمير
*
* @author جو ستامب < [email protected] >
* @الوصول العام
* @العودة فارغة
*/
الوظيفة العامة __destruct()
{
إذا ($هذا->سجل مثيل السجل) {
$هذا->سجل->إغلاق();
}
}
}
?>
auth.php – هذه هي الفئة الأساسية لجميع وظائف المصادقة. وهو ممتد من FR_Module، وتتمثل وظيفته الرئيسية في تحديد كيفية عمل فئة التحقق الأساسية.
تمامًا مثل FR_Module، لا تحتاج بعض الفئات إلى الاتصال بقاعدة البيانات بنفس الطريقة، يمكن إنشاء FR_Auth_No وتطبيقه على الفئات التي لا تتطلب وظائف المصادقة.
<?php
تمتد الفئة المجردة FR_Auth إلى FR_Module
{
// {{{ __ بناء ()
دالة __ بناء ()
{
الأصل::__construct();
}
// }}}
// {{{ مصادقة ()
مصادقة وظيفة مجردة () ؛
// }}}
// {{{ __destruct()
وظيفة __destruct()
{
الأصل::__destruct();
}
// }}}
}
?>
Module.php - قلب جميع الوحدات
<?php
تمتد الفئة المجردة FR_Module إلى FR_Object_Web
{
// {{{ ملكيات
/**
* مقدم $
*
* يُستخدم في FR_Presenter::factory() لتحديد العرض التقديمي (العرض)
* يجب استخدام الفصل للوحدة.
*
* @author جو ستامب < [email protected] >
* @var string $presenter
* @انظر FR_Presenter، FR_Presenter_common، FR_Presenter_smarty
*/
public $presenter = 'smarty';
/**
* بيانات $
*
* البيانات التي تم تعيينها بواسطة الوحدة النمطية والتي سيتم تمريرها في النهاية إلى العرض.
*
* @author جو ستامب < [email protected] >
* @var بيانات وحدة بيانات $ المختلطة
* @انظر FR_Module::set(), FR_Module::getData()
*/
بيانات
$ محمية = مصفوفة ()؛
* اسم $
*
* @author جو ستامب < [email protected] >
* @var string $name اسم فئة الوحدة النمطية
*/
اسم $ العام
/**
* ملف $tpl
*
* @author جو ستامب < [email protected] >
* @var string $tplFile اسم ملف القالب
* @انظر FR_Presenter_smarty
*/
public $tplFile
/**
* اسم الوحدة $
*
* @author جو ستامب < [email protected] >
* @var string $moduleName اسم الوحدة المطلوبة
* @انظر FR_Presenter_smarty
*/
public $moduleName = null;
/**
*$pageTemplateFile
*
* @author جو ستامب < [email protected] >
* @var string $pageTemplateFile اسم قالب الصفحة الخارجية
*/
public $pageTemplateFile = null;
// }}}
// {{{ __construct()
/**
* __ البناء
*
* @author جو ستامب < [email protected] >
*/
الوظيفة العامة __construct()
{
الأصل::__construct();
$this->name = $this->me->getName();
$this->tplFile = $this->name.'.tpl';
}
// }}}
// {{{ __تقصير()
/**
* __تقصير
*
* يتم تشغيل هذه الوظيفة بواسطة وحدة التحكم إذا لم يتم تحديد حدث
* في طلب المستخدم.
*
* @author جو ستامب < [email protected] >
*/
الوظيفة العامة المجردة __default();
// }}}
// {{{ مجموعة($var,$val)
/**
* تعيين
*
* قم بتعيين البيانات الخاصة بوحدتك في النهاية
* فئة مقدم العرض عبر FR_Module::getData().
*
* @author جو ستامب < [email protected] >
*param string $var اسم المتغير
* @param مختلط قيمة $val للمتغير
* @العودة فارغة
* @انظر FR_Module::getData()
*/
مجموعة الوظائف المحمية($var,$val) {
$this->data[$var] = $val;
}
// }}}
// {{{ getData()
/**
*الحصول على البيانات
*
* إرجاع بيانات الوحدة.
*
* @author جو ستامب < [email protected] >
* @العودة مختلطة
* @انظر FR_Presenter_common
*/
الوظيفة العامة getData ()
{
إرجاع $this->data;
}
// }}}
// {{{ isValid($module)
/**
*صالح
*
* يحدد ما إذا كانت $module هي وحدة إطار عمل صالحة
* وحدة التحكم لتحديد ما إذا كانت الوحدة تناسب إطار عملنا
* القالب إذا كان يمتد من كل من FR_Module و FR_Auth فيجب أن يكون كذلك
* جيد للتشغيل.
*
* @author جو ستامب < [email protected] >
*@ثابت
* @param وحدة $ مختلطة
* @return bool
*/
الوظيفة الثابتة العامة صالحة($module)
{
العودة (is_object($module) &&
$module مثيل FR_Module &&
$module مثيل FR_Auth);
}
// }}}
// {{{ __destruct()
الوظيفة العامة __destruct()
{
الأصل::__destruct();
}
// }}}
}
?>
presenter.php - جوهر طبقة العرض.
<?php
فئة FR_Presenter
{
// {{{ مصنع($type,FR_Module $module)
/**
*مصنع
*
* @author جو ستامب < [email protected] >
* @الوصول العام
*param string $type نوع العرض التقديمي (وجهة نظرنا)
* @param مختلط $module وحدتنا التي سيعرضها المقدم
*return مختلط PEAR_Error عند الفشل أو مقدم صالح
*@ثابت
*/
مصنع الوظائف العامة الثابتة($type,FR_Module $module)
{
$file = FR_BASE_PATH.'/includes/Presenter/'.$type.'.php';
إذا (تشمل(ملف $)) {
$class = 'FR_Presenter_'.$type;
إذا (class_exists($class)) {
$presenter = new $class($module);
إذا ($مثيل مقدم FR_Presenter_common) {
إرجاع مقدم العرض $؛
}
return PEAR::raiseError('فئة العرض التقديمي غير صالحة: '.$type);
}
return PEAR::raiseError('لم يتم العثور على فئة العرض التقديمي: '.$type);
}
return PEAR::raiseError('لم يتم العثور على ملف المقدم: '.$type);
}
// }}}
}
?>
في المقالة التالية، سأقدم بنية وحدة التحكم (وحدة التحكم في MVC، وindex.php في هذه المقالة). في المقالة الثالثة سأقدم طبقة العرض التقديمي (View in MVC). في المقالة الرابعة، سأستخدم وحدة نمطية محددة كمثال لإنشاء تطبيق (الوحدة النمطية أو النموذج في MVC).