denormalizer-8.x-1.x-dev/src/Denormalizer.php
src/Denormalizer.php
<?php namespace Drupal\denormalizer; use Drupal\Component\Utility\Unicode; use Drupal\Core\Database\Database; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Database\Query\SelectInterface; /** * Class Denormalizer. * * @package Drupal\denormalizer */ class Denormalizer { /** * The DN tables to be exported/created. * * @var array */ private $dwTables = []; protected $entityTypeManager; /** * Generate all SQL for views. */ public function build() { $databases = Database::getAllConnectionInfo(); $other_database = $databases['default']['default']; $other_database['prefix'] = ''; Database::addConnectionInfo('external', 'default', $other_database); $d = denormalizer_get_info(); foreach ($d as $denormalizer_view => $dn_info) { $this->add($denormalizer_view, $dn_info); } } /** * Add a table to the denormalizer...list. * * @param string $denormalizer_view * Name of the view. * @param array $dn_info * Info bit from hook_denormalizer_info(). */ public function add($denormalizer_view, array $dn_info) { $query = $this->generateQuery($denormalizer_view, $dn_info); $this->dwTables[$denormalizer_view] = $query; } /** * Create a view. * * @param string $denormalizer_view * Name of view to create. * @param array $dn_info * Param info. * * @return string * SQL to use to pull denormalized data or create a view. * * @throws \Exception */ public function generateQuery($denormalizer_view, array $dn_info) { $d = denormalizer_get_info(); $schema = drupal_get_module_schema($d[$denormalizer_view]['schema_module'], $d[$denormalizer_view]['base table']); $property_info = []; if (isset($dn_info['entity_type'])) { $injected_database = Database::getConnection(); if ($dn_info['entity_type'] !== 'webform_submission') { $property_info = \Drupal::service('entity_field.manager') ->getBaseFieldDefinitions($dn_info['entity_type']); $base_fields = []; $results = $injected_database->query("SHOW COLUMNS FROM " . $dn_info['entity_type'] . ";"); $base_columns = []; foreach ($results as $index => $info) { array_push($base_columns, $info->Field); array_push($base_fields, $info->Field); } $results = $injected_database->query("SHOW COLUMNS FROM " . $dn_info['entity_type'] . "_field_data;"); $base_node_field = []; foreach ($results as $index => $info) { array_push($base_node_field, $info->Field); if (!in_array($info->Field, $base_fields, TRUE)) { array_push($base_fields, $info->Field); } } } else { $results = $injected_database->query("SHOW COLUMNS FROM " . $dn_info['entity_type'] . "_data;"); $base_columns = []; foreach ($results as $index => $info) { array_push($base_columns, $info->Field); } } } else { if (empty($dn_info['external'])) { $property_info = $this->denormMetadataConvertSchema($dn_info['base table']); } } $base_table = $this->entityTypeManager() ->getDefinition($dn_info['entity_type']) ->getBaseTable(); $injected_database = Database::getConnection(); if (($dn_info['entity_type'] !== 'webform_submission')) { $q = $injected_database->select($base_table, $denormalizer_view); $q->leftJoin($base_table . '_field_data', 'field_data', "field_data.nid = $denormalizer_view.nid"); } elseif ($dn_info['entity_type'] == 'webform_submission') { $q = $injected_database->select($base_table . '_data', $denormalizer_view); } if (!empty($dn_info['external'])) { $q->fields($denormalizer_view); } $properties_columns = []; foreach ($property_info as $property => $info) { if ($info['schema field']) { if (in_array($info['schema field'], [ 'revision_timestamp', 'revision_uid', 'revision_log', 'revision_default', 'path', 'menu_link', 'moderation_state', ])) { // ??? this column does not exist continue; } if (!empty($schema[$dn_info['base table']]['fields'][$info['schema field']]['serialize'])) { // No serialized fields. continue; } $properties_columns[$info['schema field']] = $info['schema field']; if (isset($info['type']) && $info['type'] == 'date') { $q->addExpression("from_unixtime($denormalizer_view.{$info['schema field']})", "{$info['schema field']}"); } else { // @todo: How do we get column values for base fields and also field. // API fields?. // $baseColumnName = $info->getFieldStorageDefinition(). // ->getMainPropertyName();. $q->fields($denormalizer_view, [$property]); } } else { // Todo handle serializable and date fields. if (in_array($property, $base_node_field) && in_array($property, $base_columns)) { $q->fields($denormalizer_view, [$property]); } elseif (in_array($property, $base_node_field)) { $q->fields('field_data', [$property]); } elseif (in_array($property, $base_columns)) { $q->fields($denormalizer_view, [$property]); } else { // ??? this column does not exist. continue; } } } if ($dn_info['entity_type'] == 'webform_submission' && isset($base_columns)) { foreach ($base_columns as $index => $property) { $q->fields($denormalizer_view, [$property]); } } // Track fields we already processed :. if (isset($dn_info['entity_type']) && $dn_info['fields'] !== FALSE && $dn_info['entity_type'] !== 'webform_submission') { $entity_info = $this->entityTypeManager() ->getDefinition($dn_info['entity_type']); if (count($dn_info['bundles']) != 0) { foreach ($dn_info['bundles'] as $bundles) { $fields = \Drupal::service('entity_field.manager') ->getFieldDefinitions($dn_info['entity_type'], $bundles); $flag = 0; $base_fields = $this->processFields($fields, $entity_info, $base_fields, $denormalizer_view, $q, $flag); } $q->condition($denormalizer_view . ".type", $dn_info['bundles'], 'IN'); } else { $fields = \Drupal::service('entity_field.manager') ->getFieldStorageDefinitions($dn_info['entity_type']); $flag = 1; $base_fields = $this->processFields($fields, $entity_info, $base_fields, $denormalizer_view, $q, $flag); } } elseif ($dn_info['entity_type'] == 'webform_submission') { $q->condition($denormalizer_view . ".webform_id", $dn_info['bundles'], '='); } return $q; } /** * Converts the schema information available for the given table to property. * * Info. * * @param string $table * The name of the table as used in hook_schema(). * * @return array * An array of property info as suiting for hook_entity_property_info(). */ public function denormMetadataConvertSchema($table) { // $schema = StorableConfigBase::getSchemaWrapper();. $properties = []; // Todo Find the best way to get the schema ??. $injected_database = Database::getConnection(); $result = $injected_database->query("SHOW COLUMNS FROM " . $table . ";"); foreach ($result as $index => $info) { $type = ''; if (strpos($info->Type, 'int') !== FALSE || strpos($info->Type, 'serial') !== FALSE || strpos($info->Type, 'date') !== FALSE) { $type = 'integer'; } elseif (strpos($info->Type, 'float') !== FALSE || strpos($info->Type, 'numeric') !== FALSE) { $type = 'decimal'; } elseif (strpos($info->Type, 'char') !== FALSE || strpos($info->Type, 'varchar') !== FALSE || strpos($info->Type, 'text') !== FALSE) { $type = 'text'; } if ($type) { $properties[$info->Field] = [ 'type' => $type, 'label' => Unicode::ucfirst($info->Field), 'schema field' => $info->Field, // As we cannot know about any setter access, leave out the setter // callback. For getting usually no further access callback is needed. ]; if ($info->Type == 'serial') { $properties[$info->Field]['validation callback'] = 'entity_metadata_validate_integer_positive'; } } } return $properties; } /** * Process fields. * * @param array $fields * The fields definition to process. * @param \Drupal\Core\Entity\EntityTypeInterface $entity_info * The entity info. * @param array $base_fields * The base fields. * @param string $denormalizer_view * The view to create. * @param \Drupal\Core\Database\Query\SelectInterface $q * The query. * @param bool $flag * A flag, whether the bundle is blank or no, this using two different way. * To get fields info. * * @return array * Return the base fields for checking to avoid replication of the same. * Field name from different bundles. */ public function processFields(array $fields, EntityTypeInterface $entity_info, array $base_fields, $denormalizer_view, SelectInterface $q, $flag) { foreach ($fields as $field_name => $info) { if (in_array($field_name, $base_fields, TRUE)) { // This column was already processed. continue; } if (empty($info->getTargetBundle())) { // This column does not exist. continue; } if ($flag) { $isMultiple = $info->isMultiple(); $columns = $info->getColumns(); $type = $info->getType(); $cardinality = $info->getCardinality(); } else { $isMultiple = $fields[$field_name]->getFieldStorageDefinition() ->isMultiple(); $columns = $fields[$field_name]->getFieldStorageDefinition() ->getColumns(); $type = $fields[$field_name]->getType(); $cardinality = $fields[$field_name]->getFieldStorageDefinition() ->getCardinality(); } if (!isset($used[$field_name])) { $used[$field_name] = $field_name; if (!$isMultiple) { $cols = []; if ($type != 'entity_reference') { $cols = []; if ($type == 'date') { // "Date" time, we need to special handle :( Cast into a real // datetime so ETL tools can pick it up. $q->addJoin('LEFT', 'node__' . $field_name, $field_name, "$field_name.entity_id = $denormalizer_view.{$entity_info->getKey('id')}", []); $col1 = $field_name . '_value'; $q->addExpression("cast($col1 as datetime)", $col1); if (!empty($fields[$field_name]['settings']['todate'])) { $col2 = $field_name . '_value2'; $q->addExpression("cast($col2 as datetime)", $col2); } // Skip this field. continue; } foreach ($columns as $column => $info) { // No full text fields. if ($type == 'text_long' || $type == 'text_with_summary') { continue; } $cols[] = $field_name . '_' . $column; } // Todo add entity_type condition like D7 ??. if (!empty($cols)) { // Join field table. Add columns to query. $q->addJoin('LEFT', 'node__' . $field_name, $field_name, "$field_name.entity_id = $denormalizer_view.{$entity_info->getKey('id')}", []); $q->fields($field_name, $cols); } } else { // Handle taxonomy terms. We get the term value. $q->addJoin('LEFT', 'node__' . $field_name, $field_name, "$field_name.entity_id = $denormalizer_view.{$entity_info->getKey('id')}", []); $q->addJoin('LEFT', 'taxonomy_term_field_data', "{$field_name}_tax", "{$field_name}_target_id = {$field_name}_tax.tid"); $cols[] = 'name'; $q->fields("{$field_name}_tax", $cols); } } elseif ($type == 'entity_reference') { // Handle taxonomy terms. We get the values and concatenate them into. // One column. $q->addJoin('LEFT', 'node__' . $field_name, $field_name, "$field_name.entity_id = $denormalizer_view.{$entity_info->getKey('id')}", []); $q->addJoin('LEFT', 'taxonomy_term_field_data', "{$field_name}_tax", "{$field_name}_target_id = {$field_name}_tax.tid"); $q->addExpression("group_concat(distinct {$field_name}_tax.name SEPARATOR '|')", "{$field_name}_values"); // Group by primary key, so group_concat will work. $q->groupBy("$denormalizer_view.{$entity_info->getKey('id')}"); } elseif ($cardinality != 1) { if ($fields[$field_name]->getType() == 'list_string') { $q->addJoin('LEFT', 'node__' . $field_name, $field_name, "$field_name.entity_id = $denormalizer_view.{$entity_info->getKey('id')}", []); $q->addExpression("group_concat(distinct {$field_name}_value SEPARATOR '|')", "{$field_name}_values"); // Group by primary key, so group_concat will work. $q->groupBy("$denormalizer_view.{$entity_info->getKey('id')}"); } } } array_push($base_fields, $field_name); } return $base_fields; } /** * Create all database views. * * @param bool $reset * Whether or not to drop and recreate tables. * * @throws \Exception */ public function execute($reset = FALSE) { $config = \Drupal::service('config.factory') ->getEditable('denormalizer.settings'); $prefix = $config->get('view_prefix', 'snowflake_'); $db_prefix = ''; $db_target = denormalizer_target_db(); if ($db = denormalizer_target_db()) { $db_prefix = "{$db}."; $injected_database = Database::getConnection(); $target_exists = $injected_database->query('SELECT 1 FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = :name', [':name' => $db_target]) ->fetchField(); // $target_exists = db_query('SELECT 1 FROM INFORMATION_SCHEMA.SCHEMATA. // WHERE SCHEMA_NAME = :name', array(':name' => $db_target)). // ->fetchField();. if ($reset) { if ($target_exists) { $injected_database = Database::getConnection(); $injected_database->query("DROP DATABASE $db_target"); // Db_query("DROP DATABASE $db_target");. \Drupal::messenger()->addStatus(t('Dropped %target database.', ['%target' => $db_target])); $target_exists = FALSE; } } if (!$target_exists) { $injected_database = Database::getConnection(); $injected_database->query("CREATE DATABASE $db_target"); // db_query("CREATE DATABASE $db_target");. \Drupal::messenger()->addStatus(t('Created %target database.', ['%target' => $db_target])); } } if ($config->get('sql_mode', 'views') == 'views') { $type = 'VIEW'; } else { $type = 'TABLE'; } $out = []; $dn_info = denormalizer_get_info(); $all_start = microtime(TRUE); // Hack to get around https://stackoverflow.com/questions/36882149/. // Error-1067-42000-invalid-default-value-for-created-at. // Some tables have a CURRENT_TIMESTAMP so the derived table gets a zero. // Date. $injected_database = Database::getConnection(); $injected_database->query("SET sql_mode = 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'"); // db_query("SET sql_mode = 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,. // IGNORE_SPACE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'"); // A little more flexibility for handling multiple values. $config->set('group_concat_max_len', 255)->save(); $group_concat_max_len = intval($config->get('group_concat_max_len')); $injected_database = Database::getConnection(); $injected_database->query("SET group_concat_max_len = $group_concat_max_len"); foreach ($this->dwTables as $denormalizer_view => $final_q) { $target = "{$db_prefix}" . "`$prefix$denormalizer_view`"; $start = microtime(TRUE); $id_key = denormalizer_get_primary_key($denormalizer_view); $injected_database = Database::getConnection(); $reset = ($type == 'VIEW') || (!$injected_database->query('SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = :schema AND TABLE_NAME = :name', [ ':schema' => $db_target, ':name' => $denormalizer_view, ])->fetchField() || $injected_database->query('SELECT 1 FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = :schema AND TABLE_NAME = :name', [ ':schema' => $db_target, ':name' => $denormalizer_view, ])->fetchField()); if (empty($reset)) { if (!$config->get('max_changed_' . $denormalizer_view)) { // No reset, but we can't reload. $injected_database = Database::getConnection(); $count = $injected_database->query("SELECT COUNT(*) FROM $target") ->fetchField(); // $count = db_query("SELECT COUNT(*) FROM $target")->fetchField();. if ($count > 0) { $injected_database = Database::getConnection(); $injected_database->query("TRUNCATE $target"); // db_query("TRUNCATE $target");. $out[] = t('Emptied :target.', [':target' => $target]); } } else { if (isset($dn_info[$denormalizer_view]['changed_key']) && $config->get('max_changed_' . $denormalizer_view)) { // If there is a last changed key, only replicate records. // That have changed. $final_q->havingCondition($dn_info[$denormalizer_view]['changed_key'], $config->get('max_changed_' . $denormalizer_view), '>'); } } // Allow altering query before update. \Drupal::moduleHandler() ->alter('denormalizer', $final_q, $denormalizer_view, $dn_info[$denormalizer_view]); if (!empty($dn_info[$denormalizer_view]['external'])) { Database::setActiveConnection('external'); } $final_sql = denormalizer_dpq($final_q); Database::setActiveConnection(); try { $injected_database = Database::getConnection(); $affected = $injected_database->query("REPLACE INTO {$db_prefix}$prefix{$denormalizer_view} $final_sql"); // $affected = db_query("REPLACE INTO {$db_prefix}$prefix. // {$denormalizer_view} $final_sql");. $affected->allowRowCount = TRUE; $count = $affected->rowCount(); } catch (Exception $e) { \Drupal::messenger()->addError(t('Could not denormalize @table: @message', [ '@table' => $denormalizer_view, '@message' => $e->getMessage(), ])); } $end = microtime(TRUE); $time = round($end - $start, 2); $out[] = t('Loaded @records to %view in @time seconds.', [ '%view' => "{$db_prefix}$prefix{$denormalizer_view}", '@records' => $affected, '@time' => $time, ]); } else { $injected_database = Database::getConnection(); $injected_database->query("DROP TABLE IF EXISTS {$db_prefix}$prefix$denormalizer_view"); $injected_database->query("DROP VIEW IF EXISTS {$db_prefix}$prefix$denormalizer_view"); // Db_query("DROP TABLE IF EXISTS. // {$db_prefix}$prefix$denormalizer_view");. // Db_query("DROP VIEW IF EXISTS. // {$db_prefix}$prefix$denormalizer_view");. // $out[] = t('Dropped %name.', array('%name' => "{$db_prefix}. // $prefix$denormalizer_view"));. if (!empty($dn_info[$denormalizer_view]['external'])) { Database::setActiveConnection('external'); } // Allow altering query before insert. \Drupal::moduleHandler() ->alter('denormalizer', $final_q, $denormalizer_view, $dn_info[$denormalizer_view]); $final_sql = denormalizer_dpq($final_q); Database::setActiveConnection(); if ($dn_info[$denormalizer_view]['entity_type'] == 'webform_submission') { $injected_database = Database::getConnection(); $affected = $injected_database->query("$final_sql"); // @todo maybe find a way to load multiple webform bundle? $bundle = array_key_first($dn_info[$denormalizer_view]['bundles']); $elements = denormalizer_webform_get_columns($bundle); $sqlColumns = denormalizer_webform_sql_columns($elements); $implodedColumns = implode(",", $sqlColumns); $webformData = $affected->fetchAll(); $sanitizedData = []; if ($webformData) { $sanitizedData = denormalizer_webform_data_sanitize($webformData, $elements); } } if ($type == 'TABLE') { // Create an empty table. // https://www.percona.com/blog/2018/01/10/. // Why-avoid-create-table-as-select-statement/. $injected_database = Database::getConnection(); if ($dn_info[$denormalizer_view]['entity_type'] == 'webform_submission') { if (isset($implodedColumns)) { $affected = $injected_database->query("CREATE $type $target ($implodedColumns) CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = MYISAM"); $injected_database->query("ALTER TABLE $target ADD PRIMARY KEY (webform_id,sid)"); } } else { $affected = $injected_database->query("CREATE $type $target CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = MYISAM AS $final_sql LIMIT 0"); } // $affected = db_query("CREATE $type $target CHARACTER SET utf8. // COLLATE utf8_general_ci ENGINE = MYISAM AS $final_sql LIMIT 0");. $affected->allowRowCount = TRUE; $count = $affected->rowCount(); if ($id_key) { $injected_database = Database::getConnection(); $injected_database->query("ALTER TABLE $target ADD PRIMARY KEY $id_key ($id_key)"); } // Insert records into it. try { $injected_database = Database::getConnection(); if ($dn_info[$denormalizer_view]['entity_type'] == 'webform_submission') { $columns = implode(", ", array_keys($sanitizedData[0] ?? [])); foreach ($sanitizedData as $val) { $prep = []; foreach ($val as $k => $v) { $prep[':' . $k] = $v; } $sth = $injected_database->prepare("INSERT INTO $target ( " . $columns . ") VALUES (" . implode(', ', array_keys($prep)) . ")"); $result = $sth->execute($prep); } } else { $affected = $injected_database->query("INSERT INTO $target $final_sql"); // $affected = db_query("INSERT INTO $target $final_sql");. $affected->allowRowCount = TRUE; $count = $affected->rowCount(); } } catch (Exception $e) { \Drupal::messenger()->addError(t('Could not denormalize @table: @message', [ '@table' => $denormalizer_view, '@message' => $e->getMessage(), ])); } } elseif ($type == 'VIEW') { try { $injected_database = Database::getConnection(); // todo It's complex to get view from current webform table structure without creating a new table first. if ($dn_info[$denormalizer_view]['entity_type'] == 'webform_submission') { if (isset($implodedColumns)) { $temp = str_replace($prefix.$denormalizer_view.'`', $prefix.$denormalizer_view.'_for_view`',$target); $affected = $injected_database->query("CREATE TABLE $temp ($implodedColumns) CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = MYISAM"); $injected_database->query("ALTER TABLE $temp ADD PRIMARY KEY (webform_id,sid)"); $columns = implode(", ", array_keys($sanitizedData[0] ?? [])); foreach ($sanitizedData as $val) { $prep = []; foreach ($val as $k => $v) { $prep[':' . $k] = $v; } $sth = $injected_database->prepare("INSERT INTO $temp ( " . $columns . ") VALUES (" . implode(', ', array_keys($prep)) . ")"); $result = $sth->execute($prep); } $temp_q = $injected_database->select($temp,'temp'); $temp_q->fields('temp'); $temp_q_final = denormalizer_dpq($temp_q); $affected = $injected_database->query("CREATE $type $target AS $temp_q_final"); } } else { // print_r("Final : ".$final_sql);. $affected = $injected_database->query("CREATE $type $target AS $final_sql"); } // $affected = db_query("CREATE $type $target AS $final_sql");. $affected->allowRowCount = TRUE; $count = $affected->rowCount(); } catch (Exception $e) { \Drupal::messenger()->addError(t('Could not denormalize @table: @message', [ '@table' => $denormalizer_view, '@message' => $e->getMessage(), ])); } } $end = microtime(TRUE); $time = round($end - $start, 2); $out[] = t('Created @type %view with @records records in @time seconds.', [ '@type' => $type, '%view' => "{$db_prefix}$prefix{$denormalizer_view}", '@records' => $affected, '@time' => $time, ]); } if ($type == 'TABLE') { if (!empty($dn_info[$denormalizer_view]['changed_key'])) { if (!empty($dn_info[$denormalizer_view]['external'])) { Database::setActiveConnection('external'); } $injected_database = Database::getConnection(); $changed = $injected_database->query("SELECT max({$dn_info[$denormalizer_view]['changed_key']}) from $target") ->fetchField(); // $changed = db_query("SELECT max({$dn_info[$denormalizer_view]. // ['changed_key']}) from $target")->fetchField();. Database::setActiveConnection(); \Drupal::service('config.factory') ->getEditable('denormalizer_max_changed_' . $denormalizer_view, $changed); } } $args = [$denormalizer_view, $dn_info[$denormalizer_view]]; \Drupal::moduleHandler()->invokeAll('denormalizer_post_execute', $args); } $all_end = microtime(TRUE); $all_time = round($all_end - $all_start, 2); $out[] = t('Created all in @time seconds.', ['@time' => $all_time]); \Drupal::messenger()->addStatus(implode("<br/>", $out)); } /** * Show full SQL used to create all database items. */ public function getSql() { $config = \Drupal::service('config.factory') ->getEditable('denormalizer.settings'); $db_source = denormalizer_source_db(); $sql = ''; if ($config->get('db') == 'external') { $db_target = denormalizer_target_db(); $db_prefix = "{$db_target}."; $sql .= "CREATE DATABASE IF NOT EXISTS $db_target;\n\n"; } if ($config->get('sql_mode', 'views') == 'views') { $type = 'VIEW'; } else { $type = 'TABLE'; } $prefix = $config->get('view_prefix', 'snowflake_'); $dn_info = denormalizer_get_info(); foreach ($this->dwTables as $denormalizer_view => $final_q) { if (!empty($dn_info[$denormalizer_view]['external'])) { Database::setActiveConnection('external'); } // Allow altering query before insert. \Drupal::moduleHandler() ->alter('denormalizer', $final_q, $denormalizer_view, $dn_info[$denormalizer_view]); $final_sql = denormalizer_dpq($final_q); $target = "{$db_prefix}{$prefix}{$denormalizer_view}"; // Drop views and tables. $sql .= "DROP TABLE IF EXISTS $target;\n"; $sql .= "DROP VIEW IF EXISTS $target;\n"; $sql .= "CREATE $type $target AS $final_sql;\n"; if ($key = denormalizer_get_primary_key($denormalizer_view)) { $sql .= "ALTER TABLE $target ADD PRIMARY KEY $key ($key);\n\n"; } // Reset to default DB. Database::setActiveConnection(); } $page = []; $page['header']['#markup'] = t('You can use the SQL below to generate database views of denormalized data on a copy of this database. This uses the entity property and field metadata from the current site, so queries may fail if the data is different.'); $page['sql']['#type'] = 'textarea'; $page['sql']['#rows'] = '100'; $page['sql']['#value'] = $sql; return \Drupal::service('renderer')->render($page); } private function entityTypeManager() { if (empty($this->entityTypeManager)) { $this->entityTypeManager = \Drupal::entityTypeManager(); } return $this->entityTypeManager; } }