Phalcon Multi Module

featureimage

มีความจำเป็นต้องใช้ Phalcon แบบ Multi Module มาดูกันว่าทำยังไง

ความต้องการคือ จะทำระบบที่มีการตั้งค่าข้อมูลพื้นฐานหลายอย่าง และอยากให้ทุกอย่างไปรวมกันอยู่ที่ Module ชื่อ Settings ซึ่งในนี้ก็จะมี Controller ที่ทำหน้าที่ CRUD อยู่หลายๆตัว

ก่อนอื่นก็ออกแบบ File Structure ที่อยากได้ก่อน คิดไปคิดมา ได้ออกมาหน้าตาแบบนี้

apps/  
    cache
    config
    controller
    library
    messages
    models
    modules
        settings/
            controllers/
            models/
            views/
            Module.php
        student/
            controllers/
            models/
            views/
            Module.php
    views
public/  
    css/
    files/
    img/
    js/
    temp/

จากด้านบน เราออกแบบ Module ไว้ 2 ตัวคือ settings กับ student โดยที่แต่ละ Module ก็จะมี mvc เป็นของตัวเอง

ความรู้ที่จะช่วยชีวิตเราในการทำ Multi Module คือเรื่อง Namespace กับ Router เราจะใช้ Namespace ในการกำหนด Scope ของ Class ใน Module (พิมพ์ไทยคำ อังกฤษคำ ยากชิบ)

จุดสังเกตของการทำ Multi Module ใน Phalcon คือ ที่แต่ละ Module จะมีไฟล์ชื่อ Module.php ทำหน้าที่ ระบุที่อยู่ของ Model, Controller เพื่อ registerNamespaces และ registerServices

ตัวอย่างของไฟล์ Module.php

<?php

namespace Modules\Settings;

use Phalcon\Loader,  
    Phalcon\Mvc\Dispatcher,
    Phalcon\Mvc\View,
    Phalcon\Mvc\ModuleDefinitionInterface;

class Module implements ModuleDefinitionInterface  
{

    /**
     * Register a specific autoloader for the module
     */
    public function registerAutoloaders()
    {

        $loader = new Loader();

        $loader->registerNamespaces(
            array(
                'Modules\Settings\Controllers' => __DIR__ . '/controllers/',
                'Modules\Settings\Models'      => __DIR__ . '/models/',
            )
        );

        $loader->register();
    }

    /**
     * Register specific services for the module
     */
    public function registerServices($di)
    {

        //Registering a dispatcher
        $di->set('dispatcher', function () {
            $dispatcher = new \Phalcon\Mvc\Dispatcher();

            //Attach a event listener to the dispatcher
            $eventManager = new \Phalcon\Events\Manager();
            $eventManager->attach('dispatch', new \Acl('settings'));

            #$dispatcher->setEventsManager($eventManager);
            $dispatcher->setDefaultNamespace("Modules\Settings\Controllers\\");
            return $dispatcher;
        });

        //Registering the view component
        $di->set('view', function() {
            $view = new View();
            $view->setViewsDir(__DIR__ . '/views/');
            return $view;
        });
    }

}

จะเห็นว่ามีการระบุ Namespace ตามชื่อ Module ว่า namespace Modules\Settings; ช่วยทำให้ Class ในแต่ละ Module ไม่ชนกัน

อีกจุดนึงที่ต้องแก้ใขคือ Router เพราะเราไม่ได้ใช้การ Route แบบปกติที่มากับ Phalcon แล้ว เข้าไปแก้ใขไฟล์ app/config/routes.php ตามนี้

$router->add('/settings', array(
    'module' => "settings",
    'controller' => "index",
    'action' => "index"
))->setName('setting-index');

$router->add('/settings/', array(
    'module' => "settings",
    'controller' => "index",
    'action' => "index"
))->setName('setting-index');

$router->add('/settings/:controller', array(
    'module' => "settings",
    'controller' => 1,
    'action' => "index"
))->setName('settings-controller');

