PhalApi:[1.15] 数据库操作:基于NotORM的使用及优化

1.15.1 NotORM官网

这里使用了NotORM进行DB操作,具体的数据库操作使用文档请见NotORM官网:http://www.notorm.com  

1.15.2 NotORM的优化

但为了更符合项目的开发,这里对NotORM的底层作了升级修改,以下为主要修改点和新的使用:

(1)将原来返回的结果全部从对象改成数组

对原来的大部分使用无特别影响,可按原来的方式开发。主要目的是为了更方面处理返回的数据,以及简化对结果的再解析,简单明了。
如:

DI()->notorm->user->where('username = ?', 'dogstar')->fetch();

返回的将是一个数组:

array(7) {
  ["id"]=>
  string(3) "180"
  ["username"]=>
  string(17) "dogstar"
  ["regtime"]=>
  string(10) "1414811954"
  //...
}

(2)提供获取全部结果的接口 - fetchAll()

如:

$rows = DI()->notorm->event_picurl->where('(eid)', $eids)->fetchAll();

即可获取全部的数据,不再受限于分页。

(3)提供更灵活的查询方式 -queryRows()

当需要进行复杂的SQL查询时,可以使用此接口,如:

$sql = 'select * from example AS ep LEFT JOIN user AS u ON ep.ui
d = u.id  where ep.touid = :userId ORDER BY dateline desc LIMIT :start,:num';
$params = array('userId' => $userId, ':start' => $start, ':num' => $num);
$rs= DI()->notorm->example->queryRows($sql, $params);

(4)修正原来的BUG

进行LIMIT时 因加入了 OFFSET 关键字导致MySQL下无法获取数据

(5)禁止全表删除,防止误删

(6)开启HTTP调试模式

当处于debug模式时,可以输入执行的全部SQL语句,以便调试。

如:

SELECT times FROM tpl_user_session_10 WHERE (user_id = ?); -- '74110'
{"ret":0,"data":{"code":0},"msg":""}

1.15.3 可选的Model基类

(1)表数据入口模式

我们一直在考虑,是否应该提供数据库的基本操作支持,以减少开发人员重复手工编写基本的数据操作。

最后,我们认为是需要的。然后就引发了新的问题:是以继承还是以委托来支持?

委托有助于降低继承的层级,但仍然需要编写同类的操作然后再次委托。所以,这里提供了基于NotORM的Model基类:PhalApi_Model_NotORM。

然而提供这个基类还是会遇到一些问题,例如:如何界定基本操作?如何处理分表存储?如何支持定制化?

由于我们这里的Model使用了 “表数据入口” 模式,而不是“行数据入口”,也不是“活动纪录”,也不是复杂的“数据映射器”。所以在使用时可以考虑是否需要此基类。即使这样,你也可以很轻松转换到 “行数据入口”和“活动纪录”模式。这里,PhalApi中的Model是更广义上的数据源层(后面会有更多说明),因此对应地 PhalApi_Model_NotORM基类充当了数据库表访问入口的对象,处理表中所有的行。

(2)规约层的CURD

在明白了Model基类的背景后,再来了解其具体的操作和如何继承会更有意义。

而具体的操作则与数据表的结构相关,在“约定编程”下:即每一个表都有一个主键(通常为id,也可以自由配置)以及一个序列化LOB字段ext_data。我们很容易想到Model接口的定义(注释已移除,感兴趣的同学可查看源码):

interface PhalApi_Model {

    public function get($id, $fields = '*');

    public function insert($data, $id = NULL);

    public function update($id, $data);

    public function delete($id);
}

上面的接口在规约层上提供了基于表主键的CURD基本操作,在具体实现时,需要注意两点:一是分表的处理;另一点则是LOB字段的序列化。

(3)不使用Model基类的写法

由于我们使用了NotORM进行数据库的操作,所以这里也提供了基于NotORM的基类:PhalApi_Model_NotORM。下面以我们熟悉的获取用户的基本信息为例,说明此基类的使用。

为唤醒记忆,下面贴上Model_User类原来的代码:

// $ vim ./Demo/Model/User.php

<?php

class Model_User {

    public function getByUserId($userId) {
        return DI()->notorm->user->select('*')->where('id = ?', $userId)->fetch();
    }
}

对应的调用:

$model = new Model_User();
$rs = $model->getByUserId($userId);

(4)继承Model基类的写法

若继承于PhalApi_Model_NotORM,则是:

// $ vim ./Demo/Model/User.php

<?php

class Model_User extends PhalApi_Model_NotORM {

    protected function getTableName($id) {
        return 'user';
    }
}

从上面的代码可以看出,基类已经提供了基于主键的CURD操作,但我们需要钩子函数以返回对应的表名。相应地,外部调用则调整为:

$model = new Model_User();
$rs = $model->get($userId);

再进一步,我们可以得到其他的基本操作:

$model = new Model_User();

// 插入
$model->insert(array('name' => 'whatever', 'from' => 'somewhere'));

// 更新
$model->update(1, array('name' => 'dogstar huang'));

// 删除
$model->delete(1);

1.15.4 定制化你的Model基类

正如上面提及到的两个问题:LOB序列化和分表处理。所以,如果PhalApi现有就此两问题的解决方案不能满足项目的需求,可作定制化处理。

(1)LOB序列化

先是LOB序列化,考虑到有分表的存在,当发生数据库变更时(特别在线上环境)会有一定的难度和风险,因此引入了扩展字段ext_data。当然, 此字段也应对数据库变更的同时,也可以作为简单明了的值对象的大对象。序列化LOB首先要考虑的问题是使用二进制(BLOB)还是文本(CLOB),出于 通用性、易读性和测试性,我们目前使用了json格式的文本序列化。所以,如果考虑到空间或性能问题(在少量数据下我认为问题不大,如果数据量大,应该及 时重新调整数据库表结构),可以重写formatExtData() & parseExtData()。

如改成serialize序列化:

abstract class My_Model_NotORM extends PhalApi_Model_NotORM {

    /**
     * 对LOB的ext_data字段进行格式化(序列化)
     */
    protected function formatExtData(&$data) {
        if (isset($data['ext_data']) && !is_string($data['ext_data'])) {
            $data['ext_data'] = serialize($data['ext_data']);
        }
    }

    /**
     * 对LOB的ext_data字段进行解析(反序列化)
     */
    protected function parseExtData(&$data) {
        if (isset($data['ext_data']) && !is_string($data['ext_data'])) {
            $data['ext_data'] = unserialize($data['ext_data'], true);
        }
    }

    // ...
}

(2)分表处理

其次是分表处理,同样考虑到分表的情况,以及不同的表可能配置不同的主键表,而基于主键的CURD又必须要先知道表的主键名才能进行SQL查询。所 以,问题就演变成了如何找到表的主键名。这里可以自动匹配,也可以手工指定。自动匹配是智能的,因为当我们更改表的主键时,可以自动同步更新而不需要担心 遗漏(虽然这种情况很少发生)。手工指定可以大大减少系统不必要的匹配操作,因为我们开发人员也知道数据库的主键名是什么,但需要手工编写一些代码。在这 里,提供了可选的手工指定,即可重写getTableKey($table)来指定你的主键名。

如,当我们的表的主键都固定为id时:

abstract class My_Model_NotORM extends PhalApi_Model_NotORM {

    protected function getTableKey($table) {
        return 'id';
    }
}



你可能感兴趣的:(数据库操作,PhalApi)