PageRenderTime 23ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/core/lib/Drupal/Core/Entity/Sql/DefaultTableMapping.php

https://gitlab.com/geeta7/drupal
PHP | 395 lines | 151 code | 37 blank | 207 comment | 24 complexity | 57423cc2760a2e8d7276f92357f77189 MD5 | raw file
  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\Core\Entity\Sql\DefaultTableMapping.
  5. */
  6. namespace Drupal\Core\Entity\Sql;
  7. use Drupal\Core\Entity\ContentEntityTypeInterface;
  8. use Drupal\Core\Field\FieldStorageDefinitionInterface;
  9. /**
  10. * Defines a default table mapping class.
  11. */
  12. class DefaultTableMapping implements TableMappingInterface {
  13. /**
  14. * The entity type definition.
  15. *
  16. * @var \Drupal\Core\Entity\ContentEntityTypeInterface
  17. */
  18. protected $entityType;
  19. /**
  20. * The field storage definitions of this mapping.
  21. *
  22. * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]
  23. */
  24. protected $fieldStorageDefinitions = array();
  25. /**
  26. * A list of field names per table.
  27. *
  28. * This corresponds to the return value of
  29. * TableMappingInterface::getFieldNames() except that this variable is
  30. * additionally keyed by table name.
  31. *
  32. * @var array[]
  33. */
  34. protected $fieldNames = array();
  35. /**
  36. * A list of database columns which store denormalized data per table.
  37. *
  38. * This corresponds to the return value of
  39. * TableMappingInterface::getExtraColumns() except that this variable is
  40. * additionally keyed by table name.
  41. *
  42. * @var array[]
  43. */
  44. protected $extraColumns = array();
  45. /**
  46. * A mapping of column names per field name.
  47. *
  48. * This corresponds to the return value of
  49. * TableMappingInterface::getColumnNames() except that this variable is
  50. * additionally keyed by field name.
  51. *
  52. * This data is derived from static::$storageDefinitions, but is stored
  53. * separately to avoid repeated processing.
  54. *
  55. * @var array[]
  56. */
  57. protected $columnMapping = array();
  58. /**
  59. * A list of all database columns per table.
  60. *
  61. * This corresponds to the return value of
  62. * TableMappingInterface::getAllColumns() except that this variable is
  63. * additionally keyed by table name.
  64. *
  65. * This data is derived from static::$storageDefinitions, static::$fieldNames,
  66. * and static::$extraColumns, but is stored separately to avoid repeated
  67. * processing.
  68. *
  69. * @var array[]
  70. */
  71. protected $allColumns = array();
  72. /**
  73. * Constructs a DefaultTableMapping.
  74. *
  75. * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type
  76. * The entity type definition.
  77. * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $storage_definitions
  78. * A list of field storage definitions that should be available for the
  79. * field columns of this table mapping.
  80. */
  81. public function __construct(ContentEntityTypeInterface $entity_type, array $storage_definitions) {
  82. $this->entityType = $entity_type;
  83. $this->fieldStorageDefinitions = $storage_definitions;
  84. }
  85. /**
  86. * {@inheritdoc}
  87. */
  88. public function getTableNames() {
  89. return array_unique(array_merge(array_keys($this->fieldNames), array_keys($this->extraColumns)));
  90. }
  91. /**
  92. * {@inheritdoc}
  93. */
  94. public function getAllColumns($table_name) {
  95. if (!isset($this->allColumns[$table_name])) {
  96. $this->allColumns[$table_name] = array();
  97. foreach ($this->getFieldNames($table_name) as $field_name) {
  98. $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], array_values($this->getColumnNames($field_name)));
  99. }
  100. // There is just one field for each dedicated storage table, thus
  101. // $field_name can only refer to it.
  102. if (isset($field_name) && $this->requiresDedicatedTableStorage($this->fieldStorageDefinitions[$field_name])) {
  103. // Unlike in shared storage tables, in dedicated ones field columns are
  104. // positioned last.
  105. $this->allColumns[$table_name] = array_merge($this->getExtraColumns($table_name), $this->allColumns[$table_name]);
  106. }
  107. else {
  108. $this->allColumns[$table_name] = array_merge($this->allColumns[$table_name], $this->getExtraColumns($table_name));
  109. }
  110. }
  111. return $this->allColumns[$table_name];
  112. }
  113. /**
  114. * {@inheritdoc}
  115. */
  116. public function getFieldNames($table_name) {
  117. if (isset($this->fieldNames[$table_name])) {
  118. return $this->fieldNames[$table_name];
  119. }
  120. return array();
  121. }
  122. /**
  123. * {@inheritdoc}
  124. */
  125. public function getFieldTableName($field_name) {
  126. $result = NULL;
  127. if (isset($this->fieldStorageDefinitions[$field_name])) {
  128. // Since a field may be stored in more than one table, we inspect tables
  129. // in order of relevance: the data table if present is the main place
  130. // where field data is stored, otherwise the base table is responsible for
  131. // storing field data. Revision metadata is an exception as it's stored
  132. // only in the revision table.
  133. // @todo The table mapping itself should know about entity tables. See
  134. // https://www.drupal.org/node/2274017.
  135. /** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $storage */
  136. $storage = \Drupal::entityManager()->getStorage($this->entityType->id());
  137. $table_names = array(
  138. $storage->getDataTable(),
  139. $storage->getBaseTable(),
  140. $storage->getRevisionTable(),
  141. );
  142. // Collect field columns.
  143. $field_columns = array();
  144. $storage_definition = $this->fieldStorageDefinitions[$field_name];
  145. foreach (array_keys($storage_definition->getColumns()) as $property_name) {
  146. $field_columns[] = $this->getFieldColumnName($storage_definition, $property_name);
  147. }
  148. foreach (array_filter($table_names) as $table_name) {
  149. $columns = $this->getAllColumns($table_name);
  150. // We assume finding one field column belonging to the mapping is enough
  151. // to identify the field table.
  152. if (array_intersect($columns, $field_columns)) {
  153. $result = $table_name;
  154. break;
  155. }
  156. }
  157. }
  158. if (!isset($result)) {
  159. throw new SqlContentEntityStorageException("Table information not available for the '$field_name' field.");
  160. }
  161. return $result;
  162. }
  163. /**
  164. * {@inheritdoc}
  165. */
  166. public function getColumnNames($field_name) {
  167. if (!isset($this->columnMapping[$field_name])) {
  168. $this->columnMapping[$field_name] = array();
  169. if (isset($this->fieldStorageDefinitions[$field_name])) {
  170. foreach (array_keys($this->fieldStorageDefinitions[$field_name]->getColumns()) as $property_name) {
  171. $this->columnMapping[$field_name][$property_name] = $this->getFieldColumnName($this->fieldStorageDefinitions[$field_name], $property_name);
  172. }
  173. }
  174. }
  175. return $this->columnMapping[$field_name];
  176. }
  177. /**
  178. * {@inheritdoc}
  179. */
  180. public function getFieldColumnName(FieldStorageDefinitionInterface $storage_definition, $property_name) {
  181. $field_name = $storage_definition->getName();
  182. if ($this->allowsSharedTableStorage($storage_definition)) {
  183. $column_name = count($storage_definition->getColumns()) == 1 ? $field_name : $field_name . '__' . $property_name;
  184. }
  185. elseif ($this->requiresDedicatedTableStorage($storage_definition)) {
  186. $column_name = !in_array($property_name, $this->getReservedColumns()) ? $field_name . '_' . $property_name : $property_name;
  187. }
  188. else {
  189. throw new SqlContentEntityStorageException("Column information not available for the '$field_name' field.");
  190. }
  191. return $column_name;
  192. }
  193. /**
  194. * Adds field columns for a table to the table mapping.
  195. *
  196. * @param string $table_name
  197. * The name of the table to add the field column for.
  198. * @param string[] $field_names
  199. * A list of field names to add the columns for.
  200. *
  201. * @return $this
  202. */
  203. public function setFieldNames($table_name, array $field_names) {
  204. $this->fieldNames[$table_name] = $field_names;
  205. // Force the re-computation of the column list.
  206. unset($this->allColumns[$table_name]);
  207. return $this;
  208. }
  209. /**
  210. * {@inheritdoc}
  211. */
  212. public function getExtraColumns($table_name) {
  213. if (isset($this->extraColumns[$table_name])) {
  214. return $this->extraColumns[$table_name];
  215. }
  216. return array();
  217. }
  218. /**
  219. * Adds a extra columns for a table to the table mapping.
  220. *
  221. * @param string $table_name
  222. * The name of table to add the extra columns for.
  223. * @param string[] $column_names
  224. * The list of column names.
  225. *
  226. * @return $this
  227. */
  228. public function setExtraColumns($table_name, array $column_names) {
  229. $this->extraColumns[$table_name] = $column_names;
  230. // Force the re-computation of the column list.
  231. unset($this->allColumns[$table_name]);
  232. return $this;
  233. }
  234. /**
  235. * Checks whether the given field can be stored in a shared table.
  236. *
  237. * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
  238. * The field storage definition.
  239. *
  240. * @return bool
  241. * TRUE if the field can be stored in a dedicated table, FALSE otherwise.
  242. */
  243. public function allowsSharedTableStorage(FieldStorageDefinitionInterface $storage_definition) {
  244. return !$storage_definition->hasCustomStorage() && $storage_definition->isBaseField() && !$storage_definition->isMultiple();
  245. }
  246. /**
  247. * Checks whether the given field has to be stored in a dedicated table.
  248. *
  249. * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
  250. * The field storage definition.
  251. *
  252. * @return bool
  253. * TRUE if the field can be stored in a dedicated table, FALSE otherwise.
  254. */
  255. public function requiresDedicatedTableStorage(FieldStorageDefinitionInterface $storage_definition) {
  256. return !$storage_definition->hasCustomStorage() && !$this->allowsSharedTableStorage($storage_definition);
  257. }
  258. /**
  259. * Gets a list of dedicated table names for this mapping.
  260. *
  261. * @return string[]
  262. * An array of table names.
  263. */
  264. public function getDedicatedTableNames() {
  265. $table_mapping = $this;
  266. $definitions = array_filter($this->fieldStorageDefinitions, function($definition) use ($table_mapping) { return $table_mapping->requiresDedicatedTableStorage($definition); });
  267. $data_tables = array_map(function($definition) use ($table_mapping) { return $table_mapping->getDedicatedDataTableName($definition); }, $definitions);
  268. $revision_tables = array_map(function($definition) use ($table_mapping) { return $table_mapping->getDedicatedRevisionTableName($definition); }, $definitions);
  269. $dedicated_tables = array_merge(array_values($data_tables), array_values($revision_tables));
  270. return $dedicated_tables;
  271. }
  272. /**
  273. * {@inheritdoc}
  274. */
  275. public function getReservedColumns() {
  276. return array('deleted');
  277. }
  278. /**
  279. * Generates a table name for a field data table.
  280. *
  281. * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
  282. * The field storage definition.
  283. * @param bool $is_deleted
  284. * (optional) Whether the table name holding the values of a deleted field
  285. * should be returned.
  286. *
  287. * @return string
  288. * A string containing the generated name for the database table.
  289. */
  290. public function getDedicatedDataTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) {
  291. if ($is_deleted) {
  292. // When a field is a deleted, the table is renamed to
  293. // {field_deleted_data_FIELD_UUID}. To make sure we don't end up with
  294. // table names longer than 64 characters, we hash the unique storage
  295. // identifier and return the first 10 characters so we end up with a short
  296. // unique ID.
  297. return "field_deleted_data_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
  298. }
  299. else {
  300. return $this->generateFieldTableName($storage_definition, FALSE);
  301. }
  302. }
  303. /**
  304. * Generates a table name for a field revision archive table.
  305. *
  306. * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
  307. * The field storage definition.
  308. * @param bool $is_deleted
  309. * (optional) Whether the table name holding the values of a deleted field
  310. * should be returned.
  311. *
  312. * @return string
  313. * A string containing the generated name for the database table.
  314. */
  315. public function getDedicatedRevisionTableName(FieldStorageDefinitionInterface $storage_definition, $is_deleted = FALSE) {
  316. if ($is_deleted) {
  317. // When a field is a deleted, the table is renamed to
  318. // {field_deleted_revision_FIELD_UUID}. To make sure we don't end up with
  319. // table names longer than 64 characters, we hash the unique storage
  320. // identifier and return the first 10 characters so we end up with a short
  321. // unique ID.
  322. return "field_deleted_revision_" . substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
  323. }
  324. else {
  325. return $this->generateFieldTableName($storage_definition, TRUE);
  326. }
  327. }
  328. /**
  329. * Generates a safe and unambiguous field table name.
  330. *
  331. * The method accounts for a maximum table name length of 64 characters, and
  332. * takes care of disambiguation.
  333. *
  334. * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
  335. * The field storage definition.
  336. * @param bool $revision
  337. * TRUE for revision table, FALSE otherwise.
  338. *
  339. * @return string
  340. * The final table name.
  341. */
  342. protected function generateFieldTableName(FieldStorageDefinitionInterface $storage_definition, $revision) {
  343. $separator = $revision ? '_revision__' : '__';
  344. $table_name = $storage_definition->getTargetEntityTypeId() . $separator . $storage_definition->getName();
  345. // Limit the string to 48 characters, keeping a 16 characters margin for db
  346. // prefixes.
  347. if (strlen($table_name) > 48) {
  348. // Use a shorter separator, a truncated entity_type, and a hash of the
  349. // field UUID.
  350. $separator = $revision ? '_r__' : '__';
  351. // Truncate to the same length for the current and revision tables.
  352. $entity_type = substr($storage_definition->getTargetEntityTypeId(), 0, 34);
  353. $field_hash = substr(hash('sha256', $storage_definition->getUniqueStorageIdentifier()), 0, 10);
  354. $table_name = $entity_type . $separator . $field_hash;
  355. }
  356. return $table_name;
  357. }
  358. }