Yii中应将通用查询逻辑抽成静态方法或自定义Query类:静态方法如getActiveUsers()返回数组,适合简单复用;推荐用继承ActiveQuery的UserQuery类,通过active()、recent()等链式方法增强复用性;基类BaseQuery可实现跨模型复用如notDeleted();需重写find()方法并使用Expression+参数绑定防SQL注入。

在 Yii 应用中频繁复制粘贴 find()→where()→orderBy()→asArray() 这类查询链,会导致模型层逻辑分散、修改成本高、事务一致性难保障。直接在控制器里写重复查询语句,一旦字段名变更或条件升级,就得逐个文件 grep 替换,漏改一处就引发数据不一致。
把通用查询逻辑抽成静态方法
在 User 模型类中添加一个 public static 方法,例如 getActiveUsers():
public static function getActiveUsers($limit = 10) {
return self::find()
->where(['status' => self::STATUS_ACTIVE])
->orderBy(['created_at' => SORT_DESC])
->limit($limit)
->asArray()
->all();
}
调用时直接 User::getActiveUsers(5),无需重复构造查询对象。注意:该方法返回的是数组,不是 ActiveRecord 对象,不能调用 save() 或 delete()。
用 Query 对象封装可复用的查询结构
方法一:定义独立的 Query 类(推荐)
新建文件 common/queries/UserQuery.php:
namespace common\queries;
use yii\db\ActiveQuery;
class UserQuery extends ActiveQuery
{
public function active() {
return $this->andWhere(['status' => \common\models\User::STATUS_ACTIVE]);
}
public function recent($days = 30) {
$timestamp = time() - $days * 86400;
return $this->andWhere(['>=', 'created_at', $timestamp]);
}
}
在 User 模型中覆盖 find() 方法,使其返回自定义 Query 类:
public static function find() {
return new UserQuery(get_called_class());
}
现在可链式调用:User::find()->active()->recent(7)->all();每个修饰方法都可单独复用,比如只查活跃用户 User::find()->active()->one()。
跨模型复用查询条件(如软删除、时间范围)
第一步:创建基类 Query
在 common/queries/BaseQuery.php 中定义:
namespace common\queries;
use yii\db\ActiveQuery;
abstract class BaseQuery extends ActiveQuery
{
public function notDeleted() {
return $this->andWhere(['deleted_at' => null]);
}
public function createdAt($operator = '>=', $value = null) {
$value = $value ?: time() - 86400;
return $this->andWhere([$operator, 'created_at', $value]);
}
}
第二步:让 UserQuery 继承它
class UserQuery extends BaseQuery { … }
第三步:在 ArticleQuery 中也继承 BaseQuery
class ArticleQuery extends BaseQuery { … }
【必须重写 find() 方法,否则子类仍返回默认 ActiveQuery】 每个模型都要显式覆盖 find(),否则 BaseQuery 的方法无法生效。
用 Expression 和参数绑定避免硬编码
方法一:在静态方法中使用 Expression 防止 SQL 注入
public static function findByTag($tag) {
return self::find()
->where(new \yii\db\Expression('FIND_IN_SET(:tag, tags)', [':tag' => $tag]))
->asArray()
->all();
}
方法二:用 addParams 分离参数与表达式
$query = self::find()
->where(new \yii\db\Expression('status IN (:statuses)'))
->addParams([':statuses' => [self::STATUS_ACTIVE, self::STATUS_PENDING]]);
这比拼接字符串安全得多,且参数可复用——同一个 $query 后续还能调用 ->limit(10)->all()。


















