1<?php 2 3namespace Drupal\KernelTests\Core\Entity; 4 5use Drupal\Core\Field\BaseFieldDefinition; 6use Drupal\Core\Field\FieldStorageDefinitionInterface; 7use Drupal\entity_test\Entity\EntityTest; 8use Drupal\field\Entity\FieldConfig; 9use Drupal\node\Entity\Node; 10use Drupal\node\NodeInterface; 11use Drupal\Tests\field\Traits\EntityReferenceTestTrait; 12use Drupal\Tests\node\Traits\ContentTypeCreationTrait; 13use Drupal\user\Entity\Role; 14use Drupal\user\Entity\User; 15 16/** 17 * Tests validation constraints for ValidReferenceConstraintValidator. 18 * 19 * @group Validation 20 */ 21class ValidReferenceConstraintValidatorTest extends EntityKernelTestBase { 22 23 use EntityReferenceTestTrait; 24 use ContentTypeCreationTrait; 25 26 /** 27 * The typed data manager to use. 28 * 29 * @var \Drupal\Core\TypedData\TypedDataManager 30 */ 31 protected $typedData; 32 33 /** 34 * {@inheritdoc} 35 */ 36 public static $modules = ['field', 'node', 'user']; 37 38 /** 39 * {@inheritdoc} 40 */ 41 protected function setUp() { 42 parent::setUp(); 43 $this->installSchema('user', ['users_data']); 44 $this->installSchema('node', ['node_access']); 45 $this->installConfig('node'); 46 $this->typedData = $this->container->get('typed_data_manager'); 47 48 $this->createContentType(['type' => 'article', 'name' => 'Article']); 49 $this->createContentType(['type' => 'page', 'name' => 'Basic page']); 50 } 51 52 /** 53 * Tests the ValidReferenceConstraintValidator. 54 */ 55 public function testValidation() { 56 // Create a test entity to be referenced. 57 $entity = $this->createUser(); 58 // By default entity references already have the ValidReference constraint. 59 $definition = BaseFieldDefinition::create('entity_reference') 60 ->setSettings(['target_type' => 'user']); 61 62 $typed_data = $this->typedData->create($definition, ['target_id' => $entity->id()]); 63 $violations = $typed_data->validate(); 64 $this->assertEquals(0, $violations->count(), 'Validation passed for correct value.'); 65 66 // NULL is also considered a valid reference. 67 $typed_data = $this->typedData->create($definition, ['target_id' => NULL]); 68 $violations = $typed_data->validate(); 69 $this->assertEquals(0, $violations->count(), 'Validation passed for correct value.'); 70 71 $typed_data = $this->typedData->create($definition, ['target_id' => $entity->id()]); 72 // Delete the referenced entity. 73 $entity->delete(); 74 $violations = $typed_data->validate(); 75 $this->assertGreaterThan(0, $violations->count(), 'Validation failed for incorrect value.'); 76 77 // Make sure the information provided by a violation is correct. 78 $violation = $violations[0]; 79 $this->assertEqual($violation->getMessage(), t('The referenced entity (%type: %id) does not exist.', [ 80 '%type' => 'user', 81 '%id' => $entity->id(), 82 ]), 'The message for invalid value is correct.'); 83 $this->assertEqual($violation->getRoot(), $typed_data, 'Violation root is correct.'); 84 } 85 86 /** 87 * Tests the validation of pre-existing items in an entity reference field. 88 */ 89 public function testPreExistingItemsValidation() { 90 // Create two types of users, with and without access to bypass content 91 // access. 92 /** @var \Drupal\user\RoleInterface $role_with_access */ 93 $role_with_access = Role::create(['id' => 'role_with_access']); 94 $role_with_access->grantPermission('access content'); 95 $role_with_access->grantPermission('bypass node access'); 96 $role_with_access->save(); 97 98 /** @var \Drupal\user\RoleInterface $role_without_access */ 99 $role_without_access = Role::create(['id' => 'role_without_access']); 100 $role_without_access->grantPermission('access content'); 101 $role_without_access->save(); 102 103 $user_with_access = User::create(['roles' => ['role_with_access']]); 104 $user_without_access = User::create(['roles' => ['role_without_access']]); 105 106 // Add an entity reference field. 107 $this->createEntityReferenceField( 108 'entity_test', 109 'entity_test', 110 'field_test', 111 'Field test', 112 'node', 113 'default', 114 ['target_bundles' => ['article', 'page']], 115 FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED 116 ); 117 118 // Create four test nodes. 119 $published_node = Node::create([ 120 'title' => 'Test published node', 121 'type' => 'article', 122 'status' => NodeInterface::PUBLISHED, 123 ]); 124 $published_node->save(); 125 126 $unpublished_node = Node::create([ 127 'title' => 'Test unpublished node', 128 'type' => 'article', 129 'status' => NodeInterface::NOT_PUBLISHED, 130 ]); 131 $unpublished_node->save(); 132 133 $different_bundle_node = Node::create([ 134 'title' => 'Test page node', 135 'type' => 'page', 136 'status' => NodeInterface::PUBLISHED, 137 ]); 138 $different_bundle_node->save(); 139 140 $deleted_node = Node::create([ 141 'title' => 'Test deleted node', 142 'type' => 'article', 143 'status' => NodeInterface::PUBLISHED, 144 ]); 145 $deleted_node->save(); 146 147 $referencing_entity = EntityTest::create([ 148 'field_test' => [ 149 ['entity' => $published_node], 150 ['entity' => $unpublished_node], 151 ['entity' => $different_bundle_node], 152 ['entity' => $deleted_node], 153 ], 154 ]); 155 156 // Check that users with access are able pass the validation for fields 157 // without pre-existing content. 158 $this->container->get('account_switcher')->switchTo($user_with_access); 159 160 $violations = $referencing_entity->field_test->validate(); 161 $this->assertCount(0, $violations); 162 163 // Check that users without access are not able pass the validation for 164 // fields without pre-existing content. 165 $this->container->get('account_switcher')->switchTo($user_without_access); 166 167 $violations = $referencing_entity->field_test->validate(); 168 $this->assertCount(1, $violations); 169 $this->assertEquals(t('This entity (%type: %id) cannot be referenced.', [ 170 '%type' => 'node', 171 '%id' => $unpublished_node->id(), 172 ]), $violations[0]->getMessage()); 173 174 // Now save the referencing entity which will create a pre-existing state 175 // for it and repeat the checks. This time, the user without access should 176 // be able to pass the validation as well because it's not changing the 177 // pre-existing state. 178 $referencing_entity->save(); 179 180 $this->container->get('account_switcher')->switchTo($user_with_access); 181 182 $violations = $referencing_entity->field_test->validate(); 183 $this->assertCount(0, $violations); 184 185 // Check that users without access are able pass the validation for fields 186 // with pre-existing content. 187 $this->container->get('account_switcher')->switchTo($user_without_access); 188 189 $violations = $referencing_entity->field_test->validate(); 190 $this->assertCount(0, $violations); 191 192 // Re-save the referencing entity and check that the referenced entity is 193 // not affected. 194 $referencing_entity->name->value = $this->randomString(); 195 $referencing_entity->save(); 196 $this->assertEquals($published_node->id(), $referencing_entity->field_test[0]->target_id); 197 $this->assertEquals($unpublished_node->id(), $referencing_entity->field_test[1]->target_id); 198 $this->assertEquals($different_bundle_node->id(), $referencing_entity->field_test[2]->target_id); 199 $this->assertEquals($deleted_node->id(), $referencing_entity->field_test[3]->target_id); 200 201 $violations = $referencing_entity->field_test->validate(); 202 $this->assertCount(0, $violations); 203 204 // Remove one of the referenceable bundles and check that a pre-existing node 205 // of that bundle can not be referenced anymore. 206 $field = FieldConfig::loadByName('entity_test', 'entity_test', 'field_test'); 207 $field->setSetting('handler_settings', ['target_bundles' => ['article']]); 208 $field->save(); 209 $referencing_entity = $this->reloadEntity($referencing_entity); 210 211 $violations = $referencing_entity->field_test->validate(); 212 $this->assertCount(1, $violations); 213 $this->assertEquals(t('This entity (%type: %id) cannot be referenced.', [ 214 '%type' => 'node', 215 '%id' => $different_bundle_node->id(), 216 ]), $violations[0]->getMessage()); 217 218 // Delete the last node and check that the pre-existing reference is not 219 // valid anymore. 220 $deleted_node->delete(); 221 222 $violations = $referencing_entity->field_test->validate(); 223 $this->assertCount(2, $violations); 224 $this->assertEquals(t('This entity (%type: %id) cannot be referenced.', [ 225 '%type' => 'node', 226 '%id' => $different_bundle_node->id(), 227 ]), $violations[0]->getMessage()); 228 $this->assertEquals(t('The referenced entity (%type: %id) does not exist.', [ 229 '%type' => 'node', 230 '%id' => $deleted_node->id(), 231 ]), $violations[1]->getMessage()); 232 } 233 234} 235