本文将记录我在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:\phpStudy\WWW\zPhal\public"
ServerName goozp.com
ServerAlias
<Directory "D:\phpStudy\WWW\zPhal\public">
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('ZPhal\Modules\Admin\Controllers\\');
$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 Phalcon\Http\Response\Cookies;
$di->set(
"cookies",
function () {
$cookies = new Cookies();
$cookies->useEncryption(false);
return $cookies;
}
);
或者设置一个key:
<?php
use Phalcon\Crypt;
$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()这条流程。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。