|
<?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; |
|
} |
|
} |