/**
  * Tests some possible entity table updates for a revision view.
  */
 public function testVariousTableUpdatesForRevisionView()
 {
     // base + revision <-> base + translation + revision
     $this->updateEntityTypeToRevisionable();
     // Multiple changes, so we have to invalidate the caches, otherwise
     // the second update will revert the first.
     $this->entityManager->clearCachedDefinitions();
     list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE);
     $this->assertEqual('entity_test_update_revision', $view->get('base_table'));
     $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']);
     $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']);
     $this->updateEntityTypeToTranslatable();
     $this->entityDefinitionUpdateManager->applyUpdates();
     list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE);
     $this->assertEqual('entity_test_update_revision', $view->get('base_table'));
     $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']);
     $this->assertEqual('entity_test_update_revision_data', $display['display_options']['fields']['name']['table']);
     $this->updateEntityTypeToNotTranslatable();
     $this->entityDefinitionUpdateManager->applyUpdates();
     list($view, $display) = $this->getUpdatedViewAndDisplay(TRUE);
     $this->assertEqual('entity_test_update_revision', $view->get('base_table'));
     $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['id']['table']);
     $this->assertEqual('entity_test_update_revision', $display['display_options']['fields']['name']['table']);
     $this->resetEntityType();
 }
 /**
  * {@inheritdoc}
  */
 public function applyNewStorage()
 {
     // The first call is for making entity types revisionable, the second call
     // is for adding required fields.
     $this->updateManager->applyUpdates();
     $this->updateManager->applyUpdates();
     return $this;
 }
 /**
  * Asserts the given module can be installed and uninstalled.
  *
  * @param string $module_name
  *   The module to install and uninstall.
  */
 protected function assertModuleInstallUninstall($module_name)
 {
     $this->enableModules([$module_name]);
     $this->entityDefinitionUpdateManager->applyUpdates();
     $this->assertTrue($this->getModuleHandler()->moduleExists($module_name), $module_name . ' module is enabled.');
     $this->getModuleInstaller()->uninstall([$module_name]);
     $this->entityDefinitionUpdateManager->applyUpdates();
     $this->assertFalse($this->getModuleHandler()->moduleExists($module_name), $module_name . ' module is disabled.');
 }
 /**
  * Executes field storage definition updates if needed.
  *
  * @param array $entity_types
  *   A list of entity type definitions to be processed.
  */
 public function updateDefinitions(array $entity_types)
 {
     // Handle field storage definition creation, if needed.
     // @todo Generalize this code in https://www.drupal.org/node/2346013.
     // @todo Handle initial values in https://www.drupal.org/node/2346019.
     if ($this->updateManager->needsUpdates()) {
         foreach ($entity_types as $entity_type_id => $entity_type) {
             $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
             $installed_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
             foreach (array_diff_key($storage_definitions, $installed_storage_definitions) as $storage_definition) {
                 /** @var $storage_definition \Drupal\Core\Field\FieldStorageDefinitionInterface */
                 if ($storage_definition->getProvider() == 'content_translation') {
                     $this->updateManager->installFieldStorageDefinition($storage_definition->getName(), $entity_type_id, 'content_translation', $storage_definition);
                 }
             }
         }
     }
 }
 /**
  * Check that field schema is correctly handled with long-named fields.
  */
 function testLongNameFieldIndexes()
 {
     $this->addLongNameBaseField();
     $entity_type_id = 'entity_test_update';
     $entity_type = $this->entityManager->getDefinition($entity_type_id);
     $definitions = EntityTestUpdate::baseFieldDefinitions($entity_type);
     $name = 'new_long_named_entity_reference_base_field';
     $this->entityDefinitionUpdateManager->installFieldStorageDefinition($name, $entity_type_id, 'entity_test', $definitions[$name]);
     $this->assertFalse($this->entityDefinitionUpdateManager->needsUpdates(), 'Entity and field schema data are correctly detected.');
 }
Example #6
0
 /**
  * Starts the database update batch process.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The current request object.
  */
 protected function triggerBatch(Request $request)
 {
     // During the update, bring the site offline so that schema changes do not
     // affect visiting users.
     $maintenance_mode = $this->config('system.maintenance')->get('enabled');
     if (isset($maintenance_mode)) {
         $_SESSION['maintenance_mode'] = $maintenance_mode;
     }
     if (empty($_SESSION['maintenance_mode'])) {
         $this->state->set('system.maintenance_mode', TRUE);
     }
     $operations = array();
     // Resolve any update dependencies to determine the actual updates that will
     // be run and the order they will be run in.
     $start = $this->getModuleUpdates();
     $updates = update_resolve_dependencies($start);
     // Store the dependencies for each update function in an array which the
     // batch API can pass in to the batch operation each time it is called. (We
     // do not store the entire update dependency array here because it is
     // potentially very large.)
     $dependency_map = array();
     foreach ($updates as $function => $update) {
         $dependency_map[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : array();
     }
     // Determine updates to be performed.
     foreach ($updates as $function => $update) {
         if ($update['allowed']) {
             // Set the installed version of each module so updates will start at the
             // correct place. (The updates are already sorted, so we can simply base
             // this on the first one we come across in the above foreach loop.)
             if (isset($start[$update['module']])) {
                 drupal_set_installed_schema_version($update['module'], $update['number'] - 1);
                 unset($start[$update['module']]);
             }
             $operations[] = array('update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
         }
     }
     // Lastly, perform entity definition updates, which will update storage
     // schema if needed. If module update functions need to work with specific
     // entity schema they should call the entity update service for the specific
     // update themselves.
     // @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyEntityUpdate()
     // @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyFieldUpdate()
     if ($this->entityDefinitionUpdateManager->needsUpdates()) {
         $operations[] = array('update_entity_definitions', array());
     }
     $batch['operations'] = $operations;
     $batch += array('title' => $this->t('Updating'), 'init_message' => $this->t('Starting updates'), 'error_message' => $this->t('An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.'), 'finished' => array('\\Drupal\\system\\Controller\\DbUpdateController', 'batchFinished'));
     batch_set($batch);
     return batch_process('update.php/results', Url::fromRoute('system.db_update', array('op' => 'start')));
 }
 /**
  * Tests that entity updates are correctly reported in the status report page.
  */
 function testStatusReport()
 {
     // Create a test entity.
     $entity = EntityTest::create(['name' => $this->randomString(), 'user_id' => mt_rand()]);
     $entity->save();
     // Check that the status report initially displays no error.
     $this->drupalGet('admin/reports/status');
     $this->assertNoRaw('Out of date');
     $this->assertNoRaw('Mismatched entity and/or field definitions');
     // Enable an entity update and check that we have a dedicated status report
     // item.
     $this->container->get('state')->set('entity_test.remove_name_field', TRUE);
     $this->drupalGet('admin/reports/status');
     $this->assertNoRaw('Out of date');
     $this->assertRaw('Mismatched entity and/or field definitions');
     // Enable a db update and check that now the entity update status report
     // item is no longer displayed. We assume an update function will fix the
     // mismatch.
     $this->enableUpdates('entity_test', 'status_report', 8001);
     $this->drupalGet('admin/reports/status');
     $this->assertRaw('Out of date');
     $this->assertRaw('Mismatched entity and/or field definitions');
     // Apply db updates and check that entity updates were not applied.
     $this->applyUpdates();
     $this->drupalGet('admin/reports/status');
     $this->assertNoRaw('Out of date');
     $this->assertRaw('Mismatched entity and/or field definitions');
     // Check that en exception would be triggered when trying to apply them with
     // existing data.
     $message = 'Entity updates cannot run if entity data exists.';
     try {
         $this->updatesManager->applyUpdates();
         $this->fail($message);
     } catch (FieldStorageDefinitionUpdateForbiddenException $e) {
         $this->pass($message);
     }
     // Check the status report is the same after trying to apply updates.
     $this->drupalGet('admin/reports/status');
     $this->assertNoRaw('Out of date');
     $this->assertRaw('Mismatched entity and/or field definitions');
     // Delete entity data, enable a new update, run updates again and check that
     // entity updates were not applied even when no data exists.
     $entity->delete();
     $this->enableUpdates('entity_test', 'status_report', 8002);
     $this->applyUpdates();
     $this->drupalGet('admin/reports/status');
     $this->assertNoRaw('Out of date');
     $this->assertRaw('Mismatched entity and/or field definitions');
 }
 /**
  * Tests updating entity schema and creating a revisionable base field.
  *
  * This tests updating entity schema and creating a revisionable base field
  * at the same time when there are no existing entities.
  */
 public function testEntityTypeSchemaUpdateAndRevisionableBaseFieldCreateWithoutData()
 {
     $this->updateEntityTypeToRevisionable();
     $this->addRevisionableBaseField();
     $message = 'Successfully updated entity schema and created revisionable base field at the same time.';
     // Entity type updates create base fields as well, thus make sure doing both
     // at the same time does not lead to errors due to the base field being
     // created twice.
     try {
         $this->entityDefinitionUpdateManager->applyUpdates();
         $this->pass($message);
     } catch (\Exception $e) {
         $this->fail($message);
         throw $e;
     }
 }
 /**
  * Tests ::applyEntityUpdate() and ::applyFieldUpdate().
  */
 public function testSingleActionCalls()
 {
     // Ensure that the methods return FALSE when called with bogus information.
     $this->assertFalse($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'foo'), 'Calling applyEntityUpdate() with a non-existent entity returns FALSE.');
     $this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'foo', 'bar'), 'Calling applyFieldUpdate() with a non-existent entity returns FALSE.');
     $this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update', 'bar'), 'Calling applyFieldUpdate() with a non-existent field returns FALSE.');
     $this->assertFalse($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update'), 'Calling applyEntityUpdate() with an $op that is not applicable to the entity type returns FALSE.');
     $this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_DELETED, 'entity_test_update', 'new_base_field'), 'Calling applyFieldUpdate() with an $op that is not applicable to the field returns FALSE.');
     // Create a new base field.
     $this->addRevisionableBaseField();
     $this->assertTrue($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update', 'new_base_field'), 'Calling applyFieldUpdate() correctly returns TRUE.');
     $this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
     // Make the entity type revisionable.
     $this->updateEntityTypeToRevisionable();
     $this->assertTrue($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_UPDATED, 'entity_test_update'), 'Calling applyEntityUpdate() correctly returns TRUE.');
     $this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
 }
 /**
  * Ensures that a new entity level index is created when data exists.
  *
  * @see Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema::onEntityTypeUpdate
  */
 public function testCreateIndexUsingEntityStorageSchemaWithData()
 {
     // Save an entity.
     $name = $this->randomString();
     $storage = $this->entityManager->getStorage('entity_test_update');
     $entity = $storage->create(array('name' => $name));
     $entity->save();
     // Create an index.
     $indexes = array('entity_test_update__type_index' => array('type'));
     $this->state->set('entity_test_update.additional_entity_indexes', $indexes);
     $this->entityDefinitionUpdateManager->applyUpdates();
     $this->assertTrue($this->database->schema()->indexExists('entity_test_update', 'entity_test_update__type_index'), "New index 'entity_test_update__type_index' has been created on the 'entity_test_update' table.");
     // Check index size in for MySQL.
     if (Database::getConnection()->driver() == 'mysql') {
         $result = Database::getConnection()->query('SHOW INDEX FROM {entity_test_update} WHERE key_name = \'entity_test_update__type_index\' and column_name = \'type\'')->fetchObject();
         $this->assertEqual(191, $result->Sub_part, 'The index length has been restricted to 191 characters for UTF8MB4 compatibility.');
     }
 }
 /**
  * Tests creating a multi-field index when there are existing entities.
  */
 public function testEntityIndexCreateWithData()
 {
     // Save an entity.
     $name = $this->randomString();
     $entity = $this->entityManager->getStorage('entity_test_update')->create(array('name' => $name));
     $entity->save();
     // Add an entity index, run the update. For now, it's expected to throw an
     // exception.
     // @todo Improve SqlContentEntityStorageSchema::requiresEntityDataMigration()
     //   to return FALSE when only index changes are required, so that it can be
     //   applied on top of existing data: https://www.drupal.org/node/2340993.
     $this->addEntityIndex();
     try {
         $this->entityDefinitionUpdateManager->applyUpdates();
         $this->fail('EntityStorageException thrown when trying to apply an update that requires data migration.');
     } catch (EntityStorageException $e) {
         $this->pass('EntityStorageException thrown when trying to apply an update that requires data migration.');
     }
 }
 /**
  * Tests updating a base field when it has existing data.
  */
 public function testBaseFieldEntityKeyUpdateWithExistingData()
 {
     // Add the base field and run the update.
     $this->addBaseField();
     $this->entityDefinitionUpdateManager->applyUpdates();
     // Save an entity with the base field populated.
     $this->entityManager->getStorage('entity_test_update')->create(['new_base_field' => $this->randomString()])->save();
     // Save an entity with the base field not populated.
     /** @var \Drupal\entity_test\Entity\EntityTestUpdate $entity */
     $entity = $this->entityManager->getStorage('entity_test_update')->create();
     $entity->save();
     // Promote the base field to an entity key. This will trigger the addition
     // of a NOT NULL constraint.
     $this->makeBaseFieldEntityKey();
     // Try to apply the update and verify they fail since we have a NULL value.
     $message = 'An error occurs when trying to enabling NOT NULL constraints with NULL data.';
     try {
         $this->entityDefinitionUpdateManager->applyUpdates();
         $this->fail($message);
     } catch (EntityStorageException $e) {
         $this->pass($message);
     }
     // Check that the update is correctly applied when no NULL data is left.
     $entity->set('new_base_field', $this->randomString());
     $entity->save();
     $this->entityDefinitionUpdateManager->applyUpdates();
     $this->pass('The update is correctly performed when no NULL data exists.');
     // Check that the update actually applied a NOT NULL constraint.
     $entity->set('new_base_field', NULL);
     $message = 'The NOT NULL constraint was correctly applied.';
     try {
         $entity->save();
         $this->fail($message);
     } catch (EntityStorageException $e) {
         $this->pass($message);
     }
 }