$router->add('/settings/:controller/:action/:params', array(
    'module' => "settings",
    'controller' => 1,
    'action' => 2,
    'params' => 3
))->setName('settings-full');

ด้านบนคือการระบุ Router ให้การเรียก /settings ที่ URL เป็นการไปเรียกใช้งาน Module, Controller ตามที่เราระบุ พร้อมทั้ง setName เพื่อประโยชน์ในการ echo link ใน View

Models

เราต้องระบุ Namespace ให้ Models ที่อยู่ภายใต้ Module ด้วยเช่น

<?php

namespace Modules\Settings\Models;

class personType extends \Phalcon\Mvc\Model  
{
    ...

Controller

Controller ก็เช่นกัน

<?php

namespace Modules\Settings\Controllers;

use Phalcon\Mvc\Model\Criteria,  
    Phalcon\Paginator\Adapter\Model as Paginator,
    Phalcon\Translate\Adapter\NativeArray,
    Phalcon\Tag,
    Phalcon\Mvc\Url,
    Phalcon\Paginator\Adapter\Model;


use Modules\Settings\Models\Persontype,  
    Modules\Settings\Controllers;

class PersontypeController extends ControllerBase  
{
    ...

สังเกตว่า เราพยายามบอก Controller นี้ว่าให้ใช้ Controller, Models จาก ภายใน Modules\Settings ผ่านการระบุว่าจะใช้ Namespace จากที่ไหน

use Modules\Settings\Models\Persontype,  
    Modules\Settings\Controllers;

Bootstrap

ที่ไฟล์ bootstrap app/public/index.php ก็จะต้อง registerModules เข้าไปใน Application ด้วย ตามนี้

...

$application = new \Phalcon\Mvc\Application($di);

 // Register the installed modules
$application->registerModules(
    array(
        'settings'  => array(
            'className' => 'Modules\Settings\Module',
            'path'      => __DIR__ . '/../app/modules/settings/Module.php',
        ),
    )
);

...

View

สิ่งที่เกี่ยวข้องกับ View ที่อยู่ภายใต้ Module ก็จะมีเพิ่มมานิดหน่อย จากด้านบนที่เราทำ Router และตั้งชื่อ Router ไว้ จะได้เอามาใช้ประโยชน์ตอนนี้แหล่ะ

<div align="left" style="margin-top:20px;">  
    <?php echo $this->tag->linkTo(
        array(
            array('for' => 'settings-full', 
                'controller' => 'persontype', 
                'action' => 'new'), 
            '<i class="icon-plus"></i> '. $t->_("persontype"), 
            'class' => 'btn btn-primary', 
            'rel' => 'tooltip', 
            'title' => $mt->_("add data")
        )) ?>
</div>  

ด้านบนคือการสร้างปุ่ม ที่มี output เป็น HTML ตามนี้

<div align="left" style="margin-top:20px;">  
    <a href="/phoenix/settings/persontype/new/" class="btn btn-primary" rel="tooltip" data-original-title="Add Data"><i class="icon-plus"></i> Person Type</a>
</div>  

สังเกตที่ paramiter ตัวแรกของ linkTo เราส่ง Array ที่ระบุชื่อ Router เข้าไป พร้อมทั้งบอกชื่อ controller และ action

array('for' => 'settings-full',  
    'controller' => 'persontype', 
    'action' => 'new')

ส่วนนี้จะเป็นจุดที่จะบอกให้ linkTo รู้ว่า เราจะสร้าง link ที่อ้างอิงถึง Module ที่เรากำหนดไว้ใน Router

ด้วยความยืดหยุ่นของ Phalcon และ Overhead ที่น้อยมาก เราก็สามารถออกแบบ MVC Application ให้มีโครงสร้างตามใจเราได้สบายๆ แล้วชีวิตของการเขียนโปรแกรมก็จะมีความสุขมากขึ้น :)