本文將記錄我在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還會自動再加一遍字首,就找不到快取了,導致刪除失敗。
解決方法:
- 用
$this->cache->flush()
清除所有快取,但是會清除所有快取,所以如果是memcache或者redis快取可以設定一下statsKey,避免清除了所有快取。 - 或者不使用字首,就可以正常使用queryKeys()和delete()這條流程。