Phalcon填坑手冊:開發中會遇到的問題和解決方案(不斷更新)

咖灰怪發表於2019-02-16

本文將記錄我在Phalcon開發過程中遇到的問題,以及如何如何解決。

本文首發在我的部落格,我更新之後會更新過來;如果想檢視最新的,可以到我的部落格:Phalcon填坑手冊:開發中會遇到的問題和解決方案(不斷更新)

1. 正確地在控制器中獲取引數

一般情況下,GET/POST請求獲取引數:

$this->request->get(引數名);
$this->request->getPost("引數名")

路由模式下route獲取引數要用dispatcher->getParam();
route下定義好了引數名稱可以直接通過引數名稱來獲取:

this->dispatcher->getParam("引數名");

route沒有定義好名稱,只是規則中寫了:params做匹配,可以在控制器中按順序來獲取:

class NewsController extends Controller {
    public function showAction($id, $testParam)
    {
        echo $id, `|` , $testParam;
    }
}

2. 為 url 定製路由

預設自動解析/:controller/:action/:params模式
在例項化時,不加false引數:

$router = new Router();

url將會自動進行/:controller/:action/:params引數的解析, 比如https://www.goozp.com/login將會解析成Login controller下的預設action。

當使用路由時,保留預設解析模式有時候會導致解析混亂,比較建議採用完全自定義路由模式
完全自定義路由,在new時加上false:

$router = new Router(false);

不自動解析/:controller/:action/:params這些規則, 具體的路由匹配規則自己來編寫,例如:

$router->add(`/login`,
    [
        `module`     => `admin`,
        `controller` => `login`,
        `action`     => `index`,
    ]
)->setName(`login`);

這樣不會因為自動解析而導致url混亂,但是所有url都要自己來定製路由規則。

3. flash提示重寫後輸出不正確 (未解決)

重寫後輸出的html標籤是字串,外面帶””

4. Config 中 baseURI 的正確設定

因為有Apache+.htaccess檔案重寫規則 或者 nginx配置到public/index.php的重寫規則,我們不需要專案中的url帶有/publc/index.php。
但是預設是指到了/public/index.php中(比如$_SERVER[“PHP_SELF”]獲取從根目錄到當前頁面本身的路徑); 所以,如果有Apache重寫規則或者nginx配置到public/index.php的重寫配置,我們需要把url設定為不帶public/index.php的,於是就有了官方的這個設定:
使用 $_SERVER[“PHP_SELF”],並且正則去除/public/index.php

`baseUri`        => preg_replace(`/public([/\\])index.php$/`, ``, $_SERVER["PHP_SELF"]),

這是動態寫法,這種寫法的問題在於 $_SERVER[“PHP_SELF”] 的不確定性,返回的值將根據 Apache 或 nginx 配置的 root,是否配置host或者域名,$_SERVER[“PHP_SELF”]會有不同的返回值。這樣的話上面寫法前面的正則並不是全部相容的,所以這樣寫除錯起來就稍麻煩。

簡單一點,用靜態寫法:
設定host或者配置域名

`baseUri`        => `/`,

如果是想在localhost下直接開啟,則需要加上專案外層目錄名,例如:

`baseUri`        => `/zphal/`,

這樣的話,我們在定義url服務的時候只需要把這個定義的配置傳進去:

$di->setShared(`url`, function () {
    $config = $this->getConfig();

    $url = new UrlResolver();
    $url->setBaseUri($config->application->baseUri); // baseUri

    return $url;
});

以上寫法的WebServer配置:

  • Apache:

.hatccess按照官方配置就可以;配置host時配置到public下或者public外面一層的專案根目錄也可以:

<VirtualHost *:80>
    DocumentRoot "D:phpStudyWWWzPhalpublic"
    ServerName goozp.com
    ServerAlias 
  <Directory "D:phpStudyWWWzPhalpublic">
      Options FollowSymLinks ExecCGI
      AllowOverride All
      Order allow,deny
      Allow from all
      Require all granted
  </Directory>
</VirtualHost>
  • Nginx

大概的配置如下,配置到public下,並定義rewrite規則:

server {
    listen        80;
    server_name www.goozp.com goozp.com;

    root /data/www/zPhal/public;
    index index.php index.html index.htm;

    charset utf-8;
    client_max_body_size 100M;
    fastcgi_read_timeout 1800;

    location / {
        # Matches URLS `$_GET[`_url`]`
        try_files $uri $uri/ /index.php?_url=$uri&$args;
    }

    location ~ .php$ {
        try_files $uri =404;

        #fastcgi_pass  unix:/var/run/php/php7.0-fpm.sock;
        fastcgi_pass  php-fpm:9000;

        fastcgi_index /index.php;

        include fastcgi_params;
        fastcgi_split_path_info       ^(.+.php)(/.+)$;
        fastcgi_param PATH_INFO       $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED /data/www/zPhal/public/$fastcgi_path_info;
        fastcgi_param SCRIPT_FILENAME /data/www/zPhal/public/$fastcgi_script_name;
    }

    location ~ /.ht {
        deny all;
    }

    location ~* .(js|css|png|jpg|jpeg|gif|ico)$ {
        expires       max;
        log_not_found off;
        access_log    off;
    }
}

5. 事件管理器,fire寫法不管用

被手冊誤導,理解錯誤了。

下面是 錯誤 的寫法,在dispatcher中去定義了監聽事件:

$di->set(`dispatcher`, function () {
    // 建立一個事件管理器
    $eventsManager = new EventsManager();

    $media = new Media();

    $media->setEventsManager($eventsManager);

    // 監聽分發器中使用外掛產生的事件
    $eventsManager->attach(
        "media",
        new AliYunOss()
    );

    $dispatcher = new Dispatcher();
    $dispatcher->setDefaultNamespace(`ZPhalModulesAdminControllers\`);
    $dispatcher->setEventsManager($eventsManager); // 分配事件管理器到分發器

    return $dispatcher;
});

然而我想封裝的是檔案上傳功能,跟 dispatcher分發器 沒有任何關係,所以起不了作用還報錯;應該註冊一個返回DI容器的檔案上傳服務:

$di->set(`mediaUpload`,function (){
    // 建立一個事件管理器
    $eventsManager = new EventsManager();

    $media = new Media();

    $eventsManager->attach(
        "media",
        new AliYunOss()
    );

    $media->setEventsManager($eventsManager);

    return $media;
});

6.使用模型關聯不起作用

扔進去的物件報錯;需要給關聯的物件定義alias,通過alias來獲取。
如果是這樣:

$terms = new Terms();
$terms->name = $name;
$terms->slug = $slug;

$termTaxonomy = new TermTaxonomy();
$termTaxonomy->Terms  = $terms; // 這裡
$termTaxonomy->taxonomy = $type;

$termTaxonomy->save();

在$termTaxonomy->Terms = $terms;這裡,Terms是TermTaxonomy Model中定義的關係的別名(alias);
定義方式如下,在model中:

$this->belongsTo(
    "term_id",
    "ZPhal\Models\Terms",
    "term_id",
    [
        "alias" => "Terms",
    ]
);

不起alias別名會報錯。

7. 插入資料時返回主鍵id

通過$model -> getWriteConnection() -> lastInsertId();來獲取:

$model = new model();

if($model -> create($data)) {
    $insertId = $model -> getWriteConnection() -> lastInsertId($model -> getSource());
}

或者直接在執行之後拿id屬性:

<?php
$model = new model();

if($model -> create($data)) {
    $insertId = $model -> id;
}

8. model事件 beforeCreate 和欄位檢查

在 beforeCreate 事件中定義資料表欄位的資料檢查和資料賦值,不生效。

beforeCreate 在執行之前就會檢查欄位是否符合要求(validation),所以在beforecreate時再插入不行,會報錯,需要在執行create前就傳值,或者設定預設值。

可以在 beforeValidation 時進行賦值檢查的操作。

9. 操作model儲存時,save或update無效

表現為save或者update失敗,且不報錯的問題。
情況:主鍵設定為兩個欄位,更新時更新了其中一個欄位。
解決:不應該修改主鍵。
參考:https://stackoverflow.com/questions/3838414/can-we-update-primary-key-values-of-a-table

10. find()與findFirst()

  • find()與findFirst()返回值資料格式是不同的。
  • 加了 column 引數時的返回值object裡時不完整的,所以無法使用save等方法,無法使用model關係。
  • 沒有資料時,find()返回空陣列,findfrist()返回false

11. PhalconCacheBackendRedis 的 queryKeys()出現以下錯誤:

Cached keys need to be enabled to use this function (options[`statsKey`] == `_PHCR`)!

Redis的預設配置有一個引數為‘_PHCR’字首,所以queryKeys()時需要帶上查詢字首。

12 dispatcher->forward() 分發後原指令碼仍然繼續執行

可以加上return阻斷:

$this->dispatcher->forward([
    "controller" => "error",
    "action"    => "route404"
]);
return;

在分發後後面的程式碼將不再執行。

13. 錯誤:Encryption key cannot be empty

使用cookie時,預設會使用Crypt加密,而使用Crypt加密需要定義一個全域性加密key。
可以禁用cookie加密:

<?php
use PhalconHttpResponseCookies;

$di->set(
    "cookies",
    function () {
        $cookies = new Cookies();

        $cookies->useEncryption(false);

        return $cookies;
    }
);

或者設定一個key:

<?php
use PhalconCrypt;

$di->set(
    "crypt",
    function () {
        $crypt = new Crypt();

        $crypt->setKey(`#1dj8$=dp?.ak//j1V$`); // 使用你自己的key!

        return $crypt;
    }
);

14. cache刪除失敗:queryKeys()之後foreach遍歷迴圈delete()刪除失敗

正常刪除時:

$this->cache->delete($key)

如果設定了字首,會在$key自動加上字首。

queryKeys列出來的已經帶上了字首,所以這樣刪除:

$keys = $this->cache->queryKeys();
    foreach($keys as $key) {
        $this->cache->delete($key)
    }

傳進去的key還會自動再加一遍字首,就找不到快取了,導致刪除失敗。

解決方法:

  1. $this->cache->flush()清除所有快取,但是會清除所有快取,所以如果是memcache或者redis快取可以設定一下statsKey,避免清除了所有快取。
  2. 或者不使用字首,就可以正常使用queryKeys()和delete()這條流程。

相關文章