phalcon簡易指南

幫助你開始使用 phalcon 的簡易指南。php

簡介

Phalcon 2將於2015年4月17日發佈,這個版本大約85%的代碼是基於 Zephir 語言重寫的。Zephir是開源的,使用相似PHP語法的語言,生成C語言代碼,並編譯成PHP擴展。這提升了PHP擴展的開發效率,並下降了框架的後期維護成本。html

phalcon-devtools

安裝 https://github.com/phalcon/phalcon-devtools 以後能夠幫助自動生成目錄結構和代碼。同時把ide目錄下的相應版本加入IDE的External libraries以後,能夠幫助IDE自動完成代碼。node

nginx配置

配置nginx的時候,建議用$_SERVER[‘REQUEST_URI’]方式,這樣能夠防止自動加入$_GET[‘_url’]的隱規則,在參數簽名時,若是你忘記這個隱規則會導至簽名驗證失敗。mysql

參考配置:nginx

server {
    listen       80;
    server_name  www.example.com;
    index index.html index.htm index.php;

    root $root_path/example/public;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
            try_files $uri =404;
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            include fastcgi_params;
    }
}

這樣設置後,代碼也要參考URI Sources調整git

$router->setUriSource(Router::URI_SOURCE_SERVER_REQUEST_URI);

代碼結構

phalcon框架對代碼結構並無約定,你能夠按本身的實際須要自行組織代碼結構,這裏有一個供參考的例子:https://github.com/phalcon/mvcgithub

實際項目中,代碼分層至少要分三層,controller/view -> services -> modelssql

services和models爲了重用,能夠參考composer的組織方式,放到vendor。數據庫

使用PSR-4規範的namespace自動加載。api

處理404

404要處理3個場景

  1. 沒有匹配route

  2. dispatch時沒找到controller文件

  3. dispatch時沒找到action方法

當沒有匹配route時,會使用用默認的namespace,默認的module,默認的controller,默認的action,默認的params。此時你或許會困惑爲什麼一個請求會顯示默認首頁。

參考not found paths,配置route

// Not Found Paths
$router->notFound([
    'controller' => 'errors',
    'action' => 'route404'
]);

參考handling not found exceptions在注入dispatcher服務時初始化代碼以下

$di->setShared('dispatcher', function () {

    $eventsManager = new EventsManager();
    $eventsManager->attach("dispatch:beforeException", function ($event, $dispatcher, $exception) {
        if ($exception instanceof DispatcherException) {
            switch ($exception->getCode()) {
                case Dispatcher::EXCEPTION_HANDLER_NOT_FOUND:
                case Dispatcher::EXCEPTION_ACTION_NOT_FOUND:
                    $dispatcher->forward([
                        'controller' => 'errors',
                        'action'     => 'show404',
                        'params'     => array('message' => $exception->getMessage())
                    ]);
                    return false;
            }
        }

        $dispatcher->forward([
            'controller' => 'errors',
            'action'     => 'show500'
        ]);
        return false;
    });

    $dispatcher = new MvcDispatcher();
    $dispatcher->setDefaultNamespace('\namespace\Controllers');
    $dispatcher->setEventsManager($eventsManager);
    return $dispatcher;
});

以代碼因爲還攔截了其它異常,且沒有throw $exception,會中斷異常調用鏈,能夠考慮把異常$exception->getMessage(), $exception->getFile(),$exception->getLine(),$exception->getCode()信息傳給view,判斷是開發模式就顯示,生產模式就不顯示。

使用原生 SQL

原則上是不容許使用原生的SQL的,某些場景必定要用的狀況下,建議把全部的sql放到config內,在service層讀取使用。

第一種方式

參考Finding Rows

$sql = "SELECT id, name FROM robots ORDER BY name";

$connection = \Phalcon\DI::getDefault()->get('db');

$result = $connection->query($sql);

$result->setFetchMode(Db::FETCH_ASSOC);

echo $connection->getSQLStatement(); // sql

$data = $result->fetchAll();

print_r($data);

第二種方式

參考Using Raw SQL

// A raw SQL statement
$sql = "SELECT * FROM robots WHERE id > 0";

$robot = new Robots();

$result = $robot->getReadConnection()->query($sql);

$result->setFetchMode(Db::FETCH_ASSOC);

$data = $result->fetchAll();

print_r($data);
echo $robot->getReadConnection()->getSQLStatement();

使用PHQL

參考Phalcon Query Language (PHQL)

簡單粗暴的理解是把models下的namespaceclass對應成數據庫的表,類屬性對應表字段。

理解了PHQL以後就能夠讀下Working with Models

理解事件驅動

model類的事件調用順序以下:參考官方文檔http://docs.phalconphp.com/en/latest/reference/models.html#events-and-events-manager

