Phalcon的MVC框架解析

追憶丶年華發表於2018-01-16

1. mvc/simple
從最簡單的入手吧. 把一些能及時說明白的東西寫在註釋裡了,需要擴充套件的知識列在下面。

public/index.php

<?php
$loader = new \Phalcon\Loader(); //反斜槓看著不習慣?請參見下面的[ 名字空間 ]
// 註冊自動載入的類目錄,為方便使用,在php解析程式啟動時直接載入,參見[ 類的預載入 ]
$loader->registerDirs(array('../apps/controllers/', '../apps/models/'));
// Loader也要註冊自身
$loader->register();
// 依賴注入器,參見[ 依賴注入 ],在深入到概念中之前,可以理解為構造類物件的地方
$di = new \Phalcon\DI();
// 下面是一些必需的物件
// 路由器: 將URI地址解析成Controller\Action\Params的模組
$di->set('router', 'Phalcon\Mvc\Router');
// 分發器: 利用Router解析得到的資訊找到函式並執行
$di->set('dispatcher', 'Phalcon\Mvc\Dispatcher');
// 響應模組: 將執行結果封裝成http協議,併傳送給瀏覽器
$di->set('response', 'Phalcon\Http\Response');
// 請求模組: 將到來的http請求封裝成物件,在dispatcher發給controller時也會把物件帶過去便於訪問
$di->set('request', 'Phalcon\Http\Request');
// 檢視: 注入檢視物件,當dispatcher呼叫時被訪問
$di->set('view', function(){
  $view = new \Phalcon\Mvc\View();
  $view->setViewsDir('../apps/views/');
    return $view;
  });
// 資料庫物件
$di->set('db', function(){
  return new \Phalcon\Db\Adapter\Pdo\Mysql(array(
    "host" => "localhost",
    "username" => "root",
    "password" => "",
    "dbname" => "invo"
  ));
});
// 資料表的後設資料 : Memory表示該物件用於臨時獲取表結構,物件析構後表結構資料清除
$di->set('modelsMetadata', 'Phalcon\Mvc\Model\Metadata\Memory');
// 管理著Model的初始化、對應關係的類
$di->set('modelsManager', 'Phalcon\Mvc\Model\Manager');
try {
  //太簡單了,一個Application類搞定主邏輯 [ 應用類 ]
  $application = new \Phalcon\Mvc\Application();
  $application->setDI($di);
  echo $application->handle()->getContent();
}
catch(Phalcon\Exception $e){
  echo $e->getMessage();
}

註解:

名字空間:
php在5.3.0版本以後開始支援的特性,用於解決兩類問題 —— 命名衝突和給類重新命名. 使用名稱空間:別名/匯入

類的預載入:
為了在使用一些類時不用寫require或include,可以在php啟動時就把一些目錄下的類載入進來

依賴注入:
依賴注入是控制反轉模式的一種實現。完全得解釋依賴注入很難做到,因為這個模式被不同語言,不同框架用到,實現也各不相同。針對於Phalcon,可以看得出依賴注入(DI)模組是個容器,將類的生成方法寫到DI容器中, 當有用到依賴時去DI中取出即可。具體DI的作用官方文件寫得太好了官方文件

應用類:
封裝了一些常用注入的類。當有它在時你可能不知道它做了些什麼,除非看一下不用它我們需要做什麼。見下面的例子simple-without-application

controllers/ProductsController.php

<?php
// 控制器是用來執行業務邏輯的模組,每個方法叫做一個動作
// URL美化後一般是這樣路由的:www.demo.com/controllername/actionname/params
class ProductsController extends \Phalcon\Mvc\Controller {
// action執行的結果使用view渲染,傳遞變數給對應的view,對應規則靠路徑
  public function indexAction(){
    $this->view->setVar('product', Products::findFirst());
  }
}

官方文件-使用controller

models/Products.php

<?php
// Phalcon的Model類是用來與資料庫打交道的,大多數情況下,一個model對應一張表
// source表示表名,setSource設定,getSource獲取
// 預設表名是to_lower(ModelClassName)
class Products extends \Phalcon\Mvc\Model {
// 與controller一樣,不建議用__construct,而是initialize
  public function initialize(){
    $this->setSource('products');
  }
}

2. mvc/simple-without-application
只看public/index.php就可以了,其他檔案雷同

// 路由器執行handle()
$router = $di->getShared('router');
$router->handle();

// 排程器傳入路由器解析得到的controller和action以及引數
$dispatcher = $di->getShared('dispatcher');
$dispatcher->setControllerName($router->getControllerName());
$dispatcher->setActionName($router->getActionName());
$dispatcher->setParams($router->getParams());

// 排程:呼叫對應的controller::action函式
$dispatcher->dispatch();

// 渲染檢視
$view = $di->getShared('view');
$view->start();
$view->render($dispatcher->getControllerName(), $dispatcher->getActionName(), $dispatcher->getParams());
$view->finish();

// 封裝返回報文併傳送給客戶端
$response = $di->getShared('response');
$response->setContent($view->getContent());
$response->sendHeaders();
echo $response->getContent();

這些就是Application的工作,還是有Application省事啊!

3. mvc/single與mvc/single-namespaces
這是兩個對比case,意在闡明自定義namespace的用法。從例2中我們看到application負責路由排程和渲染,但是不負責DI注入。在single的例子中,自定義了application,把依賴注入的程式碼封裝進去了,使得主邏輯只剩下非常少的程式碼

class Application extends \Phalcon\Mvc\Application {
  protected function _registerAutoloaders() {
    $loader = new \Phalcon\Loader();
    $loader->registerDirs(array(
      '../apps/controllers/',
      '../apps/models/'
    ));
    $loader->register();
  }
  protected function _registerServices() {
    $di = new \Phalcon\DI();
    $di->set('router',...);
    $di->set('dispatcher',...);
    $di->set('response',...);
    $di->set('request',...);
    $di->set('view',...);
    $di->set('db',...);
    $di->set('modelsMetadata',..);
    $di->set('modelsManager',...);
    $this->setDI($di);
  }
  public function main() {
    $this->_registerServices();
    $this->_registerAutoloaders();
    echo $this->handle()->getContent();
  }
}

// 主邏輯
try {
  $application = new Application();
  $application->main();
}
catch(Phalcon\Exception $e){
  echo $e->getMessage();
}

上述程式碼註冊controller和model的class,用的方式是registerDirs。
single-namespaces也同樣自動載入自定義的controller和model,用的方式是registerNamespaces。

$loader->registerNamespaces(array(
  'Single\Controllers' => '../apps/controllers/',
  'Single\Models' => '../apps/models/'
));

上述程式碼註冊controller和model的class,用的方式是registerDirs。
single-namespaces也同樣自動載入自定義的controller和model,用的方式是registerNamespaces。

$loader->registerNamespaces(array(
'Single\Controllers' => '../apps/controllers/',
'Single\Models' => '../apps/models/'
));
因為引入了名稱空間,使用時也需要顯示宣告

namespace Single\Controllers;
use Single\Models\Products as Products;

namespace Single\Models;
class Products extends \Phalcon\Mvc\Model {
...
}
4. mvc/single-factory-default
引入了配置項。這裡用到了Include()的返回值,還用到了PHP 5.3以後才支援的閉包語法: function () use($config) {...}
參見 :閉包語法詳解

相關文章