diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index e77d74a..1ff16ed 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -9,10 +9,11 @@ use Drupal\Core\Database\Connection; use Drupal\Core\Database\Database; +use Drupal\Core\Database\SchemaObjectExistsException; +use Drupal\Core\Database\Transaction; use Drupal\Core\Entity\Query\QueryInterface; use Drupal\Core\Entity\Schema\ContentEntitySchemaHandler; use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface; -use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Language\Language; use Drupal\field\FieldInfo; @@ -248,6 +249,34 @@ public function getRevisionDataTable() { } /** + * Check if the database schema has been installed, and install it if not. + * + * @todo Make this protected in https://drupal.org/node/2249113 + * + * @return bool + * TRUE if the database schema exists or could be created successfully, + * FALSE otherwise. + */ + public function ensureSchemaExists() { + try { + $schema_service = $this->database->schema(); + foreach ($this->getSchema() as $table_name => $table_schema) { + if (!$schema_service->tableExists($table_name)) { + $schema_service->createTable($table_name, $table_schema); + } + } + return TRUE; + } + // If another process has already created the cache table, attempting to + // recreate it will throw an exception. In this case just catch the + // exception and do nothing. + catch (SchemaObjectExistsException $e) { + return TRUE; + } + return FALSE; + } + + /** * {@inheritdoc} */ public function getSchema() { @@ -303,9 +332,6 @@ public function getTableMapping() { // @todo Rename 'log' to 'revision_log' in // https://drupal.org/node/2248991. $revision_metadata_fields = array_intersect(array('revision_timestamp', 'revision_uid', 'log'), $all_fields); - $revisionable_filter_callback = function (FieldDefinitionInterface $definition) { - return $definition->isRevisionable(); - }; switch ($this->getTableLayout()) { // The base layout stores all the base field values in the base table. @@ -403,13 +429,20 @@ protected function getColumnMapping($field_names) { * {@inheritdoc} */ protected function doLoadMultiple(array $ids = NULL) { + try { // Build and execute the query. - $records = $this - ->buildQuery($ids) - ->execute() - ->fetchAllAssoc($this->idKey); + $records = $this + ->buildQuery($ids) + ->execute() + ->fetchAllAssoc($this->idKey); + + return $this->mapFromStorageRecords($records); + } + catch (\Exception $e) { + // If the schema does not exist, there are no entities to load. + return array(); + } - return $this->mapFromStorageRecords($records); } /** @@ -700,6 +733,8 @@ public function delete(array $entities) { protected function doDelete($entities) { $ids = array_keys($entities); + // Because deleting entities requires loading them beforehand, we do not + // need to care about the schema not existing here. $this->database->delete($this->entityType->getBaseTable()) ->condition($this->idKey, $ids) ->execute(); @@ -735,8 +770,9 @@ protected function doDelete($entities) { * {@inheritdoc} */ public function save(EntityInterface $entity) { - $transaction = $this->database->startTransaction(); - try { + $try_again = FALSE; + + $do_save = function (EntityInterface $entity) { // Sync the changes made in the fields array to the internal values array. $entity->updateOriginalValues(); @@ -745,11 +781,33 @@ public function save(EntityInterface $entity) { // Ignore slave server temporarily. db_ignore_slave(); return $return; - } - catch (\Exception $e) { + }; + + $rethrow_exception = function (\Exception $e, Transaction $transaction) { $transaction->rollback(); watchdog_exception($this->entityTypeId, $e); throw new EntityStorageException($e->getMessage(), $e->getCode(), $e); + }; + + $transaction = $this->database->startTransaction(); + try { + return $do_save($entity); + } + catch (\Exception $e) { + // Try to create the schema. + if (!$try_again = $this->ensureSchemaExists()) { + $rethrow_exception($e, $transaction); + } + } + + // If the schema has just been created, try the save operation again. + if ($try_again) { + try { + return $do_save($entity); + } + catch (\Exception $e) { + $rethrow_exception($e, $transaction); + } } } diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php index cf8eb39..df64f45 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Query.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Query.php @@ -247,13 +247,19 @@ protected function finish() { * Returns the query result as entity IDs. */ protected function result() { - if ($this->count) { - return $this->sqlQuery->countQuery()->execute()->fetchField(); + try { + if ($this->count) { + return $this->sqlQuery->countQuery()->execute()->fetchField(); + } + // Return a keyed array of results. The key is either the revision_id or + // the entity_id depending on whether the entity type supports revisions. + // The value is always the entity id. + return $this->sqlQuery->execute()->fetchAllKeyed(); + } + catch (\Exception $e) { + // If the schema does not exist, there are no entity IDs to load. + return array(); } - // Return a keyed array of results. The key is either the revision_id or - // the entity_id depending on whether the entity type supports revisions. - // The value is always the entity id. - return $this->sqlQuery->execute()->fetchAllKeyed(); } /** diff --git a/core/modules/entity/entity.api.php b/core/modules/entity/entity.api.php deleted file mode 100644 index fac9a40..0000000 --- a/core/modules/entity/entity.api.php +++ /dev/null @@ -1,48 +0,0 @@ -create(array( - 'name' => t('General discussion'), - 'description' => '', - 'parent' => array(0), - 'vid' => 'forums', - 'forum_container' => 0, - ))->save(); - } -} - -/** - * Allows to react the uninstallation of entity schema. - * - * After modules have been uninstalled Entity module checks for any removed - * entity types and installs the entity schema if the entity type's storage - * supports automatic schema generation. This hook is invoked after the entity - * schema has been uninstalled. - * - * @param \Drupal\Core\Entity\EntityStorageInterface[]|\Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface[] $storages - * An array of entity storages of the entity types whose schemas have been - * uninstalled keyed by entity type ID. - */ -function hook_entity_schema_uninstalled(array $storages) { - // Delete some derived data that depends on nodes. - \Drupal::state()->delete('mymodule.node_count'); -} diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module index 625beab..1fa3852 100644 --- a/core/modules/entity/entity.module +++ b/core/modules/entity/entity.module @@ -9,7 +9,6 @@ */ use Drupal\Core\Config\Entity\ConfigEntityStorage; -use Drupal\Core\Entity\Schema\ContentEntitySchemaHandlerInterface; /** * Implements hook_help(). @@ -100,77 +99,16 @@ function entity_entity_bundle_delete($entity_type_id, $bundle) { } /** - * Implements hook_modules_installed(). - */ -function entity_modules_installed($modules) { - // Install entity type tables. - $entity_manager = \Drupal::entityManager(); - $schema = \Drupal::database()->schema(); - $definitions = $entity_manager->getDefinitions(); - $storages = array(); - - foreach ($modules as $module) { - foreach ($definitions as $entity_type_id => $entity_type) { - if ($entity_type->getProvider() == $module) { - $storage = $entity_manager->getStorage($entity_type_id); - if ($storage instanceof ContentEntitySchemaHandlerInterface) { - foreach ($storage->getSchema() as $table_name => $table_schema) { - if (!$schema->tableExists($table_name)) { - $schema->createTable($table_name, $table_schema); - $storages[$entity_type_id] = $storage; - } - } - } - } - } - } - - \Drupal::moduleHandler()->invokeAll('entity_schema_installed', array($storages)); -} - -/** * Implements hook_module_preuninstall(). */ function entity_module_preuninstall($module) { - $entity_manager = \Drupal::entityManager(); - $definitions = $entity_manager->getDefinitions(); - // Clean up all entity bundles (including field instances) of every entity // type provided by the module that is being uninstalled. - foreach ($definitions as $entity_type_id => $entity_type) { + foreach (\Drupal::entityManager()->getDefinitions() as $entity_type_id => $entity_type) { if ($entity_type->getProvider() == $module) { foreach (array_keys(entity_get_bundles($entity_type_id)) as $bundle) { entity_invoke_bundle_hook('delete', $entity_type_id, $bundle); } } } - - foreach ($definitions as $entity_type_id => $entity_type) { - if ($entity_type->getProvider() == $module) { - // Remove entity tables. - $storage = $entity_manager->getStorage($entity_type_id); - if ($storage instanceof ContentEntitySchemaHandlerInterface) { - foreach ($storage->getSchema() as $table_name => $table_schema) { - $GLOBALS['entity_schema_uninstall']['storages'][$entity_type_id] = $storage; - $GLOBALS['entity_schema_uninstall']['tables'][] = $table_name; - } - } - } - } -} - - -/** - * Implements hook_modules_uninstalled(). - */ -function entity_modules_uninstalled($modules) { - if (!empty($GLOBALS['entity_schema_uninstall'])) { - $schema = \Drupal::database()->schema(); - foreach ($GLOBALS['entity_schema_uninstall']['tables'] as $table_name) { - if ($schema->tableExists($table_name)) { - $schema->dropTable($table_name); - } - } - \Drupal::moduleHandler()->invokeAll('entity_schema_uninstalled', array($GLOBALS['entity_schema_uninstall']['storages'])); - } } diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 70b53b0..7f975e9 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -35,6 +35,16 @@ function forum_install() { ), ))->save(); + // Create a default forum so forum posts can be created. + $term = entity_create('taxonomy_term', array( + 'name' => t('General discussion'), + 'description' => '', + 'parent' => array(0), + 'vid' => 'forums', + 'forum_container' => 0, + )); + $term->save(); + // Create the instance on the bundle. entity_create('field_instance_config', array( 'field_name' => 'taxonomy_forums', @@ -58,6 +68,7 @@ function forum_install() { 'weight' => 10, )) ->save(); + entity_get_display('node', 'forum', 'teaser') ->setComponent('taxonomy_forums', array( 'type' => 'taxonomy_term_reference_link', @@ -78,22 +89,6 @@ function forum_install() { } /** - * Implements hook_entity_schema_installed(). - */ -function forum_entity_schema_installed(array $storages) { - if (isset($storages['taxonomy_term'])) { - // Create a default forum so forum posts can be created. - $storages['taxonomy_term']->create(array( - 'name' => t('General discussion'), - 'description' => '', - 'parent' => array(0), - 'vid' => 'forums', - 'forum_container' => 0, - ))->save(); - } -} - -/** * Implements hook_uninstall(). */ function forum_uninstall() { diff --git a/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php b/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php index 25c61a8..de2f399 100644 --- a/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php +++ b/core/modules/user/lib/Drupal/user/Tests/UserInstallTest.php @@ -39,7 +39,7 @@ protected function setUp() { parent::setUp(); $this->container->get('module_handler')->loadInclude('user', 'install'); $this->installEntitySchema('user'); - user_entity_schema_installed(array('user' => $this->container->get('entity.manager')->getStorage('user'))); + user_install(); } diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 0640af2..b1c1064 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -5,8 +5,6 @@ * Install, update and uninstall functions for the user module. */ -use Drupal\Core\Entity\ContentEntityDatabaseStorage; - /** * Implements hook_schema(). */ @@ -63,33 +61,33 @@ function user_schema() { } /** - * Implements hook_entity_schema_installed(). + * Implements hook_install(). */ -function user_entity_schema_installed(array $storages) { - if (isset($storages['user'])) { - // Insert a row for the anonymous user. - db_insert('users') - ->fields(array( - 'uid' => 0, - 'uuid' => \Drupal::service('uuid')->generate(), - 'name' => '', - 'mail' => '', - 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, - )) - ->execute(); +function user_install() { + // @todo Clean this up in https://drupal.org/node/2249113 + \Drupal::entityManager()->getStorage('user')->ensureSchemaExists();; + // Insert a row for the anonymous user. + db_insert('users') + ->fields(array( + 'uid' => 0, + 'uuid' => \Drupal::service('uuid')->generate(), + 'name' => '', + 'mail' => '', + 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, + )) + ->execute(); - // We need some placeholders here as name and mail are uniques. - // This will be changed by the settings form in the installer. - db_insert('users') - ->fields(array( - 'uid' => 1, - 'uuid' => \Drupal::service('uuid')->generate(), - 'name' => 'placeholder-for-uid-1', - 'mail' => 'placeholder-for-uid-1', - 'created' => REQUEST_TIME, - 'status' => 1, - 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, - )) - ->execute(); - } + // We need some placeholders here as name and mail are uniques. + // This will be changed by the settings form in the installer. + db_insert('users') + ->fields(array( + 'uid' => 1, + 'uuid' => \Drupal::service('uuid')->generate(), + 'name' => 'placeholder-for-uid-1', + 'mail' => 'placeholder-for-uid-1', + 'created' => REQUEST_TIME, + 'status' => 1, + 'langcode' => \Drupal::languageManager()->getDefaultLanguage()->id, + )) + ->execute(); } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/pager/PagerPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/pager/PagerPluginBase.php index d91e4bf..1d280c6 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/pager/PagerPluginBase.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/pager/PagerPluginBase.php @@ -178,7 +178,14 @@ public function useCountQuery() { * itself being executed. */ public function executeCountQuery(&$count_query) { - $this->total_items = $count_query->execute()->fetchField(); + try { + $this->total_items = $count_query->execute()->fetchField(); + } + catch (\Exception $e) { + // If the database schema does not exist, there are no items in the + // database. + $this->total_items = 0; + } if (!empty($this->options['offset'])) { $this->total_items -= $this->options['offset']; } diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php index 175e7f4..ad47e15 100644 --- a/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php +++ b/core/modules/views/lib/Drupal/views/Plugin/views/query/Sql.php @@ -1423,10 +1423,15 @@ function execute(ViewExecutable $view) { $query->range($offset, $limit); } - $result = $query->execute(); - $result->setFetchMode(\PDO::FETCH_CLASS, 'Drupal\views\ResultRow'); + try { + $result = $query->execute(); + $result->setFetchMode(\PDO::FETCH_CLASS, 'Drupal\views\ResultRow'); + $view->result = iterator_to_array($result); + } + catch (\Exception $e) { + $view->result = array(); + } - $view->result = iterator_to_array($result); $view->pager->postExecute($view->result); $view->pager->updatePageInfo();