قائمة المحتويات
يوفر Zend_Controller
البنية اللازمة لإنشاء مواقع تعتمد على النمط ( MVC)
او Model-View-Controller.
نظام Zend_Controller
تم تصميمه ليكن خفيف و بسيط و قابل للتمدد و ان يكن modular.
إنه نظام مصغر الى اقصى حد معقول ليوفر المرونة و الحرية لمن يستخدمه, فى حين انه يوفر كل البنية الازمة , ولهذا
السبب يتشارك اجزاء النظام المبنى بـ Zend_Controller
فى هيئة تنظيم الكود و المصطلحات.
Zend_Controller
يعتمد على على عدة components اخرى, فى حين انه لا يشترط عليك ان
تكن مدرك ما يحدث خلف كواليس هذه الـ components لتتمكن من استخدام النظام, إلا ان بعض المعرفة يعملية تدفق
العمليات ستكن مساعدة جدا لك.
Zend_Controller_Front
يقود و يتحكم فى تدفق العمليات داخل نظام Zend_Controller
, و هو تطبيقنا للنمط المسمى
FrontController , و يقوم ايضا Zend_Controller_Front
بإستلام و معالجة كل الطلبات المستلمة
من قبل السيرفر , وهو مسئول بالكامل عن نقل هذه الطلبات إلى الـ ActionControllers اى
(Zend_Controller_Action
).
Zend_Controller_Request_Abstract
يمثل
بيئة الطلب "request environment" و يوفر methods لتحديد و جلب
أسماء الـ controller و الـ action و أى بارامترات request أخرى,
إضافة إلى ذلك يقم بتعقب إن ما كان الـ controller الذى يحويه قد تم
تنفيذه من قبل Zend_Controller_Dispatcher
,
و امتدادات للـ abstract request object يمكنك أن تستخدم لكبسلة
الـ request environment بالكامل, بما يسمح للـ routers أن تسحب
معلومات من الـ request environment لكى تقوم بتحديد أسماء الـ
controller و الـ action .
حسب الإعدادات الأساسية, يتم إستخدام
Zend_Controller_Request_Http
,
و الذى يوفر امكانية الوصول إلى الـ HTTP request environment
بالكامل.
يتم إستخدام Zend_Controller_Router_Interface
لتعريف الـ routers, عملية الـ routing هى عملية تحليل الـ request environment
لمعرفة أى controller و أى action تابع للـ controller , يجب أن يستلم
الـ request , هذا الـ controller و الـ action و أى بارامترات أخرى سيتم
تحديدها فى كائن الـ request و الذى سيتم معالجته بعدها بواسطة
Zend_Controller_Dispatcher
,
تحدث عملية الـ routing مرة واحدة: عند إستلام الطلب الأساسى
و قبل أن يتم تنفيذ أول controller .
الـ router الأساسى , Zend_Controller_Router
,
يستلم الجزء الأخير من URI كما هو محدد فى
Zend_Controller_Request_Http
و يقوم
بفك محتوياته إلى controller و action و أى باريمترات أخرى تم تمريرها
فى الـ url , فعلى سبيل المثال, الـ URL هذا
http://localhost/foo/bar/key/value
سيتم فك محتوياته
ليتم إستخدام الـ controller المسمى foo و الـ action المسمى bar و سيتم تحديد
باراميتر بالأسم key سيحمل القيمة value.
يُستخدم Zend_Controller_Dispatcher_Interface
لتعريف الـ dispatchers, عملية الـ dispatching هى جلب أسم الـ controller
و الـ action من كائن الـ request و على أساسهما يتم تحديد ملف/class الـ
controller و الـ action method فى الـ controller , و إن لم يتم إيجاد
الـ controller أو أن الـ action غير موجود , سيتم القيام بالعمليات الازمة
لمعرفة الـ controllers و الـ actions الأساسية الواجب تنفيذها.
عملية الـ dispatching الفعلية تتكون من إنشاء نسخة من الـ controller class و إستدعاء الـ action method فى هذا الـ class, و عكس الـ routing الذى يحدث مرة واحدة, الـ dispatching يحدث فى حلقة متكررة, و إن تم تغيير حالة الـ dispatch الخاصة بكائن الـ request عند أى نقطة, سيتم تكرار الحلقة من جديد, و إستدعاء أى action يوجد فى كائن الـ request, و فى أول مرة تنتهى الحلقة التكرارية مع أن تكون حالة الـ dispatch الخاصة بكائن الـ request (قيمتها true), سيتم إنهاء العملية.
الـ dispatcher الأساسى هو
Zend_Controller_Dispatcher
,
و هو يُعرِف الـ controllers على انهم CamelCasedClasses
ينتهون بالكلمة Controller , و الـ action methods على انهم
camelCasedMethods تنتهى بالكلمة Action : كما فى
SomeFooController::barAction
,
سيتم الأشارة للـ controller على انه somefoo و الـ action
هو bar.
ايضاً, يمكنك تحديد module
ليتم
إستخدامه عند تحميل الـ controller, الـ module يتم
إستخدامه لتحديد مجلد فرعى و/أو class prefix ليتم إستخدامه
عند تحميل الـ controller , و بسهولة , يمكنك تحديد الـ module
عند تحديد مجلدات الـ controllers .
$front->setControllerDirectory(array( 'default' => '/path/to/controllers', 'user' => '/path/to/controllers/user', 'admin' => '/path/to/controllers/admin' ));
فى المثال بالأعلى, إن تم إختيار الـ module المسمى 'user'
مع الـ controller المسمى 'news' , سيتم البحث عن
NewsController.php
فى
/path/to/controllers/user
أولاً قبل البحث
فى المسارات الأخرى. أيضاً, اولاً سيتم أعتبار أن الـ class
يسمى User_NewsController
و بعدها
NewsController
. و سيكون الـ request
بهذا الشكل مثلاً ,
http://localhost/news/action/module/user
بإستخدام الـ router الأساسى, و
http://localhost/module/news/action
بإستخدام
الـ RewriteRouter.
تعتبر الـ modules مفيدة فى حالة أنك تريد فصل الكود إلى مجلدات فرعية أو إستخدام أكواد من مصدر أخر "third party" أو إعادة إستخدام نفس الـ controller library فى تطبيقات مختلفة.
Zend_Controller_Action
هو الجزء الأساسى فى الـ controller component,
كل controller عبارة عن class يرث من
Zend_Controller_Action
,
و هذا الـ class لديه action methods.
Zend_Controller_Response_Abstract
يُعرِف الـ response class الأساسى الذى يُستخدم لتجميع و إرجاع
الردود "responses" من الـ action controllers, و هو يقوم بتجميع
كل من الـ headers و محتويات جسم الصفحة, و حيث أنه يُطبِق
__toString()
, فيمكن طباعته مباشرة
لإرسال كل الـ headers و المحتويات مرة واحدة.
الـ response class الأساسى هو
Zend_Controller_Response_Http
,
و الذى يتوافق مع العمل فى بيئة الـ HTTP.
كيفية عمل Zend_Controller
تعتبر بسيطة نسبياً .
حيث يتم إستلام طلب "request" عن
طريق Zend_Controller_Front
, و الذى بدوره يستدعى
Zend_Controller_Router
ليعرف أى
Controller (و أى action فى هذا الـ controller ) سيتم
تنفيذه .
ثم يقوم Zend_Controller_Router
بتقسيم الـ URI إلى اجزاء صغيرة
ليحدد اسم الـ controller و الـ action فى الـ request.
بعدها يقوم Zend_Controller_Front
بتشغيل حلقة تنفيذ متكررة
"dispatch loop".
حيث يقوم بإستدعاء Zend_Controller_Dispatcher
و تمرير الـ
request إليه , ليقوم بتنفيذ
الـ controller و الـ action المحددين فى الـ request (أو الأساسيين).
و بعد إنتهاء الـ controller من عمله , يعود التحكم إلى
Zend_Controller_Front
.
إذا قام الـ controller بتوضيح انه يجب تنفيذ controller اخر و ذلك عن طر
يق تغييره لحالة الـ dispatch الخاصة بالـ request ,
سيتم إستكمال الحلقة التنفيذية و سيتم تنفيذ الـ controller الجديد , أو
تنتهى العملية .
الـ request object هو عبارة عن value object بسيط يتم تمريره
ما بين Zend_Controller_Front
و الـ router
و الـ dispatcher "المنفذ" و الـ controller classes , و يحمل هذا الكائن
بيانات الـ controller و الـ action و البارامترات التى سيتم تمريرها إلى
الـ action , بالأضافة إلى بيانات الـ request environment أى كانت ..
سواء HTTP او CLI او PHP-GTK .
يمكن الوصول إلى إسم الـ controller بواسطة
getControllerName()
و
setControllerName()
.
يمكن الوصول إلى إسم الـ action الذى سيتم إستدعائه من الـ controller بواسطة
getActionName()
و
setActionName()
.
البارامترات التى سيتم تمريرها إلى الـ action هى عبارة عن
associative array تتكون من أزواج من key/value يمكن
الوصول إليهم من خلال getParams()
و
setParams()
, أو يمكن الوصول إلى كل
براميتر على حدى بواسطة getParam()
و
setParam()
.
إعتماداً على نوع الـ request, من الممكن أن يكن هناك methods
اخرى متوفرة , الـ request الأساسى الذى يتم إستخدامه
Zend_Controller_Request_Http
,
على سبيل المثال, يوفر methods لإرجاع الـ request URI و
معلومات الـ path و بارامترات $_POST
و
$_GET
.. إلخ.
يتم تمرير الـ request object إلى الـ front controller , أو إذا لم يتم توفير واحد , يتم إنشاء واحد فى بداية عملية التنفيذ "dispatch" و قبل أن يحدث الـ routing , و يتم تمريره إلى كل كائن فى سلسلة التنفيذ "dispatch chain".
ايضاً, يكن الـ request object مفيد إلى حد ما فى الـ testing , حيث أنه من الممكن أن يقوم المطور بالتعديل فى قيم الـ request environment بما فيها أسم الـ controller و الـ action و البارمترات و الـ URI ..إلخ, ثم يمرر الـ request object إلى الـ front controller ليختبر كيف سيتعامل برنامجه مع هذه البيانات, و عند جمعه مع إستخدام الـ response object , يصبح من الممكن عمل unit testing لتطبيقات الـ MVC بشكل موسع و دقيق.
قبل أن تتمكن من إنشاء اول controller بنفسك, يجب ان تفهم كيف تعمل عملية ال
توجيه "routing" كما هى مطبقة فى
Zend_Controller_Router
. تذكر أن كيفية العمل مقسمة الى عمل
يه توجيه و التى تحدث مرة واحدة ,
و عملية تنفيذ "dispatching" و التى تحدث بعدها فى حلقة تكرارية.
يقوم Zend_Controller_Front
بإستدعاء
Zend_Controller_Router
(أو أى router أخر متوافر)
ليقوم بترجمة محتوى الـ URI و يستخرج اسم الـ controller و اسم الـ
action فى هذا الـ controller .
يقوم Zend_Controller_Router
بجلب الـ URI من الـ request object
ثم يقوم بتفكيكه ليعرف أسم كل من الـ controller و الـ action و
أى بارمترات URL ممررة فى المسار ثم يقوم بوضع كل هذه البيانات
فى الـ request object.
يقوم Zend_Controller_Router
بعملية بسيطة ليتعرف على اسم
الـ controller و اسم الـ action التابع لهذا الـ controller :
http://framework.zend.com/controller/action/
لاحظ بالأعلى ان اول قسم دائما هو اسم الـ controller و أن الق سم التانى دائما يحمل أسم الـ action.
اختيارياً, يمكنك تمرير قيم فى الـ URI و التى سيتم تمريرها بعدها الى الـ controller , و هذا يكون على شكل زوج من key/value :
http://framework.zend.com/controller/action/key1/value1/
إذا لم يتم إيجاد إى من الـ controller أو الـ action فى مسار الـ URI,
فسيقوم Zend_Controller_Dispatcher
بمحاولة
جلب هذه القيم من بارامترات الـ request object , و إن لم يتم إيجادهم ,
فسيستخدم القيم الأفتراضية , و فى كلا الحالتين , القيم الأفتراضية هى
"index
".
و المثال التالى يوضح ذلك:
http://framework.zend.com/roadmap/future/ Controller: roadmap Action : future http://framework.zend.com/roadmap/ Controller: roadmap Action : index http://framework.zend.com/ Controller: index Action : index
ايضاً, يمكنك تحديد controllers موجودة فى مجلدات فرعية أو modules بعدة طرق :
أسماء الـ controllers التى تحتوى شرطة (-) أو شرطة منخفضة (_)
يمكن إستخدامها, على سبيل المثال,
http://framework.zend.com/admin-roadmap/future
من المفترض أن تشير إلى الـ controller
المسمى Admin_RoadmapController .
عن طريق تفعيل الباراميتر useModules
فى
الـ front controller , ستتمكن من إستخدام مجلدات فرعية عن طريق
URIs افضل, حينها الـ URL الخاص بالمثال السابق سيبدو مثل
http://framework.zend.com/admin/roadmap/future
.
, لعمل ذلك, قم بتفعيل الباراميتر useModules
فى الـ front controller أو الـ router :
$front->setParam('useModules', true); // or $router->setParam('useModules', true);
هذا سيعمل مع إستخدام سواء أى من الـ router الأساسى أو الـ RewriteRouter.
المرونة | |
---|---|
إن كنت تريد المزيد من الأمكانيات المرنة, فربما ستود إلقاء نظرة على قسم 7.4.3, “Zend_Controller_Router_Rewrite”. |
أسم الـ controller و أسم الـ action فى هذا الـ controller و
أى بارامترات اختيارية أخرى (تشمل الـ module), توجد فى الـ request object ,
و عندما يدخل Zend_Controller_Front
دورة التنفيذ "dispatch loop" , سيتم تمرير الـ request object
إلى Zend_controller_Dispatcher
.
عملية الـ dispatching هى أخذ الـ request object ,
(الذى يرث Zend_Controller_Request_Abstract
),
و إستخراج أسم الـ controller و الـ action و أى بارامترات اختيارية
موجودة به, ثم إنشاء نسخة من هذا الـ Controller و إستدعاء الـ Action
من هذا الـ controller , و فى حالة أنه لم يتم إيجاد قيم لأسماء الـ controller
أو الـ action, سيتم إستخدام القيم الأفتراضية الخاصة بهم,
و القيمة الأفتراضية التى يحددها Zend_Controller_Dispatcher
لهما هى index
, لكن يمكن للمطور أن يقوم بتغيير هذا بأستخدام
setDefaultController()
و
setDefaultAction()
.
عملية الـ dispatching تحدث فى حلقة تكرارية داخل الـ front controller , و قبل حدوث الـ dispatching , يقوم الـ front controller بعمل route للـ request ليجلب أى قيم حددها المستخدم للـ controller و الـ action و أى بارمترات أخرى, ثم يدخل بعد ذلك فى الـ dispatch loop , لينفذ الـ request.
عند بداية كل دورة , يتم وضع علامة "flag" فى الـ request object توضح أن الـ action قد تم تنفيذه , و إن قام action أو plugin ماقبل/بعد التنفيذ بتعديل قيمة الـ flag, الـ dispatch loop ستستمر و ستحاول تنفيذ الـ request مرة أخرى, و عن طريق تغيير الـ controller و/أو الـ action فى الـ request و إعادة تعديل قيمة الـ dispatch flag , يمكن للمطور أن يُعرِف سلسلة من الـ requests ليتم تنفيذها.
الـ action controller method الذى يتحكم فى التنفيذ هو
_forward()
; إستدعى هذا الـ method من أى
pre/postDispatch()
أو action methods,
مع توفير أسم controller و action و اختيارياً أى باراميترات إضافية
تريد أن يتم إرسالها للـ action الجديد:
public function myAction() { // do some processing... // forward to another action, FooController::barAction(): $this->_forward('foo', 'bar', array('baz' => 'bogus')); }
الـ response object هو الزوج المنطقى للـ request object,
و ظيفته هى جمع المحتوى و/أو الـ headers ليتم إخراجهم ككلتة واحدة,
و ايضاً, سيقوم الـ front controller بتمرير أى exceptions تم التقاطها
إلى الـ response object , و بهذا يسمح للمطور بأن يعالج الـ exceptions
بشكل أفضل, و يمكن إلغاء هذا السلوك بإستخدام
Zend_Controller_Front::throwExceptions(true)
:
$front->throwExceptions(true);
حيث أن الـ response object يطبق
__toString()
, فيمكن أن يتم طباعته مباشرة
بدون مشاكل , و هذا يسمح بالأستخدامات التالية :
echo $controller->getResponse(); // or $response = $controller->getResponse(); echo $response;
يجب على المطورين إستخدام الـ response object فى الـ action controllers خاصتهم , فبدلا من طباعة الخرج مباشرة و إرسال الـ headers , قم بتمريرهم إلى الـ response object :
// Within an action controller action: // Set a header $this->getResponse() ->setHeader('Content-Type', 'text/html') ->appendBody($content);
عن طريق عمل هذا, كل الـ headers سيتم إرسالها مرة واحدة , و قبل عرض المحتوى .
يجب أن يحدث exception فى أى تطبيق, لذلك تحقق
من الـ exception flag الخاصة بالـ response object
بأستخدام isException()
, و يمكنك
جلب الـ exception بأستخدام getException
,
ايضاً, من الممكن أن تقوم بإنشاء response objects تقوم بالتحويل
إلى error pages أو تقوم بعمل log لرسائل الـ exception أو أن تقوم
بعمل formating جميل لرسائل الـ exception (لبيئات التطوير) .. إلخ.
يمكنك جلب الـ response object بعد عملية الـ
dispatch()
للـ front controller ,
أو تطلب من الـ front controller أن يرجع الـ response object
بدلاً من إرسال الخرج.
// retrieve post-dispatch: $front->dispatch(); $response = $front->getResponse(); if ($response->isException()) { // log, mail, etc... } // Or, have the front controller dispatch() process return it $front->returnResponse(true); $response = $front->dispatch(); // do some processing... // finally, echo the response echo $response;
حسب الأعدادات الأساسية, رسائل الـ exception لا يتم
عرضها, هذا السلوك يمكن تغييره بإستدعاء
renderException
, أو السماح للـ front controller
ان يلقى Exceptions كما موضح بالمثال بالأعلى بإستخدام
throwExceptions()
.
$response->renderExceptions(true); $front->dispatch($request, $response); // or: $front->returnResponse(true); $response = $front->dispatch(); $response->renderExceptions(); echo $response; // or: $front->throwExceptions(true); $front->dispatch();