Skip to content
Snippets Groups Projects
Commit e55a223e authored by Alex Pott's avatar Alex Pott
Browse files

Issue #2365585 by yched: FieldItemList::filterEmptyItems() renumbers deltas...

Issue #2365585 by yched: FieldItemList::filterEmptyItems() renumbers deltas but does not update the Items
parent 36122d4c
No related branches found
No related tags found
2 merge requests!7452Issue #1797438. HTML5 validation is preventing form submit and not fully...,!789Issue #3210310: Adjust Database API to remove deprecated Drupal 9 code in Drupal 10
...@@ -89,4 +89,13 @@ public function set($index, $item) { ...@@ -89,4 +89,13 @@ public function set($index, $item) {
return $this; return $this;
} }
/**
* {@inheritdoc}
*/
public function filter($callback) {
$this->value = array_filter($this->value, $callback);
unset($this->elements);
return $this;
}
} }
...@@ -101,11 +101,9 @@ public function getSetting($setting_name) { ...@@ -101,11 +101,9 @@ public function getSetting($setting_name) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function filterEmptyItems() { public function filterEmptyItems() {
if (isset($this->list)) { $this->filter(function ($item) {
$this->list = array_values(array_filter($this->list, function($item) {
return !$item->isEmpty(); return !$item->isEmpty();
})); });
}
} }
/** /**
......
...@@ -80,4 +80,16 @@ public function set($index, $item); ...@@ -80,4 +80,16 @@ public function set($index, $item);
*/ */
public function first(); public function first();
/**
* Filters the items in the list using a custom callback.
*
* @param callable $callback
* The callback to use for filtering. Like with array_filter(), the
* callback is called for each item in the list. Only items for which the
* callback returns TRUE are preserved.
*
* @return $this
*/
public function filter($callback);
} }
...@@ -229,6 +229,33 @@ public function isEmpty() { ...@@ -229,6 +229,33 @@ public function isEmpty() {
return TRUE; return TRUE;
} }
/**
* {@inheritdoc}
*/
public function filter($callback) {
if (isset($this->list)) {
$removed = FALSE;
// Apply the filter, detecting if some items were actually removed.
$this->list = array_filter($this->list, function ($item) use ($callback, &$removed) {
if (call_user_func($callback, $item)) {
return TRUE;
}
else {
$removed = TRUE;
}
});
if ($removed) {
// Rekey the array using array_values().
$this->list = array_values($this->list);
// Manually update each item's delta.
foreach ($this->list as $delta => $item) {
$item->setContext($delta, $this);
}
}
}
return $this;
}
/** /**
* Implements \Drupal\Core\TypedData\ListInterface::onChange(). * Implements \Drupal\Core\TypedData\ListInterface::onChange().
*/ */
......
...@@ -265,6 +265,14 @@ protected function doTestReadWrite($entity_type) { ...@@ -265,6 +265,14 @@ protected function doTestReadWrite($entity_type) {
$this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', array('%entity_type' => $entity_type))); $this->assertEqual(count(iterator_to_array($entity->name->getIterator())), count($entity->name), format_string('%entity_type: Count matches iterator count.', array('%entity_type' => $entity_type)));
$this->assertTrue($entity->name->getValue() === array(0 => array('value' => NULL)), format_string('%entity_type: Name field value contains a NULL value.', array('%entity_type' => $entity_type))); $this->assertTrue($entity->name->getValue() === array(0 => array('value' => NULL)), format_string('%entity_type: Name field value contains a NULL value.', array('%entity_type' => $entity_type)));
// Test using filterEmptyItems().
$entity->name = array(NULL, 'foo');
$this->assertEqual(count($entity->name), 2, format_string('%entity_type: List has 2 items.', array('%entity_type' => $entity_type)));
$entity->name->filterEmptyItems();
$this->assertEqual(count($entity->name), 1, format_string('%entity_type: The empty item was removed.', array('%entity_type' => $entity_type)));
$this->assertEqual($entity->name[0]->value, 'foo', format_string('%entity_type: The items were renumbered.', array('%entity_type' => $entity_type)));
$this->assertEqual($entity->name[0]->getName(), 0, format_string('%entity_type: The deltas were updated in the items.', array('%entity_type' => $entity_type)));
// Test removing all list items by assigning an empty array. // Test removing all list items by assigning an empty array.
$entity->name = array(); $entity->name = array();
$this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type))); $this->assertIdentical(count($entity->name), 0, format_string('%entity_type: Name field contains no items.', array('%entity_type' => $entity_type)));
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
use Drupal\Core\TypedData\DataDefinition; use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\ListDataDefinition; use Drupal\Core\TypedData\ListDataDefinition;
use Drupal\Core\TypedData\MapDataDefinition; use Drupal\Core\TypedData\MapDataDefinition;
use Drupal\Core\TypedData\TypedDataInterface;
use Drupal\simpletest\DrupalUnitTestBase; use Drupal\simpletest\DrupalUnitTestBase;
use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Datetime\DrupalDateTime;
...@@ -318,7 +319,7 @@ public function testTypedDataLists() { ...@@ -318,7 +319,7 @@ public function testTypedDataLists() {
// Test iterating. // Test iterating.
$count = 0; $count = 0;
foreach ($typed_data as $item) { foreach ($typed_data as $item) {
$this->assertTrue($item instanceof \Drupal\Core\TypedData\TypedDataInterface); $this->assertTrue($item instanceof TypedDataInterface);
$count++; $count++;
} }
$this->assertEqual($count, 3); $this->assertEqual($count, 3);
...@@ -394,6 +395,43 @@ public function testTypedDataLists() { ...@@ -394,6 +395,43 @@ public function testTypedDataLists() {
} }
} }
/**
* Tests the filter() method on typed data lists.
*/
public function testTypedDataListsFilter() {
// Check that an all-pass filter leaves the list untouched.
$value = array('zero', 'one');
$typed_data = $this->createTypedData(ListDataDefinition::create('string'), $value);
$typed_data->filter(function(TypedDataInterface $item) {
return TRUE;
});
$this->assertEqual($typed_data->count(), 2);
$this->assertEqual($typed_data[0]->getValue(), 'zero');
$this->assertEqual($typed_data[0]->getName(), 0);
$this->assertEqual($typed_data[1]->getValue(), 'one');
$this->assertEqual($typed_data[1]->getName(), 1);
// Check that a none-pass filter empties the list.
$value = array('zero', 'one');
$typed_data = $this->createTypedData(ListDataDefinition::create('string'), $value);
$typed_data->filter(function(TypedDataInterface $item) {
return FALSE;
});
$this->assertEqual($typed_data->count(), 0);
// Check that filtering correctly renumbers elements.
$value = array('zero', 'one', 'two');
$typed_data = $this->createTypedData(ListDataDefinition::create('string'), $value);
$typed_data->filter(function(TypedDataInterface $item) {
return $item->getValue() !== 'one';
});
$this->assertEqual($typed_data->count(), 2);
$this->assertEqual($typed_data[0]->getValue(), 'zero');
$this->assertEqual($typed_data[0]->getName(), 0);
$this->assertEqual($typed_data[1]->getValue(), 'two');
$this->assertEqual($typed_data[1]->getName(), 1);
}
/** /**
* Tests using a typed data map. * Tests using a typed data map.
*/ */
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment