Esempio n. 1
0
 /**
  * Gets the field storage definitions.
  *
  * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
  */
 protected function getFieldStorageDefinitions()
 {
     if (!isset($this->fieldStorageDefinitions)) {
         $this->fieldStorageDefinitions = $this->entityManager->getFieldStorageDefinitions($this->entityType->id());
     }
     return $this->fieldStorageDefinitions;
 }
 /**
  * Retrieves suggestions for taxonomy term autocompletion.
  *
  * This function outputs term name suggestions in response to Ajax requests
  * made by the taxonomy autocomplete widget for taxonomy term reference
  * fields. The output is a JSON object of plain-text term suggestions, keyed
  * by the user-entered value with the completed term name appended.
  * Term names containing commas are wrapped in quotes.
  *
  * For example, suppose the user has entered the string 'red fish, blue' in
  * the field, and there are two taxonomy terms, 'blue fish' and 'blue moon'.
  * The JSON output would have the following structure:
  * @code
  *   {
  *     "red fish, blue fish": "blue fish",
  *     "red fish, blue moon": "blue moon",
  *   };
  * @endcode
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  * @param string $entity_type
  *   The entity_type.
  * @param string $field_name
  *   The name of the term reference field.
  *
  * @return \Symfony\Component\HttpFoundation\JsonResponse|\Symfony\Component\HttpFoundation\Response
  *   When valid field name is specified, a JSON response containing the
  *   autocomplete suggestions for taxonomy terms. Otherwise a normal response
  *   containing an error message.
  */
 public function autocomplete(Request $request, $entity_type, $field_name)
 {
     // A comma-separated list of term names entered in the autocomplete form
     // element. Only the last term is used for autocompletion.
     $tags_typed = $request->query->get('q');
     // Make sure the field exists and is a taxonomy field.
     $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type);
     if (!isset($field_storage_definitions[$field_name]) || $field_storage_definitions[$field_name]->getType() !== 'taxonomy_term_reference') {
         // Error string. The JavaScript handler will realize this is not JSON and
         // will display it as debugging information.
         return new Response(t('Taxonomy field @field_name not found.', array('@field_name' => $field_name)), 403);
     }
     $field_storage = $field_storage_definitions[$field_name];
     // The user enters a comma-separated list of tags. We only autocomplete the
     // last tag.
     $tags_typed = Tags::explode($tags_typed);
     $tag_last = Unicode::strtolower(array_pop($tags_typed));
     $matches = array();
     if ($tag_last != '') {
         // Part of the criteria for the query come from the field's own settings.
         $vids = array();
         foreach ($field_storage->getSetting('allowed_values') as $tree) {
             $vids[] = $tree['vocabulary'];
         }
         $matches = $this->getMatchingTerms($tags_typed, $vids, $tag_last);
     }
     return new JsonResponse($matches);
 }
 /**
  * Gets the entity schema for the specified entity type.
  *
  * Entity types may override this method in order to optimize the generated
  * schema of the entity tables. However, only cross-field optimizations should
  * be added here; e.g., an index spanning multiple fields. Optimizations that
  * apply to a single field have to be added via
  * SqlContentEntityStorageSchema::getSharedTableFieldSchema() instead.
  *
  * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type
  *   The entity type definition.
  * @param bool $reset
  *   (optional) If set to TRUE static cache will be ignored and a new schema
  *   array generation will be performed. Defaults to FALSE.
  *
  * @return array
  *   A Schema API array describing the entity schema, excluding dedicated
  *   field tables.
  *
  * @throws \Drupal\Core\Field\FieldException
  */
 protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE)
 {
     $this->checkEntityType($entity_type);
     $entity_type_id = $entity_type->id();
     if (!isset($this->schema[$entity_type_id]) || $reset) {
         // Back up the storage definition and replace it with the passed one.
         // @todo Instead of switching the wrapped entity type, we should be able
         //   to instantiate a new table mapping for each entity type definition.
         //   See https://www.drupal.org/node/2274017.
         $actual_definition = $this->entityManager->getDefinition($entity_type_id);
         $this->storage->setEntityType($entity_type);
         // Prepare basic information about the entity type.
         $tables = $this->getEntitySchemaTables();
         // Initialize the table schema.
         $schema[$tables['base_table']] = $this->initializeBaseTable($entity_type);
         if (isset($tables['revision_table'])) {
             $schema[$tables['revision_table']] = $this->initializeRevisionTable($entity_type);
         }
         if (isset($tables['data_table'])) {
             $schema[$tables['data_table']] = $this->initializeDataTable($entity_type);
         }
         if (isset($tables['revision_data_table'])) {
             $schema[$tables['revision_data_table']] = $this->initializeRevisionDataTable($entity_type);
         }
         // We need to act only on shared entity schema tables.
         $table_mapping = $this->storage->getTableMapping();
         $table_names = array_diff($table_mapping->getTableNames(), $table_mapping->getDedicatedTableNames());
         $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
         foreach ($table_names as $table_name) {
             if (!isset($schema[$table_name])) {
                 $schema[$table_name] = array();
             }
             foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
                 if (!isset($storage_definitions[$field_name])) {
                     throw new FieldException(SafeMarkup::format('Field storage definition for "@field_name" could not be found.', array('@field_name' => $field_name)));
                 } elseif ($table_mapping->allowsSharedTableStorage($storage_definitions[$field_name])) {
                     $column_names = $table_mapping->getColumnNames($field_name);
                     $storage_definition = $storage_definitions[$field_name];
                     $schema[$table_name] = array_merge_recursive($schema[$table_name], $this->getSharedTableFieldSchema($storage_definition, $table_name, $column_names));
                 }
             }
         }
         // Process tables after having gathered field information.
         $this->processBaseTable($entity_type, $schema[$tables['base_table']]);
         if (isset($tables['revision_table'])) {
             $this->processRevisionTable($entity_type, $schema[$tables['revision_table']]);
         }
         if (isset($tables['data_table'])) {
             $this->processDataTable($entity_type, $schema[$tables['data_table']]);
         }
         if (isset($tables['revision_data_table'])) {
             $this->processRevisionDataTable($entity_type, $schema[$tables['revision_data_table']]);
         }
         $this->schema[$entity_type_id] = $schema;
         // Restore the actual definition.
         $this->storage->setEntityType($actual_definition);
     }
     return $this->schema[$entity_type_id];
 }
 /**
  * Gets the type of the ID key for a given entity type.
  *
  * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
  *   An entity type.
  *
  * @return string|null
  *   The type of the ID key for a given entity type, or NULL if the entity
  *   type does not support fields.
  */
 protected function getEntityTypeIdKeyType(EntityTypeInterface $entity_type)
 {
     if (!$entity_type->isSubclassOf(FieldableEntityInterface::class)) {
         return NULL;
     }
     $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type->id());
     return $field_storage_definitions[$entity_type->getKey('id')]->getType();
 }
 /**
  * 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);
                 }
             }
         }
     }
 }
 /**
  * Gets a list of changes to entity type and field storage definitions.
  *
  * @return array
  *   An associative array keyed by entity type id of change descriptors. Every
  *   entry is an associative array with the following optional keys:
  *   - entity_type: a scalar having only the DEFINITION_UPDATED value.
  *   - field_storage_definitions: an associative array keyed by field name of
  *     scalars having one value among:
  *     - DEFINITION_CREATED
  *     - DEFINITION_UPDATED
  *     - DEFINITION_DELETED
  */
 protected function getChangeList()
 {
     $this->entityManager->useCaches(FALSE);
     $change_list = array();
     foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
         $original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
         // @todo Support non-storage-schema-changing definition updates too:
         //   https://www.drupal.org/node/2336895.
         if (!$original) {
             $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_CREATED;
         } else {
             if ($this->requiresEntityStorageSchemaChanges($entity_type, $original)) {
                 $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_UPDATED;
             }
             if ($this->entityManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) {
                 $field_changes = array();
                 $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
                 $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
                 // Detect created field storage definitions.
                 foreach (array_diff_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
                     $field_changes[$field_name] = static::DEFINITION_CREATED;
                 }
                 // Detect deleted field storage definitions.
                 foreach (array_diff_key($original_storage_definitions, $storage_definitions) as $field_name => $original_storage_definition) {
                     $field_changes[$field_name] = static::DEFINITION_DELETED;
                 }
                 // Detect updated field storage definitions.
                 foreach (array_intersect_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
                     // @todo Support non-storage-schema-changing definition updates too:
                     //   https://www.drupal.org/node/2336895. So long as we're checking
                     //   based on schema change requirements rather than definition
                     //   equality, skip the check if the entity type itself needs to be
                     //   updated, since that can affect the schema of all fields, so we
                     //   want to process that update first without reporting false
                     //   positives here.
                     if (!isset($change_list[$entity_type_id]['entity_type']) && $this->requiresFieldStorageSchemaChanges($storage_definition, $original_storage_definitions[$field_name])) {
                         $field_changes[$field_name] = static::DEFINITION_UPDATED;
                     }
                 }
                 if ($field_changes) {
                     $change_list[$entity_type_id]['field_storage_definitions'] = $field_changes;
                 }
             }
         }
     }
     // @todo Support deleting entity definitions when we support base field
     //   purging. See https://www.drupal.org/node/2282119.
     $this->entityManager->useCaches(TRUE);
     return array_filter($change_list);
 }
Esempio n. 7
0
 /**
  * Gets the field storage of the used field.
  *
  * @return \Drupal\Core\Field\FieldStorageDefinitionInterface
  */
 protected function getFieldStorageDefinition()
 {
     $entity_type_id = $this->definition['entity_type'];
     $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
     $field_storage = NULL;
     // @todo Unify 'entity field'/'field_name' instead of converting back and
     //   forth. https://www.drupal.org/node/2410779
     if (isset($this->definition['field_name'])) {
         $field_storage = $field_storage_definitions[$this->definition['field_name']];
     } elseif (isset($this->definition['entity field'])) {
         $field_storage = $field_storage_definitions[$this->definition['entity field']];
     }
     return $field_storage;
 }
 /**
  * Gets entity schema definitions for index and key definitions.
  *
  * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type
  *   The entity type definition.
  * @param array $schema
  *   The entity schema array.
  *
  * @return array
  *   A stripped down version of the $schema Schema API array containing, for
  *   each table, only the key and index definitions not derived from field
  *   storage definitions.
  */
 protected function getEntitySchemaData(ContentEntityTypeInterface $entity_type, array $schema)
 {
     $entity_type_id = $entity_type->id();
     // Collect all possible field schema identifiers for shared table fields.
     // These will be used to detect entity schema data in the subsequent loop.
     $field_schema_identifiers = [];
     $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
     $table_mapping = $this->storage->getTableMapping($storage_definitions);
     foreach ($storage_definitions as $field_name => $storage_definition) {
         if ($table_mapping->allowsSharedTableStorage($storage_definition)) {
             // Make sure both base identifier names and suffixed names are listed.
             $name = $this->getFieldSchemaIdentifierName($entity_type_id, $field_name);
             $field_schema_identifiers[$name] = $name;
             foreach ($storage_definition->getColumns() as $key => $columns) {
                 $name = $this->getFieldSchemaIdentifierName($entity_type_id, $field_name, $key);
                 $field_schema_identifiers[$name] = $name;
             }
         }
     }
     // Extract entity schema data from the Schema API definition.
     $schema_data = [];
     $keys = ['indexes', 'unique keys'];
     $unused_keys = array_flip(['description', 'fields', 'foreign keys']);
     foreach ($schema as $table_name => $table_schema) {
         $table_schema = array_diff_key($table_schema, $unused_keys);
         foreach ($keys as $key) {
             // Exclude data generated from field storage definitions, we will check
             // that separately.
             if ($field_schema_identifiers && !empty($table_schema[$key])) {
                 $table_schema[$key] = array_diff_key($table_schema[$key], $field_schema_identifiers);
             }
         }
         $schema_data[$table_name] = array_filter($table_schema);
     }
     return $schema_data;
 }
 /**
  * Returns whether the passed field has been already deleted.
  *
  * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
  *   The field storage definition.
  *
  * @return bool
  *   Whether the field has been already deleted.
  */
 protected function storageDefinitionIsDeleted(FieldStorageDefinitionInterface $storage_definition)
 {
     return !array_key_exists($storage_definition->getName(), $this->entityManager->getFieldStorageDefinitions($this->entityTypeId));
 }
Esempio n. 10
0
 /**
  * {@inheritdoc}
  */
 public function getTableMapping(array $storage_definitions = NULL)
 {
     $table_mapping = $this->tableMapping;
     // If we are using our internal storage definitions, which is our main use
     // case, we can statically cache the computed table mapping. If a new set
     // of field storage definitions is passed, for instance when comparing old
     // and new storage schema, we compute the table mapping without caching.
     // @todo Clean-up this in https://www.drupal.org/node/2274017 so we can
     //   easily instantiate a new table mapping whenever needed.
     if (!isset($this->tableMapping) || $storage_definitions) {
         $definitions = $storage_definitions ?: $this->entityManager->getFieldStorageDefinitions($this->entityTypeId);
         $table_mapping = new DefaultTableMapping($this->entityType, $definitions);
         $definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use($table_mapping) {
             return $table_mapping->allowsSharedTableStorage($definition);
         });
         $key_fields = array_values(array_filter(array($this->idKey, $this->revisionKey, $this->bundleKey, $this->uuidKey, $this->langcodeKey)));
         $all_fields = array_keys($definitions);
         $revisionable_fields = array_keys(array_filter($definitions, function (FieldStorageDefinitionInterface $definition) {
             return $definition->isRevisionable();
         }));
         // Make sure the key fields come first in the list of fields.
         $all_fields = array_merge($key_fields, array_diff($all_fields, $key_fields));
         // Nodes have all three of these fields, while custom blocks only have
         // log.
         // @todo Provide automatic definitions for revision metadata fields in
         //   https://www.drupal.org/node/2248983.
         $revision_metadata_fields = array_intersect(array('revision_timestamp', 'revision_uid', 'revision_log'), $all_fields);
         $revisionable = $this->entityType->isRevisionable();
         $translatable = $this->entityType->isTranslatable();
         if (!$revisionable && !$translatable) {
             // The base layout stores all the base field values in the base table.
             $table_mapping->setFieldNames($this->baseTable, $all_fields);
         } elseif ($revisionable && !$translatable) {
             // The revisionable layout stores all the base field values in the base
             // table, except for revision metadata fields. Revisionable fields
             // denormalized in the base table but also stored in the revision table
             // together with the entity ID and the revision ID as identifiers.
             $table_mapping->setFieldNames($this->baseTable, array_diff($all_fields, $revision_metadata_fields));
             $revision_key_fields = array($this->idKey, $this->revisionKey);
             $table_mapping->setFieldNames($this->revisionTable, array_merge($revision_key_fields, $revisionable_fields));
         } elseif (!$revisionable && $translatable) {
             // Multilingual layouts store key field values in the base table. The
             // other base field values are stored in the data table, no matter
             // whether they are translatable or not. The data table holds also a
             // denormalized copy of the bundle field value to allow for more
             // performant queries. This means that only the UUID is not stored on
             // the data table.
             $table_mapping->setFieldNames($this->baseTable, $key_fields)->setFieldNames($this->dataTable, array_values(array_diff($all_fields, array($this->uuidKey))));
         } elseif ($revisionable && $translatable) {
             // The revisionable multilingual layout stores key field values in the
             // base table, except for language, which is stored in the revision
             // table along with revision metadata. The revision data table holds
             // data field values for all the revisionable fields and the data table
             // holds the data field values for all non-revisionable fields. The data
             // field values of revisionable fields are denormalized in the data
             // table, as well.
             $table_mapping->setFieldNames($this->baseTable, array_values($key_fields));
             // Like in the multilingual, non-revisionable case the UUID is not
             // in the data table. Additionally, do not store revision metadata
             // fields in the data table.
             $data_fields = array_values(array_diff($all_fields, array($this->uuidKey), $revision_metadata_fields));
             $table_mapping->setFieldNames($this->dataTable, $data_fields);
             $revision_base_fields = array_merge(array($this->idKey, $this->revisionKey, $this->langcodeKey), $revision_metadata_fields);
             $table_mapping->setFieldNames($this->revisionTable, $revision_base_fields);
             $revision_data_key_fields = array($this->idKey, $this->revisionKey, $this->langcodeKey);
             $revision_data_fields = array_diff($revisionable_fields, $revision_metadata_fields, array($this->langcodeKey));
             $table_mapping->setFieldNames($this->revisionDataTable, array_merge($revision_data_key_fields, $revision_data_fields));
         }
         // Add dedicated tables.
         $definitions = array_filter($definitions, function (FieldStorageDefinitionInterface $definition) use($table_mapping) {
             return $table_mapping->requiresDedicatedTableStorage($definition);
         });
         $extra_columns = array('bundle', 'deleted', 'entity_id', 'revision_id', 'langcode', 'delta');
         foreach ($definitions as $field_name => $definition) {
             foreach (array($table_mapping->getDedicatedDataTableName($definition), $table_mapping->getDedicatedRevisionTableName($definition)) as $table_name) {
                 $table_mapping->setFieldNames($table_name, array($field_name));
                 $table_mapping->setExtraColumns($table_name, $extra_columns);
             }
         }
         // Cache the computed table mapping only if we are using our internal
         // storage definitions.
         if (!$storage_definitions) {
             $this->tableMapping = $table_mapping;
         }
     }
     return $table_mapping;
 }
Esempio n. 11
0
 /**
  * {@inheritdoc}
  */
 public function getFieldStorageDefinitions($entity_type_id)
 {
     return $this->entityManager->getFieldStorageDefinitions($entity_type_id);
 }
Esempio n. 12
0
 protected function defineOptions()
 {
     $options = parent::defineOptions();
     $field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->definition['entity_type']);
     $field_storage = $field_storage_definitions[$this->definition['field_name']];
     $field_type = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field_storage->getType());
     $column_names = array_keys($field_storage->getColumns());
     $default_column = '';
     // Try to determine a sensible default.
     if (count($column_names) == 1) {
         $default_column = $column_names[0];
     } elseif (in_array('value', $column_names)) {
         $default_column = 'value';
     }
     // If the field has a "value" column, we probably need that one.
     $options['click_sort_column'] = array('default' => $default_column);
     $options['type'] = array('default' => $field_type['default_formatter']);
     $options['settings'] = array('default' => array());
     $options['group_column'] = array('default' => $default_column);
     $options['group_columns'] = array('default' => array());
     // Options used for multiple value fields.
     $options['group_rows'] = array('default' => TRUE, 'bool' => TRUE);
     // If we know the exact number of allowed values, then that can be
     // the default. Otherwise, default to 'all'.
     $options['delta_limit'] = array('default' => $field_storage->getCardinality() > 1 ? $field_storage->getCardinality() : 'all');
     $options['delta_offset'] = array('default' => 0);
     $options['delta_reversed'] = array('default' => FALSE, 'bool' => TRUE);
     $options['delta_first_last'] = array('default' => FALSE, 'bool' => TRUE);
     $options['multi_type'] = array('default' => 'separator');
     $options['separator'] = array('default' => ', ');
     $options['field_api_classes'] = array('default' => FALSE, 'bool' => TRUE);
     return $options;
 }
 /**
  * Constructs a ContentEntitySchemaHandler.
  *
  * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
  *   The entity manager.
  * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type
  *   The entity type.
  * @param \Drupal\Core\Entity\ContentEntityDatabaseStorage $storage
  *   The storage of the entity type. This must be an SQL-based storage.
  */
 public function __construct(EntityManagerInterface $entity_manager, ContentEntityTypeInterface $entity_type, ContentEntityDatabaseStorage $storage)
 {
     $this->entityType = $entity_type;
     $this->fieldStorageDefinitions = $entity_manager->getFieldStorageDefinitions($entity_type->id());
     $this->storage = $storage;
 }