將 PHP 網頁快速套入 MVC 架構的作法

先前已提過將 PHP 網頁快速套入 MVC 架構的作法,最近在接手新的工作後,為了能儘快享用 MVC 架構的好處,又不用花太多的功夫,摸索出更快的作法。

為了將原來 Page-Based PHP 應用程式移轉到 MVC 架構,若要按 MVC 的精神重新整理原來的 PHP 程式,要花非常多的時間和精力,而且在變動的過程中,必須針對更動的部分進行測試,才能確保符合原來的功能。在移轉的過程中,也許又有需求要更改功能,就必須同時修改新舊的程式,要花費雙倍的功夫。因此,若能將程式儘快的套到 MVC 架構,就能儘快享用使 MVC 架構開發系統的好處,例如慢慢調整程式,讓程式更符合 MVC 架構的精神。當然,人是有墮性的,通常套完就放著不動了,等到有新的需求,才會以 MVC 的作法,修改和調整對應的部分。

這裡提出的作法,必須用到伺服器的 rewrite 功能和 MVC 的 route 功能。

下面是 Laravel 建議的 rewrite rule,用來將 index.php 藏起來。

# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Handle Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

上面的規則,假如不是目錄的話,將後面的 '/' 去掉。下面的規則,假如不存存取的目錄和檔名,就教給 index.php。

然後,在 app/Http/routes.php 新增規則,例如

Route::any('/info/{php_src?}', function() {
    return View::make('info.index')->render();
})->where('php_src', 'index.php');
Route::any('/user/login.php', function() {
    return View::make('user.login')->render();;
});

上述規則的第一條,URL http://my.io/infohttp://my.io/info/index.php 會對應到 view 'resources/views/info/index.php'。第二條規則,是將 http://my.io/user/login.php 對應到 view 'resources/views/user/login.php'。

依此作法,可以透過瀏覽器存取原來應用系統中對應的 PHP,但實際上,這些程式都變成 MVC 架構中的 view 了。從上面可以看到,假如我們把 asp 的程式改成 PHP,還是可以透過 route 的設定保留 asp 的副檔名。

因此,我們只要把原來的 PHP 程式,通通搬到 views 的目錄下,再建立對應的 route rule,就算移轉完成了。原來的 PHP 程式中使用的類似 include "abc.php"; 的作法,全部都可以照用,只要稍微調整一下目錄就可以。

注意,在上面的 route 設定裡,沒有用比較簡單的 return view('info.index'),因為使用 view() 的 helper function,在程式出錯時,有可能只會丟出 "Method Illuminate\View\View::__toString() must not throw an exception" 的訊息,無法知道真正出錯的地方。

若有幾十個 route 要設,也是蠻麻煩的,可以建個對應表 config/uri_mapping.php

return [
    /*
       目錄,會讀取預設的 index.php
    */
    'dirs' => [
            '/info',
            '/news',
        ],
    /*
       一對一對映的程式
    */
    'prgs' => [
            '/usr/login.php',
            '/news/show.php',
        ],
];

然後,在 routes.php,用迴圈來建立 route rule

/*
   對應原來的目錄,會讀取預設的 index.php
*/
foreach (config('uri_mapping.dirs') as $dd) {
    Route::any($dd.'/{php_src?}', function() use ($dd) {
        return View::make($dd.'.index')->render();
    })->where('php_src', 'index.php');
};
/*
   一對一對映原來的 PHP 程式
*/
foreach (config('uri_mapping.prgs') as $ff) {
    Route::any($ff, function() use ($ff)
    {
        if (!(strrpos($ff, '/') > 0)) {
            $ff = '/home'.$ff;
        }
        $vv = substr($ff, 1, -4);
        return View::make($vv)->render();
    });
}

其中,為了看起來更乾淨點,將原來放在根目錄的 PHP 程式,集中放在 home 的目錄下。

沒有用過 Laravel 的人,或許不明白為何要多此一舉。我大約在 5, 6 年前開始接觸 CodeIgniter,後來改用 Laravel,到了現在,面對原始的 PHP 程式,會有一種不知所措的感覺,不知道要怎麼寫程式。所以,只要是我接的系統,都一定要塞到 MVC 架構下,我才有辦法做下去。假如是 asp 的程式,就先轉成 PHP,再塞進 MVC 架構,假如原來就是 PHP 程式,那就直接塞啦。

嗯,假如您也是對 Laravel 依賴成性的人,希望這裡說的作法,有助於你面對舊的 PHP 程式的維護。