原文发表于: Phalcon入门教程之模型
Phalcon
提供了四种方式操作Mysql数据库:模型、PHQL、数据库抽象层以及原生SQL。不论何种方式,首先都需要在DI中注册 db
服务才能正常使用:
DI注册db服务
// 文件路径:app/core/services.php
$di -> setShared('db', function () use($config) {
$dbconfig = $config -> database -> db;
$dbconfig = $dbconfig -> toArray();
if (!is_array($dbconfig) || count($dbconfig)==0) {
throw new \Exception("the database config is error");
}
$connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array(
"host" => $dbconfig['host'], "port" => $dbconfig['port'],
"username" => $dbconfig['username'],
"password" => $dbconfig['password'],
"dbname" => $dbconfig['dbname'],
"charset" => $dbconfig['charset'])
);
return $connection;
});
数据库连接信息配置如下:
// 文件路径:app/config/system.php
return array(
//数据库表配置
'database' => array(
//数据库连接信息
'db' => array(
'host' => '127.0.0.1',
'port' => 3306,
'username' => 'admin',
'password' => 'admin',
'dbname' => 'test',
'charset' => 'utf8',
),
//表前缀
'prefix' => 'test_',
),
);
记录底层SQL语句
在我们开发过程中,有时候需要通过SQL语句来分析定位问题。那么,我们需要将ORM生成的底层SQL记录到日志中。修改DI中注册的 db
服务如下:
//文件路径:app/core/services.php
$di -> setShared('db', function () use($config) {
$dbconfig = $config -> database -> db;
$dbconfig = $dbconfig -> toArray();
if (!is_array($dbconfig) || count($dbconfig)==0) {
throw new \Exception("the database config is error");
}
$eventsManager = new \Phalcon\Events\Manager();
// 分析底层sql性能,并记录日志
$profiler = new Phalcon\Db\Profiler();
$eventsManager -> attach('db', function ($event, $connection) use ($profiler) {
if($event -> getType() == 'beforeQuery'){
//在sql发送到数据库前启动分析
$profiler -> startProfile($connection -> getSQLStatement());
}
if($event -> getType() == 'afterQuery'){
//在sql执行完毕后停止分析
$profiler -> stopProfile();
//获取分析结果
$profile = $profiler -> getLastProfile();
$sql = $profile->getSQLStatement();
$params = $connection->getSqlVariables();
(is_array($params) && count($params)) && $params = json_encode($params);
$executeTime = $profile->getTotalElapsedSeconds();
//日志记录
$currentDay = date('Ymd');
$logger = new \Phalcon\Logger\Adapter\File(ROOT_PATH . "/app/cache/logs/{$currentDay}.log");
$logger -> debug("{$sql} {$params} {$executeTime}");
}
});
$connection = new \Phalcon\Db\Adapter\Pdo\Mysql(array(
"host" => $dbconfig['host'], "port" => $dbconfig['port'],
"username" => $dbconfig['username'],
"password" => $dbconfig['password'],
"dbname" => $dbconfig['dbname'],
"charset" => $dbconfig['charset'])
);
/* 注册监听事件 */
$connection->setEventsManager($eventsManager);
return $connection;
});
通过代码可以看到,不仅是底层SQL,还将SQL绑定的参数(PDO预处理)和SQL执行时间也记录到日志中了。日志记录demo如下:
SELECT `users`.`uid` AS `uid`, `users`.`mobile` AS `mobile` FROM `users` WHERE `users`.`uid` = :uid LIMIT :APL0 {"uid":1,"APL0":1} 0.034402132034302
花括号({}
)中的就是SQL预处理时绑定的参数,最后的浮点数就是SQL执行时间(单位为秒)。
创建模型
模型类的命名必须符合驼峰命名法,而且须继承自 Phalcon\Mvc\Model
类:
// 文件路径:app/frontend/models/ArticlesModel.php
class Articles extends \Marser\App\Frontend\Models\BaseModel {
// \Marser\App\Frontend\Models\BaseModel继承自 \Phalcon\Mvc\Model 类。
// 此处是再次封装一个基础模型类, 以方便后续的通用方法封装
//...
}
数据库表映射
默认情况下,Articles
模型类对应的数据表名是 articles
;若是 ArticlesTags
模型类,则对应的数据库表名是 articles_tags
, 即类名对应着表名。如果想映射到其他数据库表,可以使用 setSource()
方法设置:
// 文件路径:app/frontend/models/ArticlesModel.php
class Articles extends \Marser\App\Frontend\Models\BaseModel {
public function initialize()
{
$this->setSource("articles_tags");
}
}
在项目开发中,建议一个数据表对应着一个模型类。即使是关联表,也强烈建议创建其对应的模型类,因为 Phalcon
中提供的连表操作,都是基于模型类的(后续的教程会分享)。
设置表前缀
在进行数据库表设计的时候,有时会在表名前加上一段前缀,如 test_articles
。我们依然可以通过 setSource()
映射数据表:
// 文件路径:app/frontend/models/ArticlesModel.php
class Articles extends \Marser\App\Frontend\Models\BaseModel {
public function initialize()
{
$this->setSource("test_articles");
}
}
假设,我们的项目中有100张数据表,那么就意味着有100个模型类。此时我们在每个模型类中都必须调用 setSource()
来映射完整的表名。如果某天我们需要修改这100张表的前缀,那么将要修改这100个模型类,不仅耗时耗力还麻烦。我们尝试着将此处理过程提取出来进行封装:
// 文件路径: app/frontend/models/ArticlesModel.php
class ArticlesModel extends \Marser\App\Frontend\Models\BaseModel {
/**
* 表名
*/
const TABLE_NAME = 'articles';
public function initialize(){
parent::initialize();
//映射数据表(补上表前缀)
$this->set_table_source(self::TABLE_NAME);
}
}
在 BaseModel
模型基类中的 set_table_source()
方法定义如下:
// 文件路径: app/frontend/models/BaseModel.php
class BaseModel extends \Phalcon\Mvc\Model {
public function initialize(){
}
/**
* 映射数据表(补上表前缀)
* @param string $tableName
* @param null $prefix
*/
protected function set_table_source($tableName, $prefix = null){
//默认从配置中读取表前缀配置
empty($prefix) && $prefix = $this->getDI()->get('config')->database->prefix;
//拼接成完整表名之后,再通过setSource()映射数据表
$this->setSource($prefix . $tableName);
}
}
我们在每个模型类中定义一个 类常量
来存储无前缀的表名,再通过 set_table_source()
成员方法来拼接表前缀并映射。眼尖的读者,应该在上面的数据库连接信息配置中有看到 prefix
的表前缀配置。
还是以上面为例,此时我们就不需要修改100个模型类的代码,而只需修改配置文件中的 prefix
配置即可。
以上代码已托管在github:https://github.com/KevinJay/marser-phalcon-demo
最后,欢迎大家加入QQ群交流讨论:
- 广州PHP高端交流群:158587573
- Phalcon玩家群:150237524