diff --git a/core/lib/Drupal/Core/Field/FieldItemList.php b/core/lib/Drupal/Core/Field/FieldItemList.php index 5d13a5e..152005b 100644 --- a/core/lib/Drupal/Core/Field/FieldItemList.php +++ b/core/lib/Drupal/Core/Field/FieldItemList.php @@ -7,6 +7,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\Core\TypedData\DataDefinitionInterface; use Drupal\Core\TypedData\Plugin\DataType\ItemList; /** @@ -378,7 +379,6 @@ protected function defaultValueWidget(FormStateInterface $form_state) { * {@inheritdoc} */ public function equals(FieldItemListInterface $list_to_compare) { - $columns = $this->getFieldDefinition()->getFieldStorageDefinition()->getColumns(); $count1 = count($this); $count2 = count($list_to_compare); if ($count1 === 0 && $count2 === 0) { @@ -396,9 +396,13 @@ public function equals(FieldItemListInterface $list_to_compare) { } // If the values are not equal ensure a consistent order of field item // properties and remove properties which will not be saved. - $callback = function (&$value) use ($columns) { + $property_definitions = $this->getFieldDefinition()->getFieldStorageDefinition()->getPropertyDefinitions(); + $non_computed_properties = array_filter($property_definitions, function (DataDefinitionInterface $property) { + return !$property->isComputed(); + }); + $callback = function (&$value) use ($non_computed_properties) { if (is_array($value)) { - $value = array_intersect_key($value, $columns); + $value = array_intersect_key($value, $non_computed_properties); ksort($value); } }; diff --git a/core/lib/Drupal/Core/Field/MapFieldItemList.php b/core/lib/Drupal/Core/Field/MapFieldItemList.php new file mode 100644 index 0000000..2d86dae --- /dev/null +++ b/core/lib/Drupal/Core/Field/MapFieldItemList.php @@ -0,0 +1,34 @@ +getValue(); + $value2 = $list_to_compare->getValue(); + + return $value1 == $value2; + } + +} diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php index 04045c7..e15fe84 100644 --- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php +++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/MapItem.php @@ -12,7 +12,8 @@ * id = "map", * label = @Translation("Map"), * description = @Translation("An entity field for storing a serialized array of values."), - * no_ui = TRUE + * no_ui = TRUE, + * list_class = "\Drupal\Core\Field\MapFieldItemList", * ) */ class MapItem extends FieldItemBase { diff --git a/core/modules/path/tests/src/Kernel/PathItemTest.php b/core/modules/path/tests/src/Kernel/PathItemTest.php index 88044c3..d7627c3 100644 --- a/core/modules/path/tests/src/Kernel/PathItemTest.php +++ b/core/modules/path/tests/src/Kernel/PathItemTest.php @@ -171,6 +171,24 @@ public function testPathItem() { $this->assertEquals('/foobar', $loaded_node->get('path')->alias); $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); $this->assertEquals('/foobar', $stored_alias); + + // Check that \Drupal\Core\Field\FieldItemList::equals() for the path field + // type. + $node = Node::create([ + 'title' => $this->randomString(), + 'type' => 'foo', + 'path' => ['alias' => '/foo'], + ]); + $second_node = Node::create([ + 'title' => $this->randomString(), + 'type' => 'foo', + 'path' => ['alias' => '/foo'], + ]); + $this->assertTrue($node->get('path')->equals($second_node->get('path'))); + + // Change the alias for the second node to a different one and try again. + $second_node->get('path')->alias = '/foobar'; + $this->assertFalse($node->get('path')->equals($second_node->get('path'))); } } diff --git a/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php b/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php index 8927597..83a0854 100644 --- a/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php +++ b/core/tests/Drupal/Tests/Core/Field/FieldItemListTest.php @@ -28,10 +28,24 @@ public function testEquals($expected, FieldItemInterface $first_field_item = NUL $container->set('plugin.manager.field.field_type', $field_type_manager); \Drupal::setContainer($container); + // Set up three properties, one of them being computed. + $property_definitions['0'] = $this->getMock('Drupal\Core\TypedData\DataDefinitionInterface'); + $property_definitions['0']->expects($this->any()) + ->method('isComputed') + ->willReturn(FALSE); + $property_definitions['1'] = $this->getMock('Drupal\Core\TypedData\DataDefinitionInterface'); + $property_definitions['1']->expects($this->any()) + ->method('isComputed') + ->willReturn(FALSE); + $property_definitions['2'] = $this->getMock('Drupal\Core\TypedData\DataDefinitionInterface'); + $property_definitions['2']->expects($this->any()) + ->method('isComputed') + ->willReturn(TRUE); + $field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); $field_storage_definition->expects($this->any()) - ->method('getColumns') - ->willReturn([0 => '0', 1 => '1']); + ->method('getPropertyDefinitions') + ->will($this->returnValue($property_definitions)); $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface'); $field_definition->expects($this->any()) ->method('getFieldStorageDefinition') @@ -95,6 +109,30 @@ public function providerTestEquals() { // types. $datasets[] = [TRUE, $field_item_b, $field_item_e]; + /** @var \Drupal\Core\Field\FieldItemBase $field_item_f */ + $field_item_f = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); + $field_item_f->setValue(['0' => 1, '1' => 2, '2' => 3]); + /** @var \Drupal\Core\Field\FieldItemBase $field_item_g */ + $field_item_g = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); + $field_item_g->setValue(['0' => 1, '1' => 2, '2' => 4]); + + // Tests field item lists where both have same values for the non-computed + // properties ('0' and '1') and a different value for the computed one + // ('2'). + $datasets[] = [TRUE, $field_item_f, $field_item_g]; + + /** @var \Drupal\Core\Field\FieldItemBase $field_item_h */ + $field_item_h = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); + $field_item_h->setValue(['0' => 1, '1' => 2, '3' => 3]); + /** @var \Drupal\Core\Field\FieldItemBase $field_item_i */ + $field_item_i = $this->getMockForAbstractClass('Drupal\Core\Field\FieldItemBase', [], '', FALSE); + $field_item_i->setValue(['0' => 1, '1' => 2, '3' => 4]); + + // Tests field item lists where both have same values for the non-computed + // properties ('0' and '1') and a different value for a property that does + // not exist ('3'). + $datasets[] = [TRUE, $field_item_h, $field_item_i]; + return $datasets; } @@ -114,10 +152,20 @@ public function testEqualsEmptyItems() { $container->set('plugin.manager.field.field_type', $field_type_manager); \Drupal::setContainer($container); + // Set up the properties of the field item. + $property_definitions['0'] = $this->getMock('Drupal\Core\TypedData\DataDefinitionInterface'); + $property_definitions['0']->expects($this->any()) + ->method('isComputed') + ->willReturn(FALSE); + $property_definitions['1'] = $this->getMock('Drupal\Core\TypedData\DataDefinitionInterface'); + $property_definitions['1']->expects($this->any()) + ->method('isComputed') + ->willReturn(FALSE); + $field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface'); $field_storage_definition->expects($this->any()) - ->method('getColumns') - ->willReturn([0 => '0', 1 => '1']); + ->method('getPropertyDefinitions') + ->will($this->returnValue($property_definitions)); $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface'); $field_definition->expects($this->any()) ->method('getFieldStorageDefinition')