Commit fa39f064 authored by xjm's avatar xjm

Issue #2361539 by alexpott, webflo, AjitS, himanshu-dixit, tameeshb, xjm:...

Issue #2361539 by alexpott, webflo, AjitS, himanshu-dixit, tameeshb, xjm: Config export key order is not predictable for sequences, add orderby property to config schema
parent 47ec7f7d
......@@ -46,7 +46,7 @@ mapping:
sequence:
label: Sequence
class: '\Drupal\Core\Config\Schema\Sequence'
definition_class: '\Drupal\Core\TypedData\ListDataDefinition'
definition_class: '\Drupal\Core\Config\Schema\SequenceDataDefinition'
# Simple extended data types:
......
<?php
namespace Drupal\Core\Config\Schema;
use Drupal\Core\TypedData\ListDataDefinition;
/**
* A typed data definition class for defining sequences in configuration.
*/
class SequenceDataDefinition extends ListDataDefinition {
/**
* Gets the description of how the sequence should be sorted.
*
* Only the top level of the array should be sorted. Top-level keys should be
* discarded when using 'value' sorting. If the sequence is an associative
* array 'key' sorting is recommended, if not 'value' sorting is recommended.
*
* @return string|null
* May be 'key' (to sort by key), 'value' (to sort by value, discarding
* keys), or NULL (if the schema does not describe how the sequence should
* be sorted).
*/
public function getOrderBy() {
return isset($this->definition['orderby']) ? $this->definition['orderby'] : NULL;
}
}
......@@ -3,6 +3,8 @@
namespace Drupal\Core\Config;
use Drupal\Core\Config\Schema\Ignore;
use Drupal\Core\Config\Schema\Sequence;
use Drupal\Core\Config\Schema\SequenceDataDefinition;
use Drupal\Core\TypedData\PrimitiveInterface;
use Drupal\Core\TypedData\Type\FloatInterface;
use Drupal\Core\TypedData\Type\IntegerInterface;
......@@ -210,6 +212,29 @@ protected function castValue($key, $value) {
foreach ($value as $nested_value_key => $nested_value) {
$value[$nested_value_key] = $this->castValue($key . '.' . $nested_value_key, $nested_value);
}
if ($element instanceof Sequence) {
$data_definition = $element->getDataDefinition();
if ($data_definition instanceof SequenceDataDefinition) {
// Apply any sorting defined on the schema.
switch ($data_definition->getOrderBy()) {
case 'key':
ksort($value);
break;
case 'value':
// The PHP documentation notes that "Be careful when sorting
// arrays with mixed types values because sort() can produce
// unpredictable results". There is no risk here because
// \Drupal\Core\Config\StorableConfigBase::castValue() has
// already cast all values to the same type using the
// configuration schema.
sort($value);
break;
}
}
}
}
return $value;
}
......
......@@ -297,3 +297,41 @@ test.double_brackets.breed:
mapping:
breed:
type: string
config_schema_test.schema_sequence_sort:
type: config_object
mapping:
keyed_sort:
type: sequence
orderby: key
sequence:
- type: string
value_sort:
type: sequence
orderby: value
sequence:
- type: string
no_sort:
type: sequence
sequence:
- type: string
complex_sort_value:
type: sequence
orderby: value
sequence:
- type: mapping
mapping:
foo:
type: string
bar:
type: string
complex_sort_key:
type: sequence
orderby: key
sequence:
- type: mapping
mapping:
foo:
type: string
bar:
type: string
......@@ -395,6 +395,76 @@ public function testConfigSaveWithSchema() {
$this->assertIdentical($installed_data, $original_data);
}
/**
* Tests configuration sequence sorting using schemas.
*/
public function testConfigSaveWithSequenceSorting() {
$data = [
'keyed_sort' => [
'b' => '1',
'a' => '2',
],
'no_sort' => [
'b' => '2',
'a' => '1',
],
];
// Save config which has a schema that enforces sorting.
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame(['a' => '2', 'b' => '1'], $this->config('config_schema_test.schema_sequence_sort')->get('keyed_sort'));
$this->assertSame(['b' => '2', 'a' => '1'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
$data = [
'value_sort' => ['b', 'a'],
'no_sort' => ['b', 'a'],
];
// Save config which has a schema that enforces sorting.
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame(['a', 'b'], $this->config('config_schema_test.schema_sequence_sort')->get('value_sort'));
$this->assertSame(['b', 'a'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
// Value sort does not preserve keys - this is intentional.
$data = [
'value_sort' => [1 => 'b', 2 => 'a'],
'no_sort' => [1 => 'b', 2 => 'a'],
];
// Save config which has a schema that enforces sorting.
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame(['a', 'b'], $this->config('config_schema_test.schema_sequence_sort')->get('value_sort'));
$this->assertSame([1 => 'b', 2 => 'a'], $this->config('config_schema_test.schema_sequence_sort')->get('no_sort'));
// Test sorts do not destroy complex values.
$data = [
'complex_sort_value' => [['foo' => 'b', 'bar' => 'b'] , ['foo' => 'a', 'bar' => 'a']],
'complex_sort_key' => ['b' => ['foo' => '1', 'bar' => '1'] , 'a' => ['foo' => '2', 'bar' => '2']],
];
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame([['foo' => 'a', 'bar' => 'a'], ['foo' => 'b', 'bar' => 'b']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_value'));
$this->assertSame(['a' => ['foo' => '2', 'bar' => '2'], 'b' => ['foo' => '1', 'bar' => '1']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_key'));
// Swap the previous test scenario around.
$data = [
'complex_sort_value' => ['b' => ['foo' => '1', 'bar' => '1'] , 'a' => ['foo' => '2', 'bar' => '2']],
'complex_sort_key' => [['foo' => 'b', 'bar' => 'b'] , ['foo' => 'a', 'bar' => 'a']],
];
$this->config('config_schema_test.schema_sequence_sort')
->setData($data)
->save();
$this->assertSame([['foo' => '1', 'bar' => '1'], ['foo' => '2', 'bar' => '2']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_value'));
$this->assertSame([['foo' => 'b', 'bar' => 'b'], ['foo' => 'a', 'bar' => 'a']], $this->config('config_schema_test.schema_sequence_sort')->get('complex_sort_key'));
}
/**
* Tests fallback to a greedy wildcard.
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment