Yii2中如何关联MySQL表与MongoDB集合并实现GridView筛选排序
解决Yii2中MySQL ActiveRecord关联MongoDB集合并实现GridView筛选排序
1. 准备工作:安装Yii2 MongoDB扩展
确保项目已安装MongoDB扩展,通过Composer执行:
composer require yiisoft/yii2-mongodb
在项目配置文件(如config/web.php或config/db.php)中添加MongoDB连接配置:
'mongodb' => [ 'class' => '\yii\mongodb\Connection', 'dsn' => 'mongodb://localhost:27017/your_db_name', ],
2. 创建MongoDB Merchant模型
新建models/Merchant.php,继承MongoDB的ActiveRecord:
<?php namespace app\models; use yii\mongodb\ActiveRecord; class Merchant extends ActiveRecord { public static function collectionName() { return 'merchant'; // 对应MongoDB集合名 } public function attributes() { return ['_id', 'merchant_id', 'name', 'type']; // 集合包含的字段 } public static function primaryKey() { return ['merchant_id']; // 以merchant_id作为关联主键 } }
3. 处理MySQL Summary模型的关联与查询逻辑
修改或新建models/Summary.php,继承MySQL的ActiveRecord,添加自定义查询和关联逻辑:
<?php namespace app\models; use yii\db\ActiveRecord; use yii\helpers\ArrayHelper; class Summary extends ActiveRecord { public static function tableName() { return 'summary'; // MySQL表名 } // 单条数据关联商户信息 public function getMerchant() { return Merchant::findOne(['merchant_id' => $this->merchant_id]); } // 构建带MongoDB字段筛选、排序的查询 public static function search($params) { $query = self::find(); // 处理商户类型筛选:先从MongoDB获取符合类型的merchant_id列表 if (!empty($params['Summary']['merchant_type'])) { $merchantIds = Merchant::find() ->where(['type' => $params['Summary']['merchant_type']]) ->select('merchant_id') ->column(); $query->andWhere(!empty($merchantIds) ? ['merchant_id' => $merchantIds] : '1=0'); } // 处理商户名称排序:先获取商户ID与名称的映射,用MySQL FIELD函数自定义排序 if (!empty($params['sort']) && strpos($params['sort'], 'merchant_name') !== false) { $sortDir = strpos($params['sort'], '-') === 0 ? 'DESC' : 'ASC'; $merchantMap = Merchant::find() ->select(['merchant_id', 'name']) ->indexBy('merchant_id') ->asArray() ->all(); if (!empty($merchantMap)) { $sortedIds = ArrayHelper::getColumn($merchantMap, 'merchant_id'); if ($sortDir === 'DESC') { $sortedIds = array_reverse($sortedIds); } $query->orderBy(['FIELD(merchant_id, ' . implode(',', $sortedIds) . ')']); } } // 绑定MySQL原生字段的筛选条件 $query->andFilterWhere([ 'report_date' => $params['Summary']['report_date'] ?? null, // 其他MySQL字段筛选规则 ]); return $query; } }
4. 配置GridView实现展示、筛选与排序
控制器代码(示例:SummaryController.php)
<?php namespace app\controllers; use app\models\Summary; use yii\web\Controller; use yii\data\ActiveDataProvider; use yii\helpers\ArrayHelper; use app\models\Merchant; class SummaryController extends Controller { public function actionIndex() { $searchModel = new Summary(); $dataProvider = new ActiveDataProvider([ 'query' => $searchModel->search(\Yii::$app->request->queryParams), 'sort' => [ 'attributes' => [ 'merchant_id', 'report_date', // 自定义排序属性 'merchant_name' => [ 'asc' => ['merchant_name' => SORT_ASC], 'desc' => ['merchant_name' => SORT_DESC], 'label' => '商户名称', ], 'merchant_type' => [ 'label' => '商户类型', ], ], ], ]); // 批量加载商户数据,避免N+1查询问题 $summaryModels = $dataProvider->getModels(); $merchantIds = ArrayHelper::getColumn($summaryModels, 'merchant_id'); $merchants = Merchant::find() ->where(['merchant_id' => $merchantIds]) ->indexBy('merchant_id') ->all(); foreach ($summaryModels as $model) { $model->merchantData = $merchants[$model->merchant_id] ?? null; } return $this->render('index', [ 'dataProvider' => $dataProvider, 'searchModel' => $searchModel, ]); } }
视图代码(示例:views/summary/index.php)
<?php use yii\grid\GridView; use yii\helpers\Html; use app\models\Merchant; use yii\helpers\ArrayHelper; ?> <div class="summary-index"> <?= GridView::widget([ 'dataProvider' => $dataProvider, 'filterModel' => $searchModel, 'columns' => [ ['class' => 'yii\grid\SerialColumn'], 'merchant_id', 'report_date:date', // 展示商户名称 [ 'attribute' => 'merchant_name', 'value' => function ($model) { return $model->merchantData ? $model->merchantData->name : '未知'; }, ], // 展示商户类型并添加筛选下拉框 [ 'attribute' => 'merchant_type', 'value' => function ($model) { return $model->merchantData ? $model->merchantData->type : '未知'; }, 'filter' => Html::activeDropDownList( $searchModel, 'merchant_type', ArrayHelper::map(Merchant::find()->distinct('type')->all(), 'type', 'type'), ['prompt' => '全部'] ), ], ['class' => 'yii\grid\ActionColumn'], ], ]); ?> </div>
注意事项
- 跨库关联无法使用Yii2原生
joinWith,必须通过批量查询避免N+1性能问题。 - 若商户数量极大,使用
FIELD函数排序可能存在性能瓶颈,建议将MongoDB常用字段同步到MySQL表,或使用统一索引工具(如Elasticsearch)处理跨库查询。 - 筛选MongoDB字段时,需先从MongoDB获取符合条件的
merchant_id,再作为条件传入MySQL查询,确保筛选逻辑准确。
内容的提问来源于stack exchange,提问作者Prabowo Murti
相关产品推荐
相关产品推荐

