70 Craft cms利用插件进行数据导出 - xiaoxin01/Blog GitHub Wiki

Craft CMS默认没有提供数据导出功能,既有的一些数据导出插件多多少少都有一些缺陷,本文介绍如何使用插件及sql语句来导出需要的资料

插件安装

安装支持使用sql query来导出资料的插件,地址在这里:

https://sprout.barrelstrengthdesign.com/craft-plugins/reports

将下载好的内容复制到Craft cms的plugins/sproutreports下,然后在admin管理界面的plugin中安装插件,即可看到Sprout Reports菜单。

测试query

点击“Sprout Report”进入Report页面,新建“Custom Query”,然后输入测试内容:

select * from craft_content limit 10;

完成之后点击Report列表页面刚刚创建的Query,即可显示出Query对应的资料。

Craft CMS资料结构分析

一般情况下,导出资料的情形分为如下两种:

  • 导出某个section的所有资料
  • 导出某个entity type的所有资料

相关的数据库表如下:

  • craft_sections: 记录所有的section
  • craft_entrytypes: 记录所有entry type,其中sectionid栏位为craft_sections的外键
  • craft_entries: 记录所有的entry,其中typeid栏位为craft_entrytypes的外键
  • craft_content: 记录所有entry栏位的值,其中elementId为craft_entries的外键
  • craft_elements_i18n: 记录entry的slug和uri等信息,其中elementId为craft_entries的外键

Query语句获取section或entity typpe的资料

基于如上的分析,如果想要获取某个entity type的资料,query语句如下:

select
c.locale,
c.title,
c.field_body
from
craft_entrytypes et,
craft_entries e,
craft_content c
where
et.name='entitytype_name'
and e.typeId =  et.id
and e.id = c.elementId

想要获取某个section的资料,query语句如下:

select
c.locale,
c.title,
c.field_body
from
craft_sections s,
craft_entrytypes et,
craft_entries e,
craft_content c
where
s.name='section_name'
and e.typeId =  et.id
and e.id = c.elementId
and s.id = et.sectionid

是否可以通过entity type来自动获取其包含的栏位及值?

上面的query方式,需要根据不同的entity type来列出不同的column来获取资料,这样对于column不同的entity type,都需要手动列举column来导出资料,不是很方便。

获取entity type包含的栏位

  • craft_entrytypes的fieldLayoutId栏位记录了其包含的field布局
  • craft_fieldlayoutfields的layoutId栏位对应到craft_entrytypes的fieldLayoutId,fieldId为craft_fields的外键
  • craft_fields记录了所有栏位信息

获取某个entity type包含的所有栏位的query语句如下:

select et.name, f.name, f.handle from
craft_entrytypes et,
craft_fieldlayoutfields flf,
craft_fields f
where et.name='entitytype_name'
and et.fieldLayoutId = flf.layoutId
and flf.fieldId = f.id

生成query语句及利用临时表返回数据

DROP TABLE IF EXISTS execute_report; 

SET @fields = (select GROUP_CONCAT(CONCAT('c.field_', t1.handle) SEPARATOR ',')
FROM (select f.handle from
craft_entrytypes et,
craft_fieldlayoutfields flf,
craft_fields f
where et.name='entitytype_name'
and et.fieldLayoutId = flf.layoutId
and flf.fieldId = f.id
and f.`type` not in ('Matrix', 'Assets', 'Entries')) t1);

SET @query_string = CONCAT('Create TEMPORARY table execute_report ', '(select ', @fields,' from 
craft_sections s,
craft_entrytypes et,
craft_entries e,
craft_content c
where et.name="entitytype_name"
and e.typeId =  et.id
and e.id = c.elementId
and s.id = et.sectionid);');

PREPARE query FROM @query_string;
EXECUTE query;
select * from execute_report;

修改插件源码以支持execute

查看插件获取数据的源码,内容如下:

sproutreports\integrations\sproutreports\datasources\SproutReportsQueryDataSource.php
public function getResults(SproutReports_ReportModel &$report)
{
    $query = $report->getOption('query');

    try
    {
        return craft()->db->createCommand($query)->queryAll();
    }
    catch (\Exception $e)
    {
        $report->setResultsError($e->getMessage());
    }
}

修改为:

public function getResults(SproutReports_ReportModel &$report)
{
    $query = $report->getOption('query');

    try
    {
        //return craft()->db->createCommand($query)->queryAll();
        
        if(stristr($query, '#execute#'))
        {
            $query = str_ireplace('#execute#', '', $query);
            craft()->db->createCommand($query)->execute();
            
            return craft()->db->createCommand('select * from execute_report;')->queryAll();
        }
        else
        {
            return craft()->db->createCommand($query)->queryAll();
        }
    }
    catch (\Exception $e)
    {
        $report->setResultsError($e->getMessage());
    }
}

如果query语句包含#execute#,则以execute方式执行,否则按默认方式执行。

最终的query语句

#execute#
DROP TABLE IF EXISTS execute_report; 

SET @fields = (select GROUP_CONCAT(CONCAT('c.field_', t1.handle) SEPARATOR ',')
FROM (select f.handle from
craft_entrytypes et,
craft_fieldlayoutfields flf,
craft_fields f
where et.name='entitytype_name'
and et.fieldLayoutId = flf.layoutId
and flf.fieldId = f.id
and f.`type` not in ('Matrix', 'Assets', 'Entries')) t1);

SET @fields = CONCAT('c.locale, c.title, ', @fields);

SET @query_string = CONCAT('Create TEMPORARY table execute_report ', '(select ', @fields,' from 
craft_sections s,
craft_entrytypes et,
craft_entries e,
craft_content c
where et.name="entitytype_name"
and e.typeId =  et.id
and e.id = c.elementId
and s.id = et.sectionid);');

PREPARE query FROM @query_string;
EXECUTE query;
⚠️ **GitHub.com Fallback** ⚠️