數據庫分庫,讀寫分離,負載均衡

用命名空間區分不一樣的數據庫實例,對應代碼結構上是不一樣的目錄區分,在同一目錄下基類負責初始化鏈接。鏈接來自初始化時注入的多個db服務

隱規則:

  • initialize()在每一個請求期間只會調用一次

  • 爲每一個 new 建立的實例執行初始化任務使用onConstruct()

namespace Company\Models\Notification;

/**
 * Class BaseModel
 *
 * beforeSave()和afterFetch()成對使用,用於讀寫數據時自動轉化數據。
 * 例如自動執行serialize unSerialize
 *
 * save()發生時事件調用順序是
 * initialize,
 * onConstruct,
 * beforeValidation,
 * beforeValidationOnCreate,
 * afterValidationOnCreate,
 * afterValidation,
 * beforeSave,
 * beforeCreate,
 * afterCreate,
 * afterSave,
 *
 * @package Company\Models\Notification
 */
class BaseModel extends \Phalcon\Mvc\Model
{

    /**
     * - initialize()在每一個請求期間只會調用一次
     * - 子類必需調用父類方法
     * - 爲每一個 new 建立的實例執行初始化任務使用onConstruct()
     */
    public function initialize()
    {
        $this->setConnectionService('db_notification');
        
        //$this->setConnectionService('node1');
        //$this->setConnectionService('node2');
        //
        //真實場景可能使用mysqlnd_ms擴展或者haproxy
        //僅演示讀負載均衡一種思路
        //$dbSlave = ['node1', 'node2', 'node3'];
        //$key = array_rand($dbSlave);
        //$db = $dbSlave[$key];
        //$this->setReadConnectionService($db);
        //
        //
        //$this->setReadConnectionService('dbRead');
        //$this->setWriteConnectionService('dbWrite');
    }
}

表前綴與分表

model中提供的getSource()方法,合理運用便可。

class BaseModel extends \Phalcon\Mvc\Model {
    public function getSource()
    {
        return 'v1'.str_tolower(get_class($this));
    }
}

class User extends BaseModel {
...
}


class Robots extends Phalcon\Mvc\Model
{
    public function getSource()
    {
        return "robots_" . date("Ym");
    }
}

cookie

2.0.9以前的版本先set('key'),而後再正常的set('key','value')。也就是set兩次繞過bug。

2.0.x分支已經修復這個bug,查看github源碼

下面的單元測試能夠重現這個bug。若是使用2.0.9以前的版本,建議採用兩次set

public function testCookies()
{
    // di factory
    $di = new \Phalcon\Di\FactoryDefault();
    $di->setShared('crypt', function () {
        $crypt = new \Phalcon\Crypt();
        // don't use PADDING_DEFAULT, Affect the cookie result
        $crypt->setPadding(\Phalcon\Crypt::PADDING_ZERO);
        $crypt->setKey('secret_key@123456789'); // Use your own key!

        return $crypt;
    });
    // http cookies
    $di->setShared('cookies', function () {
        $cookies = new \Phalcon\Http\Response\Cookies();
        $cookies->useEncryption(false);

        return $cookies;
    });

    /** @var \Phalcon\Http\Response\CookiesInterface  $cookies */
    $cookies = \Phalcon\DI::getDefault()->get('cookies');

    $_COOKIE['key'] = '1234567890';
    $_COOKIE['key2'] = '0987654321';

    //$cookies->set('key'); // ver <= 2.0.9 uncomment will test passed
    //$cookies->set('key2'); // ver <= 2.0.9 uncomment will test passed

    $this->assertEquals('1234567890', $cookies->get('key')->getValue());

    $cookies->set('key', 'value', time() + 3600, '/', false, 'kinhom.com', true);
    $cookies->set('key2', 'value2', time() + 3600, '/', false, 'kinhom.com', true);

    $this->assertInstanceOf('\Phalcon\Http\Cookie', $cookies->get('key'));

    /* 
     * phalcon ver <= 2.0.9
     * Failed asserting that two strings are equal.
     * Expected :'value'
     * Actual   :'1234567890'
     */
    $this->assertEquals('value', $cookies->get('key')->getValue());
    $this->assertEquals('value2', $cookies->get('key2')->getValue());
}

view 渲染順序是

volt模板文件內 {{ content() }} 是聯接各個模板的橋樑,完整的順序是setMainView() -> setTemplateAfter() -> setLayout() -> setTemplateBefore -> pick()

即Main Layout -> Layout -> Action View

controller中若echo字符串,Action view內要寫上{{ content() }}纔會輸出。

參考範例

背景知識

不管你是否使用phalcon框架,作爲PHP開發者,有些背景知識是必須要了解的。

相關文章
相關標籤/搜索