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

求助:Moodle插件中无需类库实现PHP数据导出至Excel

无需第三方库实现Moodle插件数据导出Excel的方案

核心思路

利用CSV格式导出数据——Excel原生支持打开CSV文件,无需依赖任何第三方类库,完全通过PHP原生函数和Moodle内置API实现。

步骤实现

1. 添加导出按钮到页面

在渲染HTML表格的代码之前,添加导出按钮,点击后触发导出请求:

// 添加导出按钮
$exporturl = new moodle_url('/local/gradebook_sync/sync.php', ['courseid' => $courseid, 'syncid' => $syncid, 'export' => 'excel']);
echo $OUTPUT->single_button($exporturl, get_string('exportexcel', 'local_gradebook_sync'), 'get');

同时在插件的语言文件lang/en/local_gradebook_sync.php中添加对应字符串:

$string['exportexcel'] = '导出为Excel';

2. 处理导出请求逻辑

在页面开头(require_once('../../config.php');之后)添加导出判断逻辑,当检测到export=excel参数时,直接生成CSV文件并输出,跳过页面渲染:

// 处理导出请求
$export = optional_param('export', '', PARAM_ALPHANUM);
if ($export === 'excel') {
    $gradebook_sync = $DB->get_record('local_gradebook_sync', ['id' => $syncid]);
    $destination_course = get_course($gradebook_sync->destinationcourseid);

    // 获取源课程模块数据
    $source_course_modules_sql = <<<SQL
        SELECT
            cm.id,
            m.name AS 'module',
            cm.instance AS 'instance',
            cs.section,
            cm.idnumber
        FROM {course_modules} cm
        JOIN {course_sections} cs ON cs.id = cm.section
        JOIN {modules} m ON m.id = cm.module
        WHERE
            cm.course = :course
            AND
            m.name NOT IN ('book', 'resource', 'folder', 'imscp', 'label', 'page','url')
    SQL;
    $source_course_modules_variables = ['course' => $gradebook_sync->sourcecourseid];
    $source_course_modules = $DB->get_records_sql($source_course_modules_sql, $source_course_modules_variables);

    foreach($source_course_modules as $source_course_module) {
        $source_course_module->activitytitle = $DB->get_record_sql('SELECT name FROM {' . $source_course_module->module . '} WHERE id = :id', ['id' => $source_course_module->instance])->name;
    }

    // 获取目标课程模块数据
    $destination_course_modules_sql = <<<SQL
        SELECT
            cm.id,
            cs.section,
            IFNULL(NULLIF(cs.name, ''), IF(cs.section = 0, 'General', CONCAT('Topic ', cs.section))) AS 'sectiontitle',
            m.name AS 'module',
            cm.instance AS 'instance',
            cm.idnumber
        FROM {course_modules} cm
        JOIN {course_sections} cs ON cs.id = cm.section
        JOIN {modules} m ON m.id = cm.module
        WHERE
            cm.course = :course
            AND
            m.name NOT IN ('book', 'resource', 'folder', 'imscp', 'label', 'page','url')
    SQL;
    $destination_course_modules_variables = ['course' => $gradebook_sync->destinationcourseid];
    $destination_course_modules = $DB->get_records_sql($destination_course_modules_sql, $destination_course_modules_variables);

    // 组织导出数据
    $export_data = [];
    $export_data[] = ['Section', 'Activity', 'Activity type', 'Status']; // 表头

    foreach($destination_course_modules as $destination_course_module) {
        $destination_course_module->activitytitle = $DB->get_record_sql('SELECT name FROM {' . $destination_course_module->module . '} WHERE id = :id', ['id' => $destination_course_module->instance])->name;

        // 映射活动类型
        switch($destination_course_module->module) {
            case 'assign':
                $activitytype = 'Assignment';
                break;
            case 'assignment':
                $activitytype = 'Assignment 2.2';
                break;
            case 'chat':
                $activitytype = 'Chat';
                break;
            case 'checklist':
                $activitytype = 'Checklist';
                break;
            case 'choice':
                $activitytype = 'Choice';
                break;
            case 'data':
                $activitytype = 'Database';
                break;
            case 'feedback':
                $activitytype = 'Feedback';
                break;
            case 'forum':
                $activitytype = 'Forum';
                break;
            case 'glossary':
                $activitytype = 'Glossary';
                break;
            case 'h5pactivity':
            case 'hvp':
                $activitytype = 'H5P';
                break;
            case 'lesson':
                $activitytype = 'Lesson';
                break;
            case 'lti':
                $activitytype = 'External tool';
                break;
            case 'questionnaire':
                $activitytype = 'Questionnaire';
                break;
            case 'quiz':
                $activitytype = 'Quiz';
                break;
            case 'scorm':
                $activitytype = 'SCORM package';
                break;
            case 'survey':
                $activitytype = 'Survey';
                break;
            case 'wiki':
                $activitytype = 'Wiki';
                break;
            case 'workshop':
                $activitytype = 'Workshop';
                break;
            default:
                $activitytype = 'Unknown';
        }

        // 判定状态
        $status = 'INVALID';
        foreach($source_course_modules AS $source_course_module) {
            if((trim($source_course_module->activitytitle) == trim($destination_course_module->activitytitle)) && ($source_course_module->section == $destination_course_module->section) && ($source_course_module->module == $destination_course_module->module)) {
                $status = 'VALID';
                break;
            }
        }

        $export_data[] = [
            $destination_course_module->sectiontitle,
            $destination_course_module->activitytitle,
            $activitytype,
            $status
        ];
    }

    // 设置HTTP响应头,触发浏览器下载
    header('Content-Type: application/vnd.ms-excel');
    header('Content-Disposition: attachment;filename="'.$destination_course->shortname.'_gradebook_sync.xls"');
    header('Cache-Control: max-age=0');

    // 生成CSV内容并输出
    $output = fopen('php://output', 'w');
    foreach ($export_data as $row) {
        // 转换编码确保中文正常显示
        foreach ($row as &$cell) {
            $cell = mb_convert_encoding($cell, 'GB2312', 'UTF-8');
        }
        fputcsv($output, $row, "\t");
    }
    fclose($output);
    exit; // 终止脚本,避免输出页面多余内容
}

3. 优化建议

将数据获取和处理的逻辑封装成独立函数(比如get_sync_records($syncid)),在页面渲染和导出逻辑中复用,减少代码冗余,提升可维护性。

内容的提问来源于stack exchange,提问作者Kayd martin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 04:40:17