FieldInfoTest.php 16.4 KB
Newer Older
1 2 3 4
<?php

/**
 * @file
5
 * Contains \Drupal\field\Tests\FieldInfoTest.
6 7 8 9
 */

namespace Drupal\field\Tests;

10 11
use Drupal\Core\Language\Language;

12
class FieldInfoTest extends FieldUnitTestBase {
13

14 15 16 17 18 19 20 21 22
  public static function getInfo() {
    return array(
      'name' => 'Field info tests',
      'description' => 'Get information about existing fields, instances and bundles.',
      'group' => 'Field API',
    );
  }

  /**
23
   * Test that field types and field definitions are correctly cached.
24 25 26 27
   */
  function testFieldInfo() {
    // Test that field_test module's fields, widgets, and formatters show up.

28
    $field_test_info = $this->getExpectedFieldTypeDefinition();
29
    $entity_type = \Drupal::service('plugin.manager.field.field_type')->getConfigurableDefinitions();
30 31
    foreach ($field_test_info as $t_key => $field_type) {
      foreach ($field_type as $key => $val) {
32
        $this->assertEqual($entity_type[$t_key][$key], $val, format_string('Field type %t_key key %key is %value', array('%t_key' => $t_key, '%key' => $key, '%value' => print_r($val, TRUE))));
33
      }
34
      $this->assertEqual($entity_type[$t_key]['provider'], 'field_test',  'Field type field_test module appears.');
35 36 37
    }

    // Verify that no unexpected instances exist.
38
    $instances = field_info_instances('entity_test');
39
    $expected = array();
40 41 42
    $this->assertIdentical($instances, $expected, format_string("field_info_instances('entity_test') returns %expected.", array('%expected' => var_export($expected, TRUE))));
    $instances = field_info_instances('entity_test', 'entity_test');
    $this->assertIdentical($instances, array(), "field_info_instances('entity_test', 'entity_test') returns an empty array.");
43 44 45

    // Create a field, verify it shows up.
    $core_fields = field_info_fields();
46
    $field = entity_create('field_config', array(
47 48
      'name' => drupal_strtolower($this->randomName()),
      'entity_type' => 'entity_test',
49
      'type' => 'test_field',
50 51
    ));
    $field->save();
52
    $fields = field_info_fields();
53
    $this->assertEqual(count($fields), count($core_fields) + 1, 'One new field exists');
54 55
    $this->assertEqual($fields[$field->uuid]->getName(), $field->getName(), 'info fields contains field name');
    $this->assertEqual($fields[$field->uuid]->getType(), $field->getType(), 'info fields contains field type');
56
    $this->assertEqual($fields[$field->uuid]->module, 'field_test', 'info fields contains field module');
57 58
    $settings = array('test_field_setting' => 'dummy test string');
    foreach ($settings as $key => $val) {
59
      $this->assertEqual($fields[$field->uuid]->getSetting($key), $val, format_string('Field setting %key has correct default value %value', array('%key' => $key, '%value' => $val)));
60
    }
61
    $this->assertEqual($fields[$field->uuid]->getCardinality(), 1, 'info fields contains cardinality 1');
62 63

    // Create an instance, verify that it shows up
64
    $instance_definition = array(
65
      'field_name' => $field->getName(),
66 67
      'entity_type' => 'entity_test',
      'bundle' => 'entity_test',
68 69 70
      'label' => $this->randomName(),
      'description' => $this->randomName(),
      'weight' => mt_rand(0, 127),
71
    );
72
    $instance = entity_create('field_instance_config', $instance_definition);
73
    $instance->save();
74

75
    $entity_type = \Drupal::entityManager()->getDefinition('entity_test');
76
    $instances = field_info_instances('entity_test', $instance->bundle);
77
    $this->assertEqual(count($instances), 1, format_string('One instance shows up in info when attached to a bundle on a @label.', array(
78
      '@label' => $entity_type->getLabel(),
79
    )));
80
    $this->assertTrue($instance_definition < $instances[$instance->getName()], 'Instance appears in info correctly');
81 82

    // Test a valid entity type but an invalid bundle.
83 84
    $instances = field_info_instances('entity_test', 'invalid_bundle');
    $this->assertIdentical($instances, array(), "field_info_instances('entity_test', 'invalid_bundle') returns an empty array.");
85 86

    // Test invalid entity type and bundle.
87
    $instances = field_info_instances('invalid_entity', $instance->bundle);
88
    $this->assertIdentical($instances, array(), "field_info_instances('invalid_entity', 'entity_test') returns an empty array.");
89 90 91 92 93 94 95

    // Test invalid entity type, no bundle provided.
    $instances = field_info_instances('invalid_entity');
    $this->assertIdentical($instances, array(), "field_info_instances('invalid_entity') returns an empty array.");

    // Test with an entity type that has no bundles.
    $instances = field_info_instances('user');
96
    $expected = array();
97
    $this->assertIdentical($instances, $expected, format_string("field_info_instances('user') returns %expected.", array('%expected' => var_export($expected, TRUE))));
98 99
    $instances = field_info_instances('user', 'user');
    $this->assertIdentical($instances, array(), "field_info_instances('user', 'user') returns an empty array.");
100 101 102 103 104 105 106 107 108 109

    // Test that querying for invalid entity types does not add entries in the
    // list returned by field_info_instances().
    field_info_cache_clear();
    field_info_instances('invalid_entity', 'invalid_bundle');
    // Simulate new request by clearing static caches.
    drupal_static_reset();
    field_info_instances('invalid_entity', 'invalid_bundle');
    $instances = field_info_instances();
    $this->assertFalse(isset($instances['invalid_entity']), 'field_info_instances() does not contain entries for the invalid entity type that was queried before');
110 111 112 113 114 115 116
  }

  /**
   * Test that cached field definitions are ready for current runtime context.
   */
  function testFieldPrepare() {
    $field_definition = array(
117 118
      'name' => 'field',
      'entity_type' => 'entity_test',
119 120
      'type' => 'test_field',
    );
121
    $field = entity_create('field_config', $field_definition);
122
    $field->save();
123 124 125 126

    // Simulate a stored field definition missing a field setting (e.g. a
    // third-party module adding a new field setting has been enabled, and
    // existing fields do not know the setting yet).
127 128 129 130
    \Drupal::config('field.field.' . $field->id())
      ->set('settings', array())
      ->save();
    field_info_cache_clear();
131 132

    // Read the field back.
133
    $field = field_info_field('entity_test', $field_definition['name']);
134 135

    // Check that all expected settings are in place.
136
    $field_type = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field_definition['type']);
137
    $this->assertEqual($field->settings, $field_type['settings'], 'All expected default field settings are present.');
138 139 140 141 142 143 144
  }

  /**
   * Test that cached instance definitions are ready for current runtime context.
   */
  function testInstancePrepare() {
    $field_definition = array(
145 146
      'name' => 'field',
      'entity_type' => 'entity_test',
147 148
      'type' => 'test_field',
    );
149
    entity_create('field_config', $field_definition)->save();
150
    $instance_definition = array(
151
      'field_name' => $field_definition['name'],
152 153
      'entity_type' => 'entity_test',
      'bundle' => 'entity_test',
154
    );
155
    $instance = entity_create('field_instance_config', $instance_definition);
156
    $instance->save();
157 158

    // Simulate a stored instance definition missing various settings (e.g. a
159 160
    // third-party module adding instance settings has been enabled, but
    // existing instances do not know the new settings).
161 162 163 164
    \Drupal::config('field.instance.' . $instance->id())
      ->set('settings', array())
      ->save();
    field_info_cache_clear();
165 166 167 168 169

    // Read the instance back.
    $instance = field_info_instance($instance_definition['entity_type'], $instance_definition['field_name'], $instance_definition['bundle']);

    // Check that all expected instance settings are in place.
170
    $field_type = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field_definition['type']);
171
    $this->assertEqual($instance->settings, $field_type['instance_settings'] , 'All expected instance settings are present.');
172 173 174 175 176 177
  }

  /**
   * Test that instances on disabled entity types are filtered out.
   */
  function testInstanceDisabledEntityType() {
178 179 180 181 182
    // Disabling the comment module invokes user_modules_uninstalled() and calls
    // drupal_flush_all_caches(). Install the necessary schema to support this.
    $this->installSchema('user', array('users_data'));
    $this->installSchema('system', array('router'));

183 184
    // For this test the field type and the entity type must be exposed by
    // different modules.
185
    $this->enableModules(array('node', 'comment'));
186
    $field_definition = array(
187 188
      'name' => 'field',
      'entity_type' => 'comment',
189 190
      'type' => 'test_field',
    );
191
    entity_create('field_config', $field_definition)->save();
192 193 194 195 196
    $instance_definition = array(
      'field_name' => 'field',
      'entity_type' => 'comment',
      'bundle' => 'comment_node_article',
    );
197
    entity_create('field_instance_config', $instance_definition)->save();
198

199 200
    $this->assertNotNull(field_info_instance('comment', 'field', 'comment_node_article'), 'Instance is returned on enabled entity types.');
    // Disable comment module. This clears field_info cache.
201
    module_uninstall(array('comment'));
202
    $this->assertNull(field_info_instance('comment', 'field', 'comment_node_article'), 'No instances are returned on disabled entity types.');
203 204
  }

205 206 207 208
  /**
   * Test field_info_field_map().
   */
  function testFieldMap() {
209
    // We will overlook fields created by the 'standard' installation profile.
210 211
    $exclude = field_info_field_map();

212 213
    // Create a new bundle for 'entity_test' entity type.
    entity_test_create_bundle('test_bundle_2');
214 215 216 217

    // Create a couple fields.
    $fields  = array(
      array(
218 219
        'name' => 'field_1',
        'entity_type' => 'entity_test',
220 221 222
        'type' => 'test_field',
      ),
      array(
223 224 225 226 227 228 229
        'name' => 'field_2',
        'entity_type' => 'entity_test',
        'type' => 'hidden_test_field',
      ),
      array(
        'name' => 'field_2',
        'entity_type' => 'entity_test_cache',
230 231 232 233
        'type' => 'hidden_test_field',
      ),
    );
    foreach ($fields as $field) {
234
      entity_create('field_config', $field)->save();
235 236 237 238 239 240
    }

    // Create a couple instances.
    $instances = array(
      array(
        'field_name' => 'field_1',
241 242
        'entity_type' => 'entity_test',
        'bundle' => 'entity_test',
243 244 245
      ),
      array(
        'field_name' => 'field_1',
246
        'entity_type' => 'entity_test',
247 248 249 250
        'bundle' => 'test_bundle_2',
      ),
      array(
        'field_name' => 'field_2',
251 252
        'entity_type' => 'entity_test',
        'bundle' => 'entity_test',
253 254 255
      ),
      array(
        'field_name' => 'field_2',
256 257
        'entity_type' => 'entity_test_cache',
        'bundle' => 'entity_test',
258 259 260
      ),
    );
    foreach ($instances as $instance) {
261
      entity_create('field_instance_config', $instance)->save();
262 263 264
    }

    $expected = array(
265 266 267 268 269 270 271 272
      'entity_test' => array(
        'field_1' => array(
          'type' => 'test_field',
          'bundles' => array('entity_test', 'test_bundle_2'),
        ),
        'field_2' => array(
          'type' => 'hidden_test_field',
          'bundles' => array('entity_test'),
273 274
        ),
      ),
275 276 277 278
      'entity_test_cache' => array(
        'field_2' => array(
          'type' => 'hidden_test_field',
          'bundles' => array('entity_test')
279 280 281 282 283 284 285 286 287 288
        ),
      ),
    );

    // Check that the field map is correct.
    $map = field_info_field_map();
    $map = array_diff_key($map, $exclude);
    $this->assertEqual($map, $expected);
  }

289 290 291 292
  /**
   * Test that the field_info settings convenience functions work.
   */
  function testSettingsInfo() {
293
    $info = $this->getExpectedFieldTypeDefinition();
294
    foreach ($info as $type => $data) {
295
      $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
296 297
      $this->assertIdentical($field_type_manager->getDefaultSettings($type), $data['settings'], format_string("field settings service returns %type's field settings", array('%type' => $type)));
      $this->assertIdentical($field_type_manager->getDefaultInstanceSettings($type), $data['instance_settings'], format_string("field instance settings service returns %type's field instance settings", array('%type' => $type)));
298 299
    }
  }
300

301 302 303 304 305 306 307
  /**
   * Tests that the field info cache can be built correctly.
   */
  function testFieldInfoCache() {
    // Create a test field and ensure it's in the array returned by
    // field_info_fields().
    $field_name = drupal_strtolower($this->randomName());
308
    $field = entity_create('field_config', array(
309 310
      'name' => $field_name,
      'entity_type' => 'entity_test',
311
      'type' => 'test_field',
312 313
    ));
    $field->save();
314
    $fields = field_info_fields();
315
    $this->assertTrue(isset($fields[$field->uuid]), 'The test field is initially found in the array returned by field_info_fields().');
316 317 318

    // Now rebuild the field info cache, and set a variable which will cause
    // the cache to be cleared while it's being rebuilt; see
319
    // field_test_entity_type_build(). Ensure the test field is still in the returned
320 321
    // array.
    field_info_cache_clear();
322
    \Drupal::state()->set('field_test.clear_info_cache_in_hook_entity_type_build', TRUE);
323
    $fields = field_info_fields();
324
    $this->assertTrue(isset($fields[$field->uuid]), 'The test field is found in the array returned by field_info_fields() even if its cache is cleared while being rebuilt.');
325 326
  }

327 328 329 330
  /**
   * Test that the widget definition functions work.
   */
  function testWidgetDefinition() {
331
    $widget_definition = \Drupal::service('plugin.manager.field.widget')->getDefinition('test_field_widget_multiple');
332 333 334 335

    // Test if hook_field_widget_info_alter is beïng called.
    $this->assertTrue(in_array('test_field', $widget_definition['field_types']), "The 'test_field_widget_multiple' widget is enabled for the 'test_field' field type in field_test_field_widget_info_alter().");
  }
336

337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
  /**
   * Returns field info definition.
   */
  protected function getExpectedFieldTypeDefinition() {
    return array(
      'test_field' => array(
        'label' => t('Test field'),
        'description' => t('Dummy field type used for tests.'),
        'settings' => array(
          'test_field_setting' => 'dummy test string',
          'changeable' => 'a changeable field setting',
          'unchangeable' => 'an unchangeable field setting',
        ),
        'instance_settings' => array(
          'test_instance_setting' => 'dummy test string',
          'test_cached_data' => FALSE,
        ),
        'default_widget' => 'test_field_widget',
        'default_formatter' => 'field_test_default',
356
        'class' => 'Drupal\field_test\Plugin\Field\FieldType\TestItem',
357 358 359 360 361 362 363 364 365 366
      ),
      'shape' => array(
        'label' => t('Shape'),
        'description' => t('Another dummy field type.'),
        'settings' => array(
          'foreign_key_name' => 'shape',
        ),
        'instance_settings' => array(),
        'default_widget' => 'test_field_widget',
        'default_formatter' => 'field_test_default',
367
        'class' => 'Drupal\field_test\Plugin\Field\FieldType\ShapeItem',
368 369 370 371 372 373 374 375 376
      ),
      'hidden_test_field' => array(
        'no_ui' => TRUE,
        'label' => t('Hidden from UI test field'),
        'description' => t('Dummy hidden field type used for tests.'),
        'settings' => array(),
        'instance_settings' => array(),
        'default_widget' => 'test_field_widget',
        'default_formatter' => 'field_test_default',
377
        'class' => 'Drupal\field_test\Plugin\Field\FieldType\HiddenTestItem',
378 379 380 381
      ),
    );
  }

382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
  /**
   * Tests that the extra fields can be translated.
   */
  function testFieldInfoExtraFieldsTranslation() {
    $this->enableModules(array('language', 'locale'));
    $this->installSchema('locale', array('locales_source', 'locales_target', 'locales_location'));
    foreach (array('en', 'hu') as $id) {
      $language = new Language(array(
        'id' => $id,
      ));
      language_save($language);
    }
    $locale_storage = $this->container->get('locale.storage');

    // Create test source string.
    $en_string = $locale_storage->createString(array(
      'source' => 'User name and password',
      'context' => '',
    ))->save();

    // Create translation for new string and save it.
    $translated_string = $this->randomString();
    $locale_storage->createTranslation(array(
      'lid' => $en_string->lid,
      'language' => 'hu',
      'translation' => $translated_string,
    ))->save();

    // Check that the label is translated.
    \Drupal::translation()->setDefaultLangcode('hu');
    $field_info = \Drupal::service('field.info');
    $user_fields = $field_info->getBundleExtraFields('user', 'user');
    $this->assertEqual($user_fields['form']['account']['label'], $translated_string);
  }
416

417
}