From 780f2d2b1645f876731e5f6f53958f9d1e4671f4 Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Tue, 29 May 2012 10:03:22 +1200 Subject: [PATCH 1/5] API CHANGE: Added DataQuery::aggregate() to allow for aggregates beyond Min, Max, Avg, Sum. --- model/DataQuery.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/model/DataQuery.php b/model/DataQuery.php index 2e5c66866..92272b44b 100644 --- a/model/DataQuery.php +++ b/model/DataQuery.php @@ -326,8 +326,8 @@ class DataQuery { * * @param String $field Unquoted database column name (will be escaped automatically) */ - function max($field) { - return $this->getFinalisedQuery()->aggregate(sprintf('MAX("%s")', Convert::raw2sql($field)))->execute()->value(); +function max($field) { + return $this->aggregate(sprintf('MAX("%s")', Convert::raw2sql($field))); } /** @@ -336,7 +336,7 @@ class DataQuery { * @param String $field Unquoted database column name (will be escaped automatically) */ function min($field) { - return $this->getFinalisedQuery()->aggregate(sprintf('MIN("%s")', Convert::raw2sql($field)))->execute()->value(); + return $this->aggregate(sprintf('MIN("%s")', Convert::raw2sql($field))); } /** @@ -345,7 +345,7 @@ class DataQuery { * @param String $field Unquoted database column name (will be escaped automatically) */ function avg($field) { - return $this->getFinalisedQuery()->aggregate(sprintf('AVG("%s")', Convert::raw2sql($field)))->execute()->value(); + return $this->aggregate(sprintf('AVG("%s")', Convert::raw2sql($field))); } /** @@ -354,7 +354,14 @@ class DataQuery { * @param String $field Unquoted database column name (will be escaped automatically) */ function sum($field) { - return $this->getFinalisedQuery()->aggregate(sprintf('SUM("%s")', Convert::raw2sql($field)))->execute()->value(); + return $this->aggregate(sprintf('SUM("%s")', Convert::raw2sql($field))); + } + + /** + * Runs a raw aggregate expression. Please handle escaping yourself + */ + function aggregate($expression) { + return $this->getFinalisedQuery()->aggregate($expression)->execute()->value(); } /** From 70d5ffefdd12eb3e69f406cf95fa90a8abb27d6d Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Tue, 29 May 2012 10:06:30 +1200 Subject: [PATCH 2/5] API CHANGE: Added GridField::addDataFields() to allow the definition of custom callbacks to be used by all GridField components. API CHANGE: Added GridField::getDataFieldValue() to encapsulate field lookup for all components. API CHANGE: Allow 'callback' key to be specified in a GridFieldDataColumn column info. In this case, title should be put as the 'title' key of a map rather than simply the column info. --- forms/gridfield/GridField.php | 29 +++++++++++++++++++++++ forms/gridfield/GridFieldDataColumns.php | 23 ++++++++++++++---- forms/gridfield/GridFieldExportButton.php | 2 +- forms/gridfield/GridFieldPrintButton.php | 2 +- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/forms/gridfield/GridField.php b/forms/gridfield/GridField.php index 0829ed0f9..ae4486dae 100755 --- a/forms/gridfield/GridField.php +++ b/forms/gridfield/GridField.php @@ -56,6 +56,11 @@ class GridField extends FormField { * @var array */ protected $columnDispatch = null; + + /** + * Map of callbacks for custom data fields + */ + protected $customDataFields = array(); protected $name = ''; @@ -411,6 +416,30 @@ class GridField extends FormField { throw new InvalidArgumentException("Bad column '$column'"); } } + + /** + * Add additional calculated data fields to be used on this GridField + * @param array $fields a map of fieldname to callback. The callback will bed passed the record as an argument. + */ + public function addDataFields($fields) { + if($this->customDataFields) $this->customDataFields = array_merge($this->customDataFields, $fields); + else $this->customDataFields = $fields; + } + + /** + * Get the value of a named field on the given record. + * Use of this method ensures that any special rules around the data for this gridfield are followed. + */ + public function getDataFieldValue($record, $fieldName) { + // Custom callbacks + if(isset($this->customDataFields[$fieldName])) { + $callback = $this->customDataFields[$fieldName]; + return $callback($record); + } + + // Default implementation + return $record->relField($fieldName); + } /** * Get extra columns attributes used as HTML attributes diff --git a/forms/gridfield/GridFieldDataColumns.php b/forms/gridfield/GridFieldDataColumns.php index 8550fb78d..d7c87f64f 100644 --- a/forms/gridfield/GridFieldDataColumns.php +++ b/forms/gridfield/GridFieldDataColumns.php @@ -118,11 +118,18 @@ class GridFieldDataColumns implements GridField_ColumnProvider { * @return string HTML for the column. Return NULL to skip. */ public function getColumnContent($gridField, $record, $columnName) { + // Find the data column for the given named column + $columns = $this->getDisplayFields($gridField); + $columnInfo = $columns[$columnName]; + + // Allow callbacks + if(is_array($columnInfo) && isset($columnInfo['callback'])) { + $method = $columnInfo['callback']; + $value = Convert::raw2xml($method($record)); + // This supports simple FieldName syntax - if(strpos($columnName, '.') === false) { - $value = $record->XML_val($columnName); } else { - $value = $this->getValueFromRelation($record, $columnName); + $value = Convert::raw2xml($gridField->getDataFieldValue($record, $columnName)); } $value = $this->castValue($gridField, $columnName, $value); @@ -154,8 +161,16 @@ class GridFieldDataColumns implements GridField_ColumnProvider { */ public function getColumnMetadata($gridField, $column) { $columns = $this->getDisplayFields($gridField); + + $title = null; + if(is_string($columns[$column])) { + $title = $columns[$column]; + } else if(is_array($columns[$column]) && isset($columns[$column]['title'])) { + $title = $columns[$column]['title']; + } + return array( - 'title' => $columns[$column], + 'title' => $title, ); } diff --git a/forms/gridfield/GridFieldExportButton.php b/forms/gridfield/GridFieldExportButton.php index eda95b0d3..23e39bf36 100644 --- a/forms/gridfield/GridFieldExportButton.php +++ b/forms/gridfield/GridFieldExportButton.php @@ -138,7 +138,7 @@ class GridFieldExportButton implements GridField_HTMLProvider, GridField_ActionP $value = $columnHeader($relObj); } else { - $value = $item->relField($columnSource); + $value = $gridField->getDataFieldValue($item, $columnSource); } $value = str_replace(array("\r", "\n"), "\n", $value); diff --git a/forms/gridfield/GridFieldPrintButton.php b/forms/gridfield/GridFieldPrintButton.php index 884c6b616..e8dea9c3a 100644 --- a/forms/gridfield/GridFieldPrintButton.php +++ b/forms/gridfield/GridFieldPrintButton.php @@ -115,7 +115,7 @@ class GridFieldPrintButton implements GridField_HTMLProvider, GridField_ActionPr foreach($items as $item) { $itemRow = new ArrayList(); foreach($printColumns as $field => $label) { - $value = $item->relField($field); + $value = $gridField->getDataFieldValue($item, $field); $itemRow->push( new ArrayData(array( "CellString" => $value, From 0821f021cc41ab97cec392d39d84ce9d0c51dbb6 Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Tue, 29 May 2012 10:07:09 +1200 Subject: [PATCH 3/5] API CHANGE: Added DataList::filterByCallback() to allow filtering by PHP code. --- model/DataList.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/model/DataList.php b/model/DataList.php index 896e62ee3..a4b5d43c9 100644 --- a/model/DataList.php +++ b/model/DataList.php @@ -256,6 +256,24 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab return $this; } + /** + * Filter this DataList by a callback function. + * The function will be passed each record of the DataList in turn, and must return true for the record to be included. + * Returns the filtered list. + * + * Note that, in the current implementation, the filtered list will be an ArrayList, but this may change in a future + * implementation. + */ + public function filterByCallback($callback) { + if(!is_callable($callback)) throw new LogicException("DataList::filterByCallback() must be passed something callable."); + + $output = new ArrayList; + foreach($this as $item) { + if($callback($item)) $output->push($item); + } + return $output; + } + /** * Translates a Object relation name to a Database name and apply the relation join to * the query. Throws an InvalidArgumentException if the $field doesn't correspond to a relation From e19ab7aa5acf39b84f4f7b4f45300617c97acf79 Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Wed, 30 May 2012 11:44:20 +1200 Subject: [PATCH 4/5] BUGFIX: Ensure that DataList::filter() doesn't modify the original object. --- model/DataList.php | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/model/DataList.php b/model/DataList.php index a4b5d43c9..b2f7c090c 100644 --- a/model/DataList.php +++ b/model/DataList.php @@ -220,18 +220,26 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab * @return DataList */ public function filter() { - $numberFuncArgs = count(func_get_args()); - $whereArguments = array(); - if($numberFuncArgs == 1 && is_array(func_get_arg(0))) { - $whereArguments = func_get_arg(0); - } elseif($numberFuncArgs == 2) { - $whereArguments[func_get_arg(0)] = func_get_arg(1); - } else { - throw new InvalidArgumentException('Incorrect number of arguments passed to filter()'); + // Validate and process arguments + $arguments = func_get_args(); + switch(sizeof($arguments)) { + case 1: $filters = $arguments[0]; break; + case 2: $filters = array($arguments[0] => $arguments[1]); break; + default: + throw new InvalidArgumentException('Incorrect number of arguments passed to filter()'); } + + $clone = clone $this; + $clone->addFilter($filters); + return $clone; + } + /** + * Modify this DataList, adding a filter + */ + public function addFilter($filterArray) { $SQL_Statements = array(); - foreach($whereArguments as $field => $value) { + foreach($filterArray as $field => $value) { if(is_array($value)) { $customQuery = 'IN (\''.implode('\',\'',Convert::raw2sql($value)).'\')'; } else { From 948ca468f63d0ee3d0b9d8a934a54be322e4a93e Mon Sep 17 00:00:00 2001 From: Sam Minnee Date: Wed, 30 May 2012 11:46:32 +1200 Subject: [PATCH 5/5] API CHANGE: Added $value argument as the first argument of a gridfield formattting callback. --- forms/gridfield/GridFieldDataColumns.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forms/gridfield/GridFieldDataColumns.php b/forms/gridfield/GridFieldDataColumns.php index d7c87f64f..fe858c07b 100644 --- a/forms/gridfield/GridFieldDataColumns.php +++ b/forms/gridfield/GridFieldDataColumns.php @@ -227,7 +227,7 @@ class GridFieldDataColumns implements GridField_ColumnProvider { $spec = $this->fieldFormatting[$fieldName]; if(is_callable($spec)) { - return $spec($item); + return $spec($value, $item); } else { $format = str_replace('$value', "__VAL__", $spec); $format = preg_replace('/\$([A-Za-z0-9-_]+)/', '$item->$1', $format);