Yii模型可通过定义场景常量(如SCENARIO_LIST、SCENARIO_DETAIL、SCENARIO_EXPORT)和统一入口方法findForScenario(),按需返回预设查询逻辑的Query对象,支持关联加载、字段选择及动态条件注入,提升复用性与可维护性。

你需要让同一个Yii模型在不同业务环节执行不同的查询逻辑,比如用户列表页只查基础字段,详情页要连带角色和权限数据,导出报表时又得额外加载统计字段——不能靠硬编码写死SQL,也不能每次手动拼条件。
定义场景常量与基础查询方法
在模型类顶部声明清晰的场景常量,避免散落字符串造成维护困难。
public const SCENARIO_LIST = 'list';
public const SCENARIO_DETAIL = 'detail';
public const SCENARIO_EXPORT = 'export';
在模型中新增一个统一入口方法,接收场景参数并返回对应查询对象:
public static function findForScenario($scenario = self::SCENARIO_LIST)
{
$query = self::find();
switch ($scenario) {
case self::SCENARIO_DETAIL:
$query->with(['roles', 'permissions']);
break;
case self::SCENARIO_EXPORT:
$query->select(['*', 'login_count AS total_logins']);
break;
}
return $query;
}
控制器中按需调用场景查询
第一步:在控制器动作里明确传入场景标识,不要依赖默认值。
第二步:调用模型的 findForScenario() 方法获取已预设逻辑的查询对象。
第三步:继续链式追加分页、排序等通用操作,保持复用性。
例如列表页:
$users = User::findForScenario(User::SCENARIO_LIST)
→orderBy('created_at DESC')
→limit(20)
→all();
例如导出页:
$users = User::findForScenario(User::SCENARIO_EXPORT)
→asArray()
→all();
【注意】asArray() 必须在 all() 之前调用,否则返回的是 Active Record 对象数组,无法直接用于 CSV 导出
动态注入关联条件(高级用法)
方法一:在 findForScenario() 中判断当前用户权限,自动附加 where 条件
if (Yii::$app->user->can('viewAllUsers')) {
$query->andWhere(['status' => [User::STATUS_ACTIVE, User::STATUS_PENDING]]);
} else {
$query->andWhere(['status' => User::STATUS_ACTIVE]);
}
方法二:允许外部传入回调函数,实现更灵活的定制
public static function findForScenario($scenario = self::SCENARIO_LIST, $customizer = null)
{
$query = self::find();
// ……原有分支逻辑
if (is_callable($customizer)) {
$customizer($query);
}
return $query;
}
调用示例:
User::findForScenario(User::SCENARIO_LIST, function ($q) {
$q->andWhere(['department_id' => Yii::$app->user->identity->department_id]);
});


















