mirror of
https://github.com/silverstripe/silverstripe-userforms.git
synced 2024-10-22 17:05:42 +02:00
FIX submission performance issues with large data
The more submissions a form receives, the more submission fields it must process just to be able to load `getCMSFields`. Arguably submission data does not belong here, but this is beyond the scope of this patch. On popular forms it is not improbable to be trying to process 300,000 submitted fields just to test the unique sets of name and title... however databases have the ability to do this without wasting PHP cycles and memory, leaving us with a much smaller set to process and hopefully bypassing one (of several) performance issues with this module. The consequence of not making allowance for this is that a page in the CMS suddenly stops saving or loading via web server or PHP (or both) process timeouts (e.g. saving takes longer than 30 seconds so saving never happens).
This commit is contained in:
parent
198466a979
commit
3cd8c7ea77
@ -145,6 +145,10 @@ class EditableFormField extends DataObject
|
||||
'ShowOnLoad' => true,
|
||||
];
|
||||
|
||||
private static $indexes = [
|
||||
'Name' => 'Name',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* @config
|
||||
|
@ -34,6 +34,10 @@ class SubmittedFormField extends DataObject
|
||||
|
||||
private static $table_name = 'SubmittedFormField';
|
||||
|
||||
private static $indexes = [
|
||||
'Name' => 'Name',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Member $member
|
||||
* @param array $context
|
||||
|
@ -193,7 +193,6 @@ trait UserForm
|
||||
// define tabs
|
||||
$fields->findOrMakeTab('Root.FormOptions', _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.CONFIGURATION', 'Configuration'));
|
||||
$fields->findOrMakeTab('Root.Recipients', _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.RECIPIENTS', 'Recipients'));
|
||||
$fields->findOrMakeTab('Root.Submissions', _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SUBMISSIONS', 'Submissions'));
|
||||
|
||||
|
||||
// text to show on complete
|
||||
@ -237,87 +236,8 @@ trait UserForm
|
||||
$fields->addFieldToTab('Root.Recipients', $emailRecipients);
|
||||
$fields->addFieldsToTab('Root.FormOptions', $this->getFormOptions());
|
||||
|
||||
|
||||
// view the submissions
|
||||
// make sure a numeric not a empty string is checked against this int column for SQL server
|
||||
$parentID = (!empty($this->ID)) ? (int) $this->ID : 0;
|
||||
|
||||
// get a list of all field names and values used for print and export CSV views of the GridField below.
|
||||
$columnSQL = <<<SQL
|
||||
SELECT "SubmittedFormField"."Name" as "Name", COALESCE("EditableFormField"."Title", "SubmittedFormField"."Title") as "Title", COALESCE("EditableFormField"."Sort", 999) AS "Sort"
|
||||
FROM "SubmittedFormField"
|
||||
LEFT JOIN "SubmittedForm" ON "SubmittedForm"."ID" = "SubmittedFormField"."ParentID"
|
||||
LEFT JOIN "EditableFormField" ON "EditableFormField"."Name" = "SubmittedFormField"."Name" AND "EditableFormField"."ParentID" = '$parentID'
|
||||
WHERE "SubmittedForm"."ParentID" = '$parentID'
|
||||
ORDER BY "Sort", "Title"
|
||||
SQL;
|
||||
// Sanitise periods in title
|
||||
$columns = array();
|
||||
|
||||
foreach (DB::query($columnSQL)->map() as $name => $title) {
|
||||
$columns[$name] = trim(strtr($title ?? '', '.', ' '));
|
||||
}
|
||||
|
||||
$config = GridFieldConfig::create();
|
||||
$config->addComponent(new GridFieldToolbarHeader());
|
||||
$config->addComponent($sort = new GridFieldSortableHeader());
|
||||
$config->addComponent($filter = new UserFormsGridFieldFilterHeader());
|
||||
$config->addComponent(new GridFieldDataColumns());
|
||||
$config->addComponent(new GridFieldEditButton());
|
||||
$config->addComponent(new GridFieldDeleteAction());
|
||||
$config->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
||||
$config->addComponent($pagination = new GridFieldPaginator(25));
|
||||
$config->addComponent(new GridFieldDetailForm(null, true, false));
|
||||
$config->addComponent(new GridFieldButtonRow('after'));
|
||||
$config->addComponent($export = new GridFieldExportButton('buttons-after-left'));
|
||||
$config->addComponent($print = new GridFieldPrintButton('buttons-after-left'));
|
||||
|
||||
// show user form items in the summary tab
|
||||
$summaryarray = array(
|
||||
'ID' => 'ID',
|
||||
'Created' => 'Created',
|
||||
'LastEdited' => 'Last Edited'
|
||||
);
|
||||
|
||||
foreach (EditableFormField::get()->filter(array('ParentID' => $parentID)) as $eff) {
|
||||
if ($eff->ShowInSummary) {
|
||||
$summaryarray[$eff->Name] = $eff->Title ?: $eff->Name;
|
||||
}
|
||||
}
|
||||
|
||||
$config->getComponentByType(GridFieldDataColumns::class)->setDisplayFields($summaryarray);
|
||||
|
||||
/**
|
||||
* Support for {@link https://github.com/colymba/GridFieldBulkEditingTools}
|
||||
*/
|
||||
if (class_exists(BulkManager::class)) {
|
||||
$config->addComponent(new BulkManager);
|
||||
}
|
||||
|
||||
$sort->setThrowExceptionOnBadDataType(false);
|
||||
$filter->setThrowExceptionOnBadDataType(false);
|
||||
$pagination->setThrowExceptionOnBadDataType(false);
|
||||
|
||||
// attach every column to the print view form
|
||||
$columns['Created'] = 'Created';
|
||||
$columns['SubmittedBy.Email'] = 'Submitter';
|
||||
$filter->setColumns($columns);
|
||||
|
||||
// print configuration
|
||||
|
||||
$print->setPrintHasHeader(true);
|
||||
$print->setPrintColumns($columns);
|
||||
|
||||
// export configuration
|
||||
$export->setCsvHasHeader(true);
|
||||
$export->setExportColumns($columns);
|
||||
|
||||
$submissions = GridField::create(
|
||||
'Submissions',
|
||||
'',
|
||||
$this->Submissions()->sort('Created', 'DESC'),
|
||||
$config
|
||||
);
|
||||
$submissions = $this->getSubmissionsGridField();
|
||||
$fields->findOrMakeTab('Root.Submissions', _t('SilverStripe\\UserForms\\Model\\UserDefinedForm.SUBMISSIONS', 'Submissions'));
|
||||
$fields->addFieldToTab('Root.Submissions', $submissions);
|
||||
$fields->addFieldToTab(
|
||||
'Root.FormOptions',
|
||||
@ -344,6 +264,88 @@ SQL;
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function getSubmissionsGridField()
|
||||
{
|
||||
// view the submissions
|
||||
// make sure a numeric not a empty string is checked against this int column for SQL server
|
||||
$parentID = (!empty($this->ID)) ? (int) $this->ID : 0;
|
||||
|
||||
// get a list of all field names and values used for print and export CSV views of the GridField below.
|
||||
$columnSQL = <<<SQL
|
||||
SELECT DISTINCT
|
||||
"SubmittedFormField"."Name" as "Name",
|
||||
REPLACE(COALESCE("EditableFormField"."Title", "SubmittedFormField"."Title"), '.', ' ') as "Title",
|
||||
COALESCE("EditableFormField"."Sort", 999) AS "Sort"
|
||||
FROM "SubmittedFormField"
|
||||
LEFT JOIN "SubmittedForm" ON "SubmittedForm"."ID" = "SubmittedFormField"."ParentID"
|
||||
LEFT JOIN "EditableFormField" ON "EditableFormField"."Name" = "SubmittedFormField"."Name"
|
||||
WHERE "SubmittedForm"."ParentID" = '$parentID'
|
||||
AND "EditableFormField"."ParentID" = '$parentID'
|
||||
ORDER BY "Sort", "Title"
|
||||
SQL;
|
||||
|
||||
$columns = DB::query($columnSQL)->map();
|
||||
|
||||
$config = GridFieldConfig::create();
|
||||
$config->addComponent(new GridFieldToolbarHeader());
|
||||
$config->addComponent($sort = new GridFieldSortableHeader());
|
||||
$config->addComponent($filter = new UserFormsGridFieldFilterHeader());
|
||||
$config->addComponent(new GridFieldDataColumns());
|
||||
$config->addComponent(new GridFieldEditButton());
|
||||
$config->addComponent(new GridFieldDeleteAction());
|
||||
$config->addComponent(new GridFieldPageCount('toolbar-header-right'));
|
||||
$config->addComponent($pagination = new GridFieldPaginator(25));
|
||||
$config->addComponent(new GridFieldDetailForm(null, true, false));
|
||||
$config->addComponent(new GridFieldButtonRow('after'));
|
||||
$config->addComponent($export = new GridFieldExportButton('buttons-after-left'));
|
||||
$config->addComponent($print = new GridFieldPrintButton('buttons-after-left'));
|
||||
|
||||
// show user form items in the summary tab
|
||||
$summaryarray = array(
|
||||
'ID' => 'ID',
|
||||
'Created' => 'Created',
|
||||
'LastEdited' => 'Last Edited'
|
||||
);
|
||||
|
||||
foreach (EditableFormField::get()->filter(['ParentID' => $parentID, 'ShowInSummary' => 1]) as $eff) {
|
||||
$summaryarray[$eff->Name] = $eff->Title ?: $eff->Name;
|
||||
}
|
||||
|
||||
$config->getComponentByType(GridFieldDataColumns::class)->setDisplayFields($summaryarray);
|
||||
|
||||
/**
|
||||
* Support for {@link https://github.com/colymba/GridFieldBulkEditingTools}
|
||||
*/
|
||||
if (class_exists(BulkManager::class)) {
|
||||
$config->addComponent(new BulkManager);
|
||||
}
|
||||
|
||||
$sort->setThrowExceptionOnBadDataType(false);
|
||||
$filter->setThrowExceptionOnBadDataType(false);
|
||||
$pagination->setThrowExceptionOnBadDataType(false);
|
||||
|
||||
// attach every column to the print view form
|
||||
$columns['Created'] = 'Created';
|
||||
$columns['SubmittedBy.Email'] = 'Submitter';
|
||||
$filter->setColumns($columns);
|
||||
|
||||
// print configuration
|
||||
$print->setPrintHasHeader(true);
|
||||
$print->setPrintColumns($columns);
|
||||
|
||||
// export configuration
|
||||
$export->setCsvHasHeader(true);
|
||||
$export->setExportColumns($columns);
|
||||
|
||||
$submissions = GridField::create(
|
||||
'Submissions',
|
||||
'',
|
||||
$this->Submissions()->sort('Created', 'DESC'),
|
||||
$config
|
||||
);
|
||||
return $submissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow overriding the EmailRecipients on a {@link DataExtension}
|
||||
* so you can customise who receives an email.
|
||||
|
Loading…
Reference in New Issue
Block a user