You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 04:37:42