框架深入 - yii\db\Expression

  • 作者:KK

  • 发表日期:2017.3.25


快速了解

yii\db\Expression是一个代表了“数据库表达式”的类

通过以下代码可以快速了解它:

$query1 = new \yii\db\Query();
$command1 = $query1->from('user')->where(['<=', 'add_time', 'NOW()'])->createCommand();
echo $command1->rawSql; // SELECT * FROM `user` WHERE `add_time` <= 'NOW()'		重点是这个NOW()被单引号包住了


//------用Expression类传入表达式------
$query2 = new \yii\db\Query();
$expression = new \yii\db\Expression('NOW()');
$command2 = $query2->from('user')->where(['<=', 'add_time', $expression])->createCommand();
echo $command2->rawSql; // SELECT * FROM `user` WHERE `add_time` <= NOW()		没有引号包住NOW()

好了这下应该清楚了


解读

如果不用Expression

条件里面有函数等表达式的话,会被当成字符串用'号引住,因为框架要实现防注入


当查询构造器遇到Expression

它会将表达式原封不动地加入查询语句里,不会加任何引号

其实这个Expression类里面的代码很简单:

namespace yii\db;

class Expression extends \yii\base\Object
{
    public $expression;
	
    public $params = [];
	
    public function __construct($expression, $params = [], $config = [])
    {
        $this->expression = $expression;
        $this->params = $params;
        parent::__construct($config);
    }
	
    public function __toString()
    {
        return $this->expression;
    }
}

如果光看Expression类的代码一般是不能快速理解是怎么发生作用的,那么这样的代码如何产生作用?

再看看我这段虚拟的框架底层处理逻辑:

function buildWhere($condition){
	$where = '';
	$value = $condition['add_time'];
	if(gettype($value) != 'object'){
		$where .= "add_time <= '" . $value . "'"; // 加了引号
		
	}elseif($value instanceof \yii\db\Expression){
		$where .= 'add_time <= ' . $value; //触发了Expression的toString,于是表达式原样拼接
	}
	return $where;
}

你在@vendor/yiisoft/yii2/db目录的所有php文件里全词大小写匹配搜索一下Expression就可以更全面地了解它的处理逻辑了


也可以用在value里

比如下面这样将表达式放在更新方法的value里实现所有vip用户金币+9999

$data = ['gold' => new \yii\db\Expression('gold + 9999')];
$command = Yii::$app->db->createCommand()->update('user', $data, ['vip' => 1]);

echo $command->rawSql; // UPDATE `user` SET `gold`= gold + 9999 WHERE `vip`=1

更多例子

通过Expression传入自由的SQL合法表达式

$query1 = new \yii\db\Query();
$command1 = $query1
	->from('user')
	->where(['<=', 'age', new \yii\db\Expression('33 + 99')])
	->createCommand();

echo $command1->rawSql; // SELECT * FROM `user` WHERE `age` <= 33 + 99



$query2 = new \yii\db\Query();
$expression = new \yii\db\Expression('DATE_SUB(NOW(), INTERVAL 1 MONTH)');
$command2 = $query2
	->from('article')
	->where(['date' => $expression])
	->createCommand();
echo $command2->rawSql; // SELECT * FROM `article` WHERE `date` = DATE_SUB(NOW(), INTERVAL 1 MONTH)