Skip to content

Commit

Permalink
Merge pull request #9190 from open-sausages/pulls/4/test-state
Browse files Browse the repository at this point in the history
Don't include default value in url grid state
  • Loading branch information
bergice authored Nov 17, 2020
2 parents bdb2deb + 5226d96 commit 4607151
Show file tree
Hide file tree
Showing 11 changed files with 297 additions and 34 deletions.
20 changes: 18 additions & 2 deletions src/Forms/GridField/GridField.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,6 @@ public function __construct($name, $title = null, SS_List $dataList = null, Grid

$this->setConfig($config);

$this->state = new GridState($this);

$this->addExtraClass('grid-field');
}

Expand Down Expand Up @@ -407,13 +405,31 @@ public function getManipulatedList()
*/
public function getState($getData = true)
{
// Initialise state on first call. This ensures it's evaluated after components have been added
if (!$this->state) {
$this->initState();
}

if ($getData) {
return $this->state->getData();
}

return $this->state;
}

private function initState(): void
{
$this->state = new GridState($this);

$data = $this->state->getData();

foreach ($this->getComponents() as $item) {
if ($item instanceof GridField_StateProvider) {
$item->initDefaultState($data);
}
}
}

/**
* Returns the whole gridfield rendered with all the attached components.
*
Expand Down
29 changes: 21 additions & 8 deletions src/Forms/GridField/GridFieldFilterHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
*
* @see GridField
*/
class GridFieldFilterHeader implements GridField_URLHandler, GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider
class GridFieldFilterHeader implements GridField_URLHandler, GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
{
/**
* See {@link setThrowExceptionOnBadDataType()}
Expand Down Expand Up @@ -173,8 +173,8 @@ public function handleAction(GridField $gridField, $actionName, $arguments, $dat
return;
}

$state = $gridField->State->GridFieldFilterHeader;
$state->Columns = null;
$state = $this->getState($gridField);

if ($actionName === 'filter') {
if (isset($data['filter'][$gridField->getName()])) {
foreach ($data['filter'][$gridField->getName()] as $key => $filter) {
Expand All @@ -184,6 +184,20 @@ public function handleAction(GridField $gridField, $actionName, $arguments, $dat
}
}

/**
* Extract state data from the parent gridfield
* @param GridField $gridField
* @return GridState_Data
*/
private function getState(GridField $gridField): GridState_Data
{
return $gridField->State->GridFieldFilterHeader;
}

public function initDefaultState(GridState_Data $data): void
{
$data->GridFieldFilterHeader->initDefaults(['Columns' => []]);
}

/**
* @inheritDoc
Expand All @@ -195,13 +209,12 @@ public function getManipulatedData(GridField $gridField, SS_List $dataList)
}

/** @var Filterable $dataList */
/** @var GridState_Data $columns */
$columns = $gridField->State->GridFieldFilterHeader->Columns(null);
if (empty($columns)) {
/** @var array $filterArguments */
$filterArguments = $this->getState($gridField)->Columns->toArray();
if (empty($filterArguments)) {
return $dataList;
}

$filterArguments = $columns->toArray();
$dataListClone = clone($dataList);
$results = $this->getSearchContext($gridField)
->getQuery($filterArguments, false, false, $dataListClone);
Expand Down Expand Up @@ -413,7 +426,7 @@ public function getLegacyFilterHeader(GridField $gridField)
}

$columns = $gridField->getColumns();
$filterArguments = $gridField->State->GridFieldFilterHeader->Columns->toArray();
$filterArguments = $this->getState($gridField)->Columns->toArray();
$currentColumn = 0;
$canFilter = false;
$fieldsList = new ArrayList();
Expand Down
16 changes: 9 additions & 7 deletions src/Forms/GridField/GridFieldPaginator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* GridFieldPaginator paginates the {@link GridField} list and adds controls
* to the bottom of the {@link GridField}.
*/
class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider
class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
{
use Configurable;

Expand Down Expand Up @@ -140,13 +140,15 @@ public function handleAction(GridField $gridField, $actionName, $arguments, $dat
*/
protected function getGridPagerState(GridField $gridField)
{
$state = $gridField->State->GridFieldPaginator;

// Force the state to the initial page if none is set
$state->currentPage(1);
$state->itemsPerPage($this->getItemsPerPage());
return $gridField->State->GridFieldPaginator;
}

return $state;
public function initDefaultState(GridState_Data $data): void
{
$data->GridFieldPaginator->initDefaults([
'currentPage' => 1,
'itemsPerPage' => $this->getItemsPerPage()
]);
}

/**
Expand Down
23 changes: 19 additions & 4 deletions src/Forms/GridField/GridFieldSortableHeader.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*
* @see GridField
*/
class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider
class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider, GridField_StateProvider
{

/**
Expand Down Expand Up @@ -119,7 +119,7 @@ public function getHTMLFragments($gridField)
$forTemplate = new ArrayData([]);
$forTemplate->Fields = new ArrayList;

$state = $gridField->State->GridFieldSortableHeader;
$state = $this->getState($gridField);
$columns = $gridField->getColumns();
$currentColumn = 0;

Expand Down Expand Up @@ -236,7 +236,7 @@ public function handleAction(GridField $gridField, $actionName, $arguments, $dat
return;
}

$state = $gridField->State->GridFieldSortableHeader;
$state = $this->getState($gridField);
switch ($actionName) {
case 'sortasc':
$state->SortColumn = $arguments['SortColumn'];
Expand Down Expand Up @@ -266,11 +266,26 @@ public function getManipulatedData(GridField $gridField, SS_List $dataList)
}

/** @var Sortable $dataList */
$state = $gridField->State->GridFieldSortableHeader;
$state = $this->getState($gridField);
if ($state->SortColumn == "") {
return $dataList;
}

return $dataList->sort($state->SortColumn, $state->SortDirection('asc'));
}

/**
* Extract state data from the parent gridfield
* @param GridField $gridField
* @return GridState_Data
*/
private function getState(GridField $gridField): GridState_Data
{
return $gridField->State->GridFieldSortableHeader;
}

public function initDefaultState(GridState_Data $data): void
{
$data->GridFieldSortableHeader->initDefaults(['SortColumn' => null, 'SortDirection' => 'asc']);
}
}
5 changes: 5 additions & 0 deletions src/Forms/GridField/GridFieldStateManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public function addStateToURL(GridField $gridField, string $url): string
$key = $this->getStateKey($gridField);
$value = $gridField->getState(false)->Value();

// Using a JSON-encoded empty array as the blank value, to avoid changing Value() semantics in a minor release
if ($value === '{}') {
return $url;
}

return HTTP::setGetVar($key, $value, $url);
}

Expand Down
21 changes: 21 additions & 0 deletions src/Forms/GridField/GridField_StateProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace SilverStripe\Forms\GridField;

/**
* A GridField component that provides state, notably default state.
*
* Implementation of this interface is optional; without it, no default state is assumed.
* The benefit of default state is that it won't be included in URLs, keeping URLs tidier.
*/
interface GridField_StateProvider extends GridFieldComponent
{
/**
* Initialise the default state in the given GridState_Data
*
* We recommend that you call $data->initDefaults() to do this.
*
* @param $data The top-level sate object
*/
public function initDefaultState(GridState_Data $data): void;
}
25 changes: 17 additions & 8 deletions src/Forms/GridField/GridState.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,26 @@ public static function array_to_object($d)

public function setValue($value, $data = null)
{
if (is_string($value)) {
$this->data = new GridState_Data(json_decode($value, true));
// Apply the value on top of the existing defaults
$data = json_decode($value, true);
if ($data) {
$this->mergeValues($this->getData(), $data);
}

parent::setValue($value);
return $this;
}

private function mergeValues(GridState_Data $data, array $array): void
{
foreach ($array as $k => $v) {
if (is_array($v)) {
$this->mergeValues($data->$k, $v);
} else {
$data->$k = $v;
}
}
}

/**
* @return GridState_Data
*/
Expand Down Expand Up @@ -94,11 +106,8 @@ public function getList()
*/
public function Value()
{
if (!$this->data) {
return json_encode([]);
}

return json_encode($this->data->toArray());
$data = $this->data ? $this->data->getChangesArray() : [];
return json_encode($data, JSON_FORCE_OBJECT);
}

/**
Expand Down
62 changes: 57 additions & 5 deletions src/Forms/GridField/GridState_Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,58 @@ class GridState_Data
*/
protected $data;

protected $defaults = [];

public function __construct($data = [])
{
$this->data = $data;
}

public function __get($name)
{
return $this->getData($name, new GridState_Data());
return $this->getData($name, new self());
}

public function __call($name, $arguments)
{
// Assume first parameter is default value
$default = empty($arguments) ? new GridState_Data() : $arguments[0];
if (empty($arguments)) {
$default = new self();
} else {
$default = $arguments[0];
}

return $this->getData($name, $default);
}

/**
* Initialise the defaults values for the grid field state
* These values won't be included in getChangesArray()
*
* @param array $defaults
*/
public function initDefaults(array $defaults): void
{
foreach ($defaults as $key => $value) {
$this->defaults[$key] = $value;
$this->getData($key, $value);
}
}

/**
* Retrieve the value for the given key
*
* @param string $name The name of the value to retrieve
* @param mixed $default Default value to assign if not set
* @param mixed $default Default value to assign if not set. Note that this *will* be included in getChangesArray()
* @return mixed The value associated with this key, or the value specified by $default if not set
*/
public function getData($name, $default = null)
{
if (!isset($this->data[$name])) {
if (!array_key_exists($name, $this->data)) {
$this->data[$name] = $default;
} else {
if (is_array($this->data[$name])) {
$this->data[$name] = new GridState_Data($this->data[$name]);
$this->data[$name] = new self($this->data[$name]);
}
}

Expand Down Expand Up @@ -77,6 +98,9 @@ public function __toString()
return json_encode($this->toArray());
}

/**
* Return all data, including defaults, as array
*/
public function toArray()
{
$output = [];
Expand All @@ -85,6 +109,34 @@ public function toArray()
$output[$k] = (is_object($v) && method_exists($v, 'toArray')) ? $v->toArray() : $v;
}

return $output;
}
/**
* Convert the state to an array including only value that differ from the default state defined by initDefaults()
* @return array
*/
public function getChangesArray(): array
{
$output = [];

foreach ($this->data as $k => $v) {
if (is_object($v) && method_exists($v, 'getChangesArray')) {
$value = $v->getChangesArray();
// Empty arrays represent pristine data, so we do not include them
if (empty($value)) {
continue;
}
} else {
$value = $v;
// Check if we have a default value for this key and if it matches our current value
if (array_key_exists($k, $this->defaults) && $this->defaults[$k] === $value) {
continue;
}
}

$output[$k] = $value;
}

return $output;
}
}
Loading

0 comments on commit 4607151

Please sign in to comment.