Skip to content

Instantly share code, notes, and snippets.

@kinglozzer
Last active December 19, 2017 10:09
Show Gist options
  • Save kinglozzer/18c18344a94c5798d7127461e70e3cdd to your computer and use it in GitHub Desktop.
Save kinglozzer/18c18344a94c5798d7127461e70e3cdd to your computer and use it in GitHub Desktop.
FormCaptureAdmin:
extensions:
- FormCaptureAdminExtension
<?php
class FormCaptureAdminExtension extends Extension
{
public function updateEditForm(Form $form)
{
$gridField = $form->Fields()->dataFieldByName('CapturedFormSubmission');
$gridField->getConfig()->removeComponentsByType('GridFieldExportButton');
$gridField->getConfig()->removeComponentsByType('GridFieldPrintButton');
$gridField->getConfig()->addComponent(new FormCaptureGridFieldExportButton('buttons-before-left'));
}
}
<?php
use League\Csv\Writer;
use League\Flysystem\Filesystem;
use League\Flysystem\ZipArchive\ZipArchiveAdapter;
class FormCaptureGridFieldExportButton extends GridFieldExportButton
{
/**
* {@inheritdoc}
*/
public function handleExport($gridField, $request = null)
{
$now = date("d-m-Y-H-i");
$fileName = "export-$now.zip";
// We write multiple CSVs to a ZIP to be downloaded
$filesystem = new Filesystem(new ZipArchiveAdapter(TEMP_FOLDER . '/' . $fileName));
// Get a list of form types - each will have its own CSV file
$types = CapturedFormSubmission::get()->alterDataQuery(function ($dataQuery) {
$dataQuery->groupBy('Type');
})->column('Type');
foreach ($types as $type) {
$csv = Writer::createFromString('');
$submissions = CapturedFormSubmission::get()->filter(['Type' => $type]);
$fields = $submissions->relation('CapturedFields');
// Fetch header row data and push it to the CSV
$headers = $this->getHeadersForFields($fields);
$csv->insertOne($headers);
// We have no guarantees about field order matching the header order, so we have to extract
// all the fields into an array first and then check it against the headers later. Alternative
// would be to $submission->CapturedFields()->filter(['Type' => $header])... but that would
// result in thousands of extra queries and be incredibly slow
$rawData = $this->getDataFromFields($fields);
// Now we've got an array of _all_ the fields we received in an unknown order...
foreach ($rawData as $submissionID => $submissionData) {
$data = [];
// ...we can loop over it in the same order as the headers
foreach ($headers as $header) {
$data[] = isset($submissionData[$header]) ? $submissionData[$header] : null;
}
// Push the data to the CSV
$csv->insertOne($data);
}
// Add the CSV file to the ZIP
$filesystem->put($type . '.csv', $csv->__toString());
gc_collect_cycles();
}
// This has to be called manually as PHP's ZipArchive normally closes on __destruct()
$filesystem->getAdapter()->getArchive()->close();
// Grab the contents... using streams would be a nice enhancement
$contents = file_get_contents(TEMP_FOLDER . '/' . $fileName);
unlink(TEMP_FOLDER . '/' . $fileName);
if (!$contents) {
return null;
}
return SS_HTTPRequest::send_file($contents, $fileName, 'application/zip');
}
/**
* @param DataList $fields
* @return array
*/
protected function getHeadersForFields(DataList $fields)
{
// We don't know the headers, so we query them here
$headers = $fields->alterDataQuery(function ($dataQuery) {
$dataQuery->groupBy('Name'); // Quicker than fetching them all and doing array_unique()
})->column('Name');
// Try to put the headers in a sane order
$preferred = [
'PersonTitle', 'Title', 'FirstName', 'Surname', 'EmailAddress', 'TelephoneNumber',
'MobileNumber', 'Address1', 'Address2', 'Town', 'County', 'Postcode', 'Country'
];
usort($headers, function ($a, $b) use ($preferred) {
$aKey = array_search($a, $preferred);
$bKey = array_search($b, $preferred);
if ($aKey === false) return 1;
if ($bKey === false) return -1;
return ($aKey > $bKey) ? 1 : -1;
});
// Push "Created", as that's a useful field
array_unshift($headers, 'Created');
return $headers;
}
/**
* Build an array of data from submitted fields, grouped by submission ID
*
* @param DataList $fields
* @return array
*/
protected function getDataFromFields(DataList $fields)
{
// Use raw database values rather than casted DB fields - much, much faster
$query = $fields->dataQuery();
$data = [];
foreach ($query->execute() as $capturedField) {
$submissionID = $capturedField['FormSubmissionID'];
if (!isset($data[$submissionID])) {
// Bonus! Created date always comes first
$data[$submissionID] = [
'Created' => $capturedField['Created']
];
}
$data[$submissionID][$capturedField['Name']] = $capturedField['Value'];
}
return $data;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment