วันพุธที่ 1 ธันวาคม พ.ศ. 2553

$zend{'2.2-day'}='create project';

หลักจากเห็น source code ของตัวอย่างการเริ่มใช้งานใน Getting start with Zend Framework #1 แล้ว บทความนี้จะขอขยายความต่อแล้วกันน้ะครับ
ดังที่กล่าวไว้แล้ว สถาปัตยกรรมของ framework ตัวนี้ เป็น MVC

Bootstrap

ในไฟล์ทำงานหลัก ที่โหลดทุกอย่างที่ต้องการ และกำหนดค่าเริ่มต้นต่างๆ ซึ่งเราใช้ไฟล์ index.php เป็น bootstrap เพียงไฟล์เดียว

โครงสร้างของ URI

URI ที่ request มานั้น จะมีโครงสร้างคือ {ROOT}/Controller/Action/Param1/Value1/Param2/Value2 ซึ่งถ้าไม่ระบุ Controller และ ไม่ระบุ Action จะ default ไปที่ IndexController และ IndexAction เช่น
  • / จะเป็นการเรียกใช้งาน default controller - IndexController และ default action - IndexAction
  • /blog จะเป็นการเรียก BlogController และ IndexAction (ที่อยู่ใน BlogController)
  • /blog/new จะเป็นการเรียก BlogConroller และ NewAction (ที่อยู่ใน BlogController)
  • /blog/edit/id/1 จะเป็นการเรียก BlogController, EditAction และมี id=1 เป็น GET parameter ส่งไป
  • เป็นต้น

มาดูในไฟล์ Bootstrap::index.php กัน

ลองมาดูเนื้อหาในไฟล์ index.php กันเลย ว่ามีอะไรบ้าง
error_reporting(E_ALL|E_STRICT); 
date_default_timezone_set('Europe/London');
ส่วน นี้เป็นการ make sure ว่าให้ app ของเราแสดง error, warning และ notice ทุกอย่างที่เกิดขึ้น ที่ต้องใส่ไว้ เพราะว่าเผื่อใน php.ini ไม่ได้เปิดให้แสดง นอกจากนี้แล้ว บรรทัดต่อมาเป็นการเซต default time zone สำหรับ PHP5.1+ ซึ่งจริงแล้วถ้าบ้านเราควรเซตเป็น Aisa/Bangkok แต่ code ด้านบน ผม copy เขามาทั้งดุ้น :P
set_include_path('.' . PATH_SEPARATOR . './library' 
. PATH_SEPARATOR . './application/models/'. PATH_SEPARATOR . get_include_path());
code นี้สำหรับเซต include_path สำหรับ app ของเรา ตรงจุดนี้ค่อนข้างสำคัญน้ะครับ ว่าต้องเซต include_path ให้ไปถึง library, application/models ด้วย เพราะต่อไปนี้ ถ้าจะ include หรือ require library ต่างๆ (ที่อยู่ใน Zend Framewok) เราจะใช้ Zend_Loader แทนการ include หรือ require แต่ทั้งนี้ทั้งนั้น ก่อนที่เราจะใช้งาน Zend_loader เราก็ต้อง require Class ตัวนี้ก่อน
include "Zend/Loader.php";
ต่อ ไปนี้ ถ้าจะใช้งาน class อะไร เราก็ไม่ต้อง include หรือ require แล้ว ให้ Loader ทำการโหลดให้เรา แต่ในทางปฏิบัติแล้ว การทำงานข้างในของ Loader ก็คงไม่หนีไปจาก include หรือ require เท่าไหร่ (อันนี้คาดการณ์เอาน้ะคับ ยังไม่ได้เข้าไป hack ดู) แต่สิ่งที่เพิ่มเติมมา จะเป็นส่วนของการจัดการ error หรือ error handler คือปกติ ถ้าเรา include หรือ require มาเอง ถ้าไฟล์นั้นๆ ไม่มีอยู่ (เช่นเราระบุ path ผิด) php ก็จะขึ้น error หรือ warining โต้งๆ ออกมา จริงๆ เราก็ดักจับ และจัดการได้เองนั่นแหล่ะ แต่ถ้าใช้ Zend_Loader แล้วเขาจัดการไว้ให้หมดแล้ว โดยที่ error ต่างๆ ดังกล่าว จะถูกโยนตกไปที่ Exception เราก็ไปจัดการ exception ของเราต่อเอง (ซึ่งในตัวอย่างนี้ไม่ได้จัดการอะไร นอกจากจะ print ออกมา ให้เห็น)
Zend_Loader::loadClass('Zend_Controller_Front'); 
Zend_Loader::loadClass('Zend_Config_Ini'); 
Zend_Loader::loadClass('Zend_Registry'); 
Zend_Loader::loadClass('Zend_Db'); 
Zend_Loader::loadClass('Zend_Db_Table');
ทำ การโหลด library ที่จำเป็นสำหรับการทำงานของเรา โดยในตอนที่โหลดนั้น Zend_Loader จะแปลง under scholl (_) เป็น / (ตัว path separator หรือตัวแบ่งแยกใน path) และจะต่อท้าย ให้ด้วย .php เช่น
Zend_Loader::loadClass('Zend_Controller_Front')
จะเป็นการโหลด Zend/Controller/Front.php มาใช้งาน ชื่อต่างๆ จะเป็น case sensitive คือต้องเขียนตัวใหญ่ ตัวเล็กให้ถูกต้อง ตัวใหญ่กับตัวเล็กค่าไม่เหมือนกัน
ในตัวอย่างนี้ Zend_Loader จะไปหาไฟล์ Zend/Controller/Front.php ที่อยู่ใน include_path ซึ่งเราก็ได้ใส่ library ของเราไว้แล้ว นั่นเอง ซึ่งถ้าโครงร้างของ library หรือ class ต่างๆ ของเรา ที่เขียนขึ้นมาเอง หรือ ดาวน์โหลดมาใช้งาน เราก็สามารถใช้ Zend_Loader โหลดมาก็ได้น้ะครับ แต่โครงสร้าง ต้องเป็นประมาณนี้น้ะครับ เช่น
Zend_Loader::loadClass('Smarty_Smarty.class')
จะเป็นการโหลด Smarty/Smarty.class.php มา โอเค มาว่าต่อกันเลย
// load configuration 
$config = new Zend_Config_Ini('./application/config.ini', 'general');
ทำ การโหลด configuration จาก config file: application/config.ini ที่เราสร้างเอาไว้แล้ว ในที่นี้ config file ของเราเป็น ini file ก็เลยใช้ Zend_Config_Ini แต่ Zend ยังรองรับ config ในรูปแบบ xml เอาไว้ด้วย โดยที่ถ้า config file ของเราเป็น xml file เราก็ใช้ Zend_Config_Xml ในการโหลดมา
$registry = Zend_Registry::getInstance();
$registry->set('config', $config);
เราจะไม่ใช้ global parameter แล้ว แต่จะให้ Zend_Registry จัดการให้เรา โดยเราสามารถใช้
$registry->set() และ $registry->get() แทนได้

// setup database 
$db = Zend_Db::factory($config->db->adapter, $config->db->config->asArray()); 
Zend_Db_Table::setDefaultAdapter($db);

เซต up การใช้งาน database โดยที่ ในตัวอย่างนี้ (ดูใน config.ini) เราใช้ PDO_MYSQL 
เป็น db adapter ดังนั้น คุณต้อง เปิด หรือเรียกใช้งาน extension=php_pdo_mysql.dll 
(สำหรับ windows) เอาไว้ด้วย
// setup controller $frontController = Zend_Controller_Front::getInstance(); $frontController->throwExceptions(true); $frontController->setControllerDirectory('./application/controllers');
 สร้าง Zend_Controller_Front สำหรับจัดการ map Request ที่เข้ามา ให้ตรงกับ php function แล้วให้แสดงผลออกมาให้ถูกต้อง ต่อจากนั้น เซตให้ frontController ของเรา ให้ throw exception ทั้งหมดออกมา เพื่อให้เห็นเวลาเกิด error ต่างๆ ซึ่งถ้าตอนใช้งานจริงแล้ว เราก็ไม่ควรที่จะแสดงให้ user เห็น
และเราสามารถเซต Controller Directory ได้ ว่าเราเก็บ controller ไว้ที่ไหน ในที่นี้เราเซตให้ไปที่ controller directory ที่เราเตรียมไว้คือ application/controllers นั่นเอง
 $frontController->dispatch();
และสุดท้ายของไฟล์ index.php ก็คือเรียกรัน application ของเรา

Controllers

มาสร้าง Controllers กันต่อเลยครับ สำหรับ controllers นั้น เราจะสร้าง Class ที่ extend จาก Zend_Controller_Action โดยให้ตั้งชื่อขึ้นต้นด้วยชื่อ controller ของคุณแล้วตามด้วย Controller เช่นสำหรับ index controller ต้องตั้งชื่อว่า IndexController ชื่อขึ้นต้นด้วยตัวใหญ่เสมอน้ะครับ และชื่อจะเป็น case sensitive และเก็บ controller class ของเราไว้ที่ application/controllers ตามที่เราได้เคยเซตไว้แล้วนั่นเอง
class Zend_Controller_Action จะตระเตรียม method ต่างๆ ไว้สำหรับการทำงานของ controller action ไว้ ชื่อของ medthod จะขึ้นต้นด้วยตัวเล็ก และลงท้ายด้วย Action และเป็น case sensitive เช่น indexAction()
แต่จะมี method พิเศษชื่อ init() ซึ่งจะเป็น method แรกที่จะถูกเรียกให้ทำงาน ตอน controller เริ่มทำงานซึ่งปกติ เราจะเอาไว้สำหรับโหลดค่าเริ่มต้นต่างๆ ที่จำเป็น เช่นในตัวอย่าง เราใช้โหลด Model Album เพราะว่าทุก action จำเป็นต้องใช้อยู่แล้ว เราก็สั่งโหลดใน init() ที่เดียวก็พอ
ถ้ากลับไปดู IndexController ของเรา จะพบว่า ได้เตรียม action ไว้ต่างๆ ดังนี้
class IndexController extends Zend_Controller_Action 
{     
 function init()
 {
  ...
  Zend_Loader::loadClass('Album');
 }
 function indexAction()
 {
  $this->view->title = "My Albums";
  ...
 } function addAction()
 {  $this->view->title = "Add New Album";
  ...
 } function editAction() 
 {  $this->view->title = "Edit Album";
  ...
 } function deleteAction()
 {  $this->view->title = "Delete Album";
  ...
 }}
จะเห็นว่าเราได้สร้าง IndexController และมี action ดังนี้
  • init() สำหรับเป็น action ที่ทำงานตอนเริ่มต้น
  • indexAction() เป็น index action หรือ default action จะถูกเรียกใช้งานเมื่อเรียก /zf-tutorial/ หรือ /zf-tutorial/index
  • addAction() เตรียมไว้สำหรับการเพิ่มข้อมูล จะถูกเรียกทำงานเมื่อเรียก /zf-tutorial/index/add หรือ /zf-tutorial/add
  • editAction() เตรียมไว้สำหรับแก้ไขข้อมูล จะถูกเรียกใช้งานเมื่อเรียก /zf-tutorial/edit/id/1 หรือ /zf-tutorial/index/edit/id/1 (id/1 จะถูกส่งเป็น GET params เหมือนกับ id=1) และสุดท้าย
  • deleteAction() เตรียมไว้สำหรับลบข้อมูล จะถูกเรียกใช้งานเมื่อ /zf-tutorial/delete/id/1

Models

สำหรับ model ในตัวอย่างนี้ model Album ของเรา ทำงานไม่ได้ซับซ้อนอะไร ก็เลยแค่ extend จาก Zend_Db_Table เราก็สามารถใช้งาน model ในการ insert/update/delete ได้แล้ว รายละเอียดของ Zend_Db_Table จะไม่ขอกล่าวถึงในที่นี้น้ะครับ
ดู code ของ model Album ของเรา
<?php
class
Album extends Zend_Db_Table
{
    protected $_name = 'album';
}
?>
เขียน code แค่นี้เองครับ นี่แหล่ะข้อดีของการใช้ Framework เพราะว่า หลายๆ อย่างเราไม่ต้องเขียน code เองเลย เขาเตรียมไว้ให้หมดแล้ว นอกจากว่า งานของเรามันจะพิศดารจริงๆ แต่ชื่อ class ต้องขึ้นต้นด้วยตัวใหญ่ และบันทึกในไฟล์ขึ้นต้นด้วยตัวใหญ่ และเป็น case sensitive เหมือนเดิม
 บางที เวลาเกิด error หรือ exception ต่างๆขึ้น แล้วเห็นน้องเปิด และไล่ดู code ของ Framework ถ้าการไล่ code นั้นเพื่อเป็นการศึกษา ก็ดีไปครับ แต่ถ้าไล่เพื่อจะเข้าไปแก้ ขอห้าม! ไว้ เลยน้ะครับ เราไม่ควรไปแก้ไขอะไรใน code ของ Framework ไม่ว่าจะเป็นของค่ายไหนๆ ก็ตาม ถ้าเราจะยังใช้ Framework ของเขาอยู่ เพราะว่า มันจะยากลำบากเวลาจะ upgrade version น้ะครับ ถ้าจะมีการแก้ไขอะไร หรือไม่พอใน code ส่วนไหน ให้ทำในลักษณะการ extend และ overide มาจะดีกว่าครับ

Views

สำหรับส่วนของ views นั้น เราก็สร้างไฟล์ดังนี้น้ะครับ
  • เก็บไว้ใน /application/views/scripts/{controllerName}/{actionName} โดยที่ {controllerName} เป็นชื่อ controller และ {actionName} เป็นชื่อของ action และนามสกุล .phtml (ต้อง .phtml เท่านั้นน้ะครับ Zend Framework จะมาโหลดเอานามสกุลนี้ไปทำงาน) เช่น ถ้าเราจะสร้าง view สำหรับ controller IndexController และ indexAction ให้สร้าง view ชื่อ index.phtml เก็บไว้ที่ /application/views/scripts/index/index.phtml หรือ สร้าง view สำหรับ addAction() ใน IndexController เราก็ต้องสร้างไฟล์ add.phtml เก็บไว้ที่ /application/views/scripts/index/add.phtml
  • เนื่องจาก view เป็นส่วนการแสดงผล code ใน view นี้ก็จะมี code html, xhtml หรือ tag อื่นๆ ตามแต่ output platform ของเรา และเราสามารถใส่ code php ใน view นี้ได้เลย
  • สำหรับการวาง structure ของ view ของเรา เช่น ถ้ามีการแยกส่วน header.phtml และ footer.phtml ออกมา เอาไว้ใช้งานร่วมกันในทุกๆ action ปกติเราจะ include ใช่ไหมครับ แต่ใน view แล้วเราใช้ method render() แทน (render - แปลว่า แสดง (play, act, perform) หรือ แปล (translate) ) โดยเราเรียก method นี้โดย เรียก $this->render("header.phtml") ได้เลย
ตัวอย่างของ view
ตัวอย่าง view ง่ายๆ น้ะครับ สมมติเป็น view ของ IndexController::indexAction() และเก็บไว้ที่ /application/views/scripts/index/index.phtml
<?php echo $this->render('header.phtml'); ?> 
<h1><?php echo $this->escape($this->title); ?></h1> 
<?php echo $this->render('footer.phtml'); ?>  
เห็น มั้ยครับ ว่าการใช้งาน Framework (ไม่เฉพาะ Zend Framework น้ะครับ Framework อื่นๆด้วย) เขาจะมีรูปแบบ มีโครงสร้าง มี class มี library ต่างๆ ไว้ให้เยอะมากๆ เราแทบจะไม่ต้องเขียน code อะไรเลย นอกจากส่วนของ business logic ของเราเอง จะทำให้เราสามารถพัฒนา application ได้อย่างรวดเร็ว
แต่ถ้าพูดถึงประสิทธิภาพการทำงาน (ความเร็ว) ของ application คงจะต้องยอมรับว่า การพัฒนา application จาก framework ต่างๆ นั้นต้องด้อยไปกว่าการเขียน code แบบตรงไปตรงมาน้ะครับ เพราะว่า framework นั้นจะเกิด overhead ตามจุดต่างๆ อยู่ไม่น้อยเหมือนกัน แต่เมื่อเอามาเทียบกับระยะเวลาที่ลดลง ในการพัฒนา และลด bug ต่างๆ ที่อาจจะเกิดขึ้นในการเขียน code ของเราเอง ผมคุ้มน้ะครับ และอย่างหนึ่ง ประสบการณ์การเขียนโปรแกรมของผมมา 6-7 ปี ผมไม่เคยเจอ web application ตายเพราะทำงานไม่ทันอันเนื่องมาจากการเขียน code (ไม่นับ การวน loop ไม่รู้จบ ของโปรแกรมเมอร์ละอ่อน บางทั่นน้ะคับ) เคยเห็นแต่ระบบตาย อันเนื่องมาจาก database ที่ query นาน หรือ db connection เต็ม (ผมไม่เคยทำงานกับระบบอะไรใหญ่ๆ ด้วยหล่ะมั้ง ใหญ่สุดที่เคยทำงาน ก็ประมาณเกือบ 1 ล้าน transactions ต่อวัน - เก็บ page view information ด้วย ก็เลยเยอะขนาดนี้)...
ข้อควรพิจารณาอยากหนึ่ง ของการใช้ framework ต่างๆ การเข้าไปไล่ดู code ของ framework ต่างๆ นั้นเพื่อจุดประสงค์ในการศึกษา ว่าชาวบ้านที่เขาเขียน code ให้ชาวโลกเขาใช้งานนั้น เขาเขียนกันยังงัย นั้น เป็นสิ่งที่ดี และน่าชื่นชมน้ะครับ แต่อย่าไปแก้ไข code ใดๆ น้ะครับ เพราะเดี๋ยวจะมีปัญหาในการ upgrade version ถ้าจะแก้จริงๆ ก็ควรจะ extend หรือ overide มาเป็น class ของเราต่างหาก จะดีกว่านะครับ
zf-tutorial.rar11.7 KB

ไม่มีความคิดเห็น:

แสดงความคิดเห็น