7.3. Subclassing

7.3.1. مقدمة

تم بناء Zend_Controller و فى اعتبارنا ان يكون قابل للتمدد بسهولة, سواء عن طريق إن شاء subclasses ترث من الـ class الموجودة او إنشاء classes جديدة تعتمد على الـ interfaces المسمى Zend_Controller_Router_Interface و Zend_Controller_Dispatcher_Interface.

الأسباب الممكنة لإنشاء router 'موجه' أو dispatcher 'منفذ' جديد , من الممكن أن تكون :

  • احيانا يكن نظام التوجيه "routing" الحالى غير مناسب فى بعض الحالات, مثل أن يتم إستخدامه فى موقع ما يستخدم أسلوب أخر لأعادة التوجيه "routing" لا يتوافق مع ميكانيكية عمل الموجه الذى يوفره إطار عمل Zend.

  • ربما تريد أن تطبق عملية التوجيه "routing" بشكل او بطريقة مختلفة تماماً , كما قرأت , Zend_Controller_Router يتعامل فقط مع الـ URIs, لكن من المحتمل أنك ستود إستخدام الـ MVC pattern لتطوير نوع أخر من البرامج , مثل تطبيقات الـ console , ففى هذه الحالة ستحتاج الى router خاص يقوم بمعالجة مدخلات الـ command line ليتعرف على الـ route المطلوب .

  • و ربما تكن الألية التى يوفرها Zend_Controller_Dispatcher غير متناسبة مع احتياجك , الإعدادات الأساسية تعتبر أن الـ controllers عبارة عن classes و أن الـ actions عبارة عن methods فى هذه الـ classes , لكن هناك استراتيجيات اخرى يمكن اعتمادها لتنفذ نفس الأداء , على سبيل المثال يمكن اعتبار الـ controllers عبارة عن مجلدات "directories" و الـ actions عبارة عن ملفات داخل هذه المجلدات.

  • و ربما تود أن توفر امكانيات و خصائص اضافية يمكن أن ترثها كل الـ controllers لديك , على سبيل المثال , الأعدادات الأساسية لا توفر امكانية أن يتكامل Zend_Controller_Action فى عمله تلقائياً مع Zend_View , لكن يمكنك أن تنشئ الـ controller الخاص بك الذى يوفر هذه الأمكانية, و ذلك عن طريق الوراثة من Zend_Controller_Action , و إستخدامه لن يحتاج إلى أى تعديلات على Zend_Controller_Router أو Zend_Controller_Dispatcher.

يرجى أن تكن حريص عند محاولة تغيير أجزاء من النظام , تحديداً المنفذ "dispatcher". إحدى ميزات Zend_Controller هى أنه يوفر الأمكانيات الأساسية لبناء التطبيقات , لكن ربما إذا تم تغيير جزء كبير من سلوكه الأساسى , ستضيع بعض ميزاته المتوافرة فى حالته الطبيعية. لكن بالطبع هناك احتياجات و حالات مختلفة و لا يمكن أن يقوم حل واحد بتوفير كل هذه الأحتياجات , فلذلك تتوافر لديك الحرية التامة للتعديل إذا احتجتها.

7.3.2. التعريفات

عند إنشائك لـ subclasses من اى من الـ classes المتوفرة فى Zend_Controller , يفضل أن تستخدم الأساليب التالية لتعريف الـ classes او اسماء الملفات , اتباعك لهذه الأساليب فى التعريف سيسهل لأى مبرمج أخر يستخدم ZF أن يفهم كيفية عمل مشروعك .

7.3.2.1. Prefix

كل الـ classes المتوفرة داخل ZF تتبع نفس الأسلوب فى التعريف حيث أن كل class يوجد فى بداية اسمه النص "_Zend" , هذا هو ما يسمى الـ prefix , لذلك ننصح أن تقوم بتسمية كل الـ classes التى تنشئها بنفس الأسلوب, على سبيل المثال أن كان إسم الشركة التى تعمل بها هو "Widget" , فيمكنك أن تضع الـ prefix الخاص بأسماء ال classes خاصتك ليكون "_Widget".

7.3.2.2. بنية المجلدات

كل الـ classes الخاصة بـ Zend_Controller يتم حفظها فى المجلد library كما يلى :

/library
  /Zend
    /Controller
      Action.php
      Dispatcher.php
      Router.php

عند القيام بإنشاء subclasses من الـ classes الخاصة بـ Zend_Controller , ينصح أن يتم وضع الـ classes الجديدة فى مجلدات لها بنية معرفة تحت اسم الـ prefix الذى تستخدمه , هذا سيسهل ايجاد هذه الـ classes من قبل أى مبرمج يعمل على مشروعك .

على سبيل المثال, إن قامت شركة اسمها widget بإنشاء نسختها المعدلة من الموجه "router" , أن تكن بنية المجلدات بهذا الشكل .

/library
  /Zend
  /Widget
    /Controller
      Router.php
      README.txt

لاحظ أن فى هذا المثال المجلد /Widget/Controller يأتى على نفس هيئة /Zend/Controller و هذا الأسلوب يمكنك أن تتبعه إن امكن . فى هذا المثال يوجد Widget_Controller_Router و الذى من الممكن أن يكن subclass او بديل لـ Zend_Controller_Router و ذلك بتطبيقه للـ interface المسمى Zend_Controller_Router_Interface.

أيضاً, لاحظ فى المثال السابق أن الملف README.txt تم وضعه فى المجلد /Widget/Controller , تنصح ZF أن يتم توفير مستندات توثيقية "documentations" للمشاريع التى تقوم بها إلى جانب اختبارات "tests" للبرنامج عند تقديمه لعميلك, لكن ننصح ايضا ان تضع ملف README.txt فى المجلد لتوضيح تغييراتك الجديدة على الـ classes و كيف سيتم التعامل معها .

7.3.3. Router Interface

الـ interface المسمى Zend_Controller_Router_Interface يوفر تعريف لـ method واحد فقط :

<?php
				
  /**				
   * @param  Zend_Controller_Dispatcher_Interface
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Dispatcher_Token|boolean
   */
  public function route(Zend_Controller_Dispatcher_Interface $dispatcher);

?>

عملية التوجيه "routing" تحدث مرة واحدة , و ذلك بمجرد استلام النظام للطلب , و يتواجد الـ router لينتج Zend_Controller_Dispatch_Token و الذى يحدد الـ controller و الـ action التابع لهذا الـ controller , و هذا يتم تمريره لاحقا الى المنفذ "dispatcher", و فى حالة أنه إذا لم يتم التعرف على بيانات الـ route (ربما نتج عن عملية تحويل غير منطقية ) سيتم ارجاع القيمة FALSE .

بعض الموجهات "routers" تقوم بمعاجلة الطلبات و تحب ان تتأكد من أن الـ token الذى قامت بإنشائه قابل للتنفيذ قبل أن تقوم بإرجاعه , و لهذا السبب يقوم الـ router بإستقبال الـ object handle الخاص بالـ dispatcher كالبراميتر الأساسى للـ method المسمى ()route, و يقوم الـ dispatcher بتوفير method يسمى ()isDispatchable للقيام بهذا الأختبار.

7.3.4. Dispatcher Interface

سيقوم Zend_Controller_Front بإستدعاء الـ router ليستقبل أول dispatch token , و الذى سيتم تمريره إلى الـ dispatcher . سيقوم الـ dispatcher بتنفيذ الـ action (عن طريق إنشاء instance من الـ controller ثم إستدعاء الـ action منه ) , ثم سيقوم بإرجاع اما القيمة FALSE أو dispatch token جديد.

يقوم Zend_Controller_Front يإستدعاء الـ dispatcher بشكل متكرر و ذلك إلى أن لا يتم إرجاع dispatch token جديد من الـ dispatcher , و هذا ما يسمى بحلقة التنفيذ او dispatch loop , و هذا الأسلوب يسمح بتنفيذ الـ actions بتسلسل منطقى إلى ان ينتهى كل العمل .

الـ interface المسمى Zend_Controller_Dispatcher_Interface يوفر تعريف لإثنين من الـ methods :

<?php
				
/**
 * @param  Zend_Controller_Dispatcher_Token $route
 * @return boolean
 */
public function isDispatchable(Zend_Controller_Dispatcher_Token $route);

?>

يقوم ()isDispatchable بالتأكد من ان الـ dispatch token الممرر إليه قابل للتنفيذ. و يرجع TRUE إن كان قابل , أو يرجع FALSE إن كان غير قابل للتنفيذ, أما تعريف المعنى المقصود من أن هذا الـ dispatch token قابل للتنفيذ ام لا, متروكة للمبرمج الذى سيستخدم الـ interface , و فى تطبيقنا الأساسى الذى اعتمدناه فى Zend_Controller_Dispatcher كانت القابلية للتنفيذ هى ان يكون الملف الذى يحتوى الـ controller موجود , و أن يكون الـ class الخاص بهذا الـ controller موجود فى الملف , و أن الـ method الذى يحمل نفس اسم الـ action موجود داخل هذا الـ class .

<?php
			
/**
 * @param  Zend_Controller_Dispatcher_Token $route
 * @return Zend_Controller_Dispatcher_Token|boolean
 */
public function dispatch(Zend_Controller_Dispatcher_Token $route);

?>

()dispatch هو من يقوم بالتفيذ , هذا الـ method يجب أن ينفذ الـ action الخاص بالـ controller , و يجب أن يرجع إما dispatch token جديد ليتم تنفيذه ايضا , أو القيمة FALSE ليعرف انه لا يوجد عمل أخر يجب تنفيذه.