Skip to content

Commit

Permalink
Merge tag '0.2.0'
Browse files Browse the repository at this point in the history
* Updated ExcelDataFormatter::addRow() and ExcelDataFormatter::getFieldsForObj() so that DataObjects can suggest a default set of Fields to Export.
* Added the ability for the ExcelDataFormatter to use field labels instead of just displaying the Database Field name.
* Added a change log.
* Updated the ReadMe with information about how to choose the column and customise the headers.
* Generalise the logic on the GridFieldExcelExportButton to minimise code duplication between the XLSX, XLS and CSV action.
* Update GridFieldExcelExportAction and GridFieldExcelExportButton so we can set the UseLabelsAsHeaders flag on the underlying ExcelDataFormatter.
  • Loading branch information
maxime-rainville committed May 25, 2016
2 parents 592be60 + 4d8f89c commit d65bccd
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 27 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Tag 0.2.0

## Wednesday, May 25, 2016
* Updated ExcelDataFormatter::addRow() and ExcelDataFormatter::getFieldsForObj() so that DataObjects can suggest a default set of Fields to Export.
* Added the ability for the ExcelDataFormatter to use field labels instead of just displaying the Database Field name.
* Added a change log.
* Updated the ReadMe with information about how to choose the column and customise the headers.
* Generalise the logic on the GridFieldExcelExportButton to minimise code duplication between the XLSX, XLS and CSV action.
* Update GridFieldExcelExportAction and GridFieldExcelExportButton so we can set the UseLabelsAsHeaders flag on the underlying ExcelDataFormatter.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,65 @@ Exporting your data is just as easy as entering a URL.
* Drill down into relationships: *http://localhost/api/v1/Tag/127/Articles.xlsx*

[SilverStripe RestfulServer Module Supported operations](https://github.com/silverstripe/silverstripe-restfulserver#supported-operations)

## Customising the output

There's 2 ways you can control the output:
* Choose which fields to output ;
* Choose to use field label instead of fields names in the headers.

### Choose which fields to output
Because the `ExcelDataFormatter` extends [DataFormatter](http://api.silverstripe.org/3.3/class-DataFormatter.html), you can use methods like `setCustomFields()`, `setCustomAddFields()` or `setRemoveFields()` to control what fields will be present in the spread sheet.

```
$formatter = new ExcelDataFormatter();
// This formatter instead of returning every field of a DataObject, will only return 3 fields.
$formatter->setCustomFields(['ID', 'Title', 'LastEdited']);
// If youe DataObject has dynamic properties, you can reference them using setCustomAddFields().
$formatter->setCustomAddFields(['ChildrenCount']);
```

#### Defining a default column set
You can customise the default column set that will be return for a specific DataObject class by defining a `getExcelExportFields()` method on your DataOject class.

This `getExcelExportFields()` method should return an array of fields following the same format used by `DataObject::inheritedDatabaseFields()`:
```
return [
'ID' => 'Int',
'Name' => 'Varchar',
'Address' => 'Text'
];
```

You may also reference relationships in this array or dynamic properties:
```
return [
'Owner.Name' => 'Varchar',
'Category.Title' => 'Varchar',
'ChildrenCount' => 'Int',
];
```

This will also allow you to control the order the fields appear in the Spread Sheet. Note that ID will always be the first field and cannot be removed.

This behavior can be overriden for specific instances of `ExcelDataFormatter` by calling the `setCustomFields()` method.

## Use field labels or field names as column headers
Out of the box, the actual field names will be used as column header. (e.g.: `FirstName` rather than `First Name`).

You can customise this behavior and use the Field Labels as define on your DataObject class instead. When generating the header row, `ExcelDataFormatter` will call the `fieldLabel()` method on your Data Object to decide what string to use in each header.

### Change the default for all `ExcelDataFormatter`
In you YML config, you can use the following syntax to change the default headers.
```
ExcelDataFormatter:
UseLabelsAsHeaders: true
```

### Override the default for a specific instance
You may change the default behavior for a specific instance.
```
$formatter->setUseLabelsAsHeaders(true);
```
105 changes: 97 additions & 8 deletions code/ExcelDataFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ class ExcelDataFormatter extends DataFormatter

private static $api_base = "api/v1/";

/**
* Determined what we will use as headers for the spread sheet.
* @var bool
*/
protected $useLabelsAsHeaders = null;

/**
* @inheritdoc
*/
Expand Down Expand Up @@ -72,14 +78,42 @@ protected function setHeader()
/**
* @inheritdoc
*/
protected function getFieldsForObj($do)
protected function getFieldsForObj($obj)
{
$fields = parent::getFieldsForObj($do);
$dbFields = array();

// if custom fields are specified, only select these
if(is_array($this->customFields)) {
foreach($this->customFields as $fieldName) {
// @todo Possible security risk by making methods accessible - implement field-level security
if($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) {
$dbFields[$fieldName] = $fieldName;
}
}
} elseif ($obj->hasMethod('getExcelExportFields')) {
$dbFields = $obj->getExcelExportFields();
} else {
// by default, all database fields are selected
$dbFields = $obj->inheritedDatabaseFields();
}

if(is_array($this->customAddFields)) {
foreach($this->customAddFields as $fieldName) {
// @todo Possible security risk by making methods accessible - implement field-level security
if($obj->hasField($fieldName) || $obj->hasMethod("get{$fieldName}")) {
$dbFields[$fieldName] = $fieldName;
}
}
}

// Make sure our ID field is the first one.
$fields = array('ID' => $fields['ID']) + $fields;
$dbFields = array('ID' => 'Int') + $dbFields;

if(is_array($this->removeFields)) {
$dbFields = array_diff_key($dbFields, array_combine($this->removeFields,$this->removeFields));
}

return $fields;
return $dbFields;
}

/**
Expand All @@ -102,7 +136,7 @@ public function getPhpExcelObject(SS_List $set)
if ($first) {
// Set up the header row
$fields = $this->getFieldsForObj($first);
$this->headerRow($sheet, $fields);
$this->headerRow($sheet, $fields, $first);

// Add a new row for each DataObject
foreach ($set as $item) {
Expand Down Expand Up @@ -173,17 +207,21 @@ protected function setupExcel(DataObjectInterface $do)
* Add an header row to a {@link PHPExcel_Worksheet}.
* @param PHPExcel_Worksheet $sheet
* @param array $fields List of fields
* @param DataObjectInterface $do
* @return PHPExcel_Worksheet
*/
protected function headerRow(PHPExcel_Worksheet &$sheet, array $fields)
protected function headerRow(PHPExcel_Worksheet &$sheet, array $fields, DataObjectInterface $do)
{
// Counter
$row = 1;
$col = 0;

$useLabelsAsHeaders = $this->getUseLabelsAsHeaders();

// Add each field to the first row
foreach ($fields as $field => $type) {
$sheet->setCellValueByColumnAndRow($col, $row, $field);
$header = $useLabelsAsHeaders ? $do->fieldLabel($field) : $field;
$sheet->setCellValueByColumnAndRow($col, $row, $header);
$col++;
}

Expand Down Expand Up @@ -216,7 +254,13 @@ protected function addRow(
$col = 0;

foreach ($fields as $field => $type) {
$sheet->setCellValueByColumnAndRow($col, $row, $item->$field);
if ($item->hasField($field) || $item->hasMethod("get{$field}")) {
$value = $item->$field;
} else {
$viewer = SSViewer::fromString('$' . $field . '.RAW');
$value = $item->renderWith($viewer, true);
}
$sheet->setCellValueByColumnAndRow($col, $row, $value);
$col++;
}

Expand All @@ -241,4 +285,49 @@ protected function getFileData(PHPExcel $excel, $format)

return $fileData;
}

/**
* Accessor for UseLabelsAsHeaders. If this is `true`, the data formatter will call {@link DataObject::fieldLabel()} to pick the header strings. If it's set to false, it will use the raw field name.
*
* You can define this for a specific ExcelDataFormatter instance with `setUseLabelsAsHeaders`. You can set the default for all ExcelDataFormatter instance in your YML config file:
*
* ```
* ExcelDataFormatter:
* UseLabelsAsHeaders: true
* ```
*
* Otherwise, the data formatter will default to false.
*
* @return bool
*/
public function getUseLabelsAsHeaders()
{
if ($this->useLabelsAsHeaders !== null) {
return $this->useLabelsAsHeaders;
}

$useLabelsAsHeaders = static::config()->UseLabelsAsHeaders;
if ($useLabelsAsHeaders !== null) {
return $useLabelsAsHeaders;
}

return false;
}

/**
* Setter for UseLabelsAsHeaders. If this is `true`, the data formatter will call {@link DataObject::fieldLabel()} to pick the header strings. If it's set to false, it will use the raw field name.
*
* If `$value` is `null`, the data formatter will fall back on whatevr the default is.
* @param bool $value
* @return ExcelDataFormatter
*/
public function setUseLabelsAsHeaders($value)
{
if ($value === null) {
$this->useLabelsAsHeaders = null;
} else {
$this->useLabelsAsHeaders = (bool)$value;
}
return $this;
}
}
36 changes: 35 additions & 1 deletion code/GridFieldExcelExportAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ class GridFieldExcelExportAction implements GridField_ColumnProvider, GridField_
*/
protected $exportType;

/**
* Whatever to override the default $useFieldLabelsAsHeaders value for the DataFormatter.
* @var bool
*/
protected $useLabelsAsHeaders = null;


/**
* Instanciate a new GridFieldExcelExportAction
Expand Down Expand Up @@ -127,7 +133,7 @@ public function handleAction(GridField $gridField, $actionName, $arguments, $dat
if ($title) {
$filename .= ' - ' . $title;
}

// Pick Converter
switch ($this->exportType) {
case 'xlsx':
Expand All @@ -151,6 +157,7 @@ public function handleAction(GridField $gridField, $actionName, $arguments, $dat
$this->setHeader($gridField, $this->exportType, $filename);

// Export our Data Object
$formater->setUseLabelsAsHeaders($this->useLabelsAsHeaders);
$fileData = $formater->convertDataObject($item);

return $fileData;
Expand Down Expand Up @@ -178,4 +185,31 @@ protected function setHeader($gridField, $ext, $filename = '')
'.' . $ext . '"'
);
}

/**
* Set the DataFormatter's UseFieldLabelsAsHeaders property
* @param bool $value
* @return GridFieldExcelExportButton
*/
public function setUseLabelsAsHeaders($value)
{
if ($value === null) {
$this->useLabelsAsHeaders = null;
} else {
$this->useLabelsAsHeaders = (bool)$value;
}
return $this;
}

/**
* Return the value that will be assigned to the DataFormatter's UseFieldLabelsAsHeaders property
*
* If null, will fallback on the default.
*
* @return bool|null
*/
public function getUseLabelsAsHeaders()
{
return $this->useLabelsAsHeaders;
}
}
Loading

0 comments on commit d65bccd

Please sign in to comment.