最近在制作 Typecho default-ultra 主题,在这个主题中我加入了输出随机文章的功能。代码片段如下:

$select = $this->select()->from('table.contents')
    ->where('table.contents.password IS NULL OR table.contents.password = ""')
    ->where('table.contents.status = ?', 'publish')
    ->where('table.contents.created <= ?', time())
    ->where('table.contents.type = ?', 'post')
    ->limit($this->parameter->limit)
    ->order('RAND()');

乍一看,这个代码片段本身没什么问题,使用过程中也没什么问题。

然而:我是在 PHP84 + MySQL8 的环境下开发和运行的,确实没问题,但是考虑到有用户使用 SQLLite 和 PostgreSQL,那就有问题了。

在 MySQL 中,生成随机数的函数是 RAND,然而在 SQLLite 和 PostgreSQL 中,生成随机数的函数是 RANDOM,如果使用的是 SQLLite 和 PostgreSQL,那这里必然会报错。

再者,在 PostgreSQL 中,判断空字符串不能直接用双引号,因为双引号会被解析为标识符,这里也会报错。

所以,在编写主题时,涉及到 SQL 语句的,还是要充分考虑数据库的兼容性~

修改代码片段:

// 获取db对象
$db = Typecho_Db::get();
// 获取数据库适配器名称
$adapterName = $db->getAdapterName();

// 根据数据库适配器名称,设定不同的SQL
if ($adapterName == 'pgsql' || $adapterName == 'Pdo_Pgsql' || $adapterName == 'Pdo_SQLite' || $adapterName == 'SQLite') {
    $orderBy = 'RANDOM()';
} else {
    $orderBy = 'RAND()';
}

$select = $this->select()->from('table.contents')
    ->where('table.contents.password IS NULL OR table.contents.password = \'\'')
    ->where('table.contents.status = ?', 'publish')
    ->where('table.contents.created <= ?', time())
    ->where('table.contents.type = ?', 'post')
    ->limit($this->parameter->limit)
    ->order($orderBy);

完美解决。