Commit bbed679f authored by alexpott's avatar alexpott

Issue #2144327 by amateescu, effulgentsia, yched, dasjo: Make all field types provide a schema().

parent 6065342e
......@@ -7,47 +7,11 @@
namespace Drupal\Core\Field;
use Drupal\field\FieldInterface;
/**
* Interface definition for 'configurable field type' plugins.
*/
interface ConfigFieldItemInterface extends FieldItemInterface {
/**
* Returns the schema for the field.
*
* This method is static, because the field schema information is needed on
* creation of the field. No field instances exist by then, and it is not
* possible to instantiate a FieldItemInterface object yet.
*
* @param \Drupal\field\FieldInterface $field
* The field definition.
*
* @return array
* An associative array with the following key/value pairs:
* - columns: An array of Schema API column specifications, keyed by column
* name. This specifies what comprises a value for a given field. For
* example, a value for a number field is simply 'value', while a value
* for a formatted text field is the combination of 'value' and 'format'.
* It is recommended to avoid having the column definitions depend on
* field settings when possible. No assumptions should be made on how
* storage engines internally use the original column name to structure
* their storage.
* - indexes: (optional) An array of Schema API index definitions. Only
* columns that appear in the 'columns' array are allowed. Those indexes
* will be used as default indexes. Callers of field_create_field() can
* specify additional indexes or, at their own risk, modify the default
* indexes specified by the field-type module. Some storage engines might
* not support indexes.
* - foreign keys: (optional) An array of Schema API foreign key
* definitions. Note, however, that the field data is not necessarily
* stored in SQL. Also, the possible usage is limited, as you cannot
* specify another field as related, only existing SQL tables,
* such as {taxonomy_term_data}.
*/
public static function schema(FieldInterface $field);
/**
* Returns a form for the field-level settings.
*
......
......@@ -10,12 +10,20 @@
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\ListDefinition;
use Drupal\field\FieldException;
/**
* A class for defining entity fields.
*/
class FieldDefinition extends ListDefinition implements FieldDefinitionInterface {
/**
* The field schema.
*
* @var array
*/
protected $schema;
/**
* Creates a new field definition.
*
......@@ -201,4 +209,54 @@ public function getDefaultValue(EntityInterface $entity) {
return $this->getSetting('default_value');
}
/**
* {@inheritdoc}
*/
public function getSchema() {
if (!isset($this->schema)) {
// Get the schema from the field item class.
$definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($this->getFieldType());
$class = $definition['class'];
$schema = $class::schema($this);
// Fill in default values for optional entries.
$schema += array('indexes' => array(), 'foreign keys' => array());
// Check that the schema does not include forbidden column names.
if (array_intersect(array_keys($schema['columns']), static::getReservedColumns())) {
throw new FieldException('Illegal field type columns.');
}
// Merge custom indexes with those specified by the field type. Custom
// indexes prevail.
$schema['indexes'] = $this->indexes + $schema['indexes'];
$this->schema = $schema;
}
return $this->schema;
}
/**
* {@inheritdoc}
*/
public function getColumns() {
$schema = $this->getSchema();
// A typical use case for the method is to iterate on the columns, while
// some other use cases rely on identifying the first column with the key()
// function. Since the schema is persisted in the Field object, we take care
// of resetting the array pointer so that the former does not interfere with
// the latter.
reset($schema['columns']);
return $schema['columns'];
}
/**
* A list of columns that can not be used as field type columns.
*
* @return array
*/
public static function getReservedColumns() {
return array('deleted');
}
}
......@@ -209,4 +209,36 @@ public function isMultiple();
*/
public function getDefaultValue(EntityInterface $entity);
/**
* Returns the field schema.
*
* Note that this method returns an empty array for computed fields which have
* no schema.
*
* @return array
* The field schema, as an array of key/value pairs in the format returned
* by hook_field_schema():
* - columns: An array of Schema API column specifications, keyed by column
* name. This specifies what comprises a single value for a given field.
* No assumptions should be made on how storage backends internally use
* the original column name to structure their storage.
* - indexes: An array of Schema API index definitions. Some storage
* backends might not support indexes.
* - foreign keys: An array of Schema API foreign key definitions. Note,
* however, that depending on the storage backend specified for the field,
* the field data is not necessarily stored in SQL.
*/
public function getSchema();
/**
* Returns the field columns, as defined in the field schema.
*
* @return array
* The array of field columns, keyed by column name, in the same format
* returned by getSchema().
*
* @see \Drupal\field\Entity\FieldInterface::getSchema()
*/
public function getColumns();
}
......@@ -23,6 +23,41 @@
*/
interface FieldItemInterface extends ComplexDataInterface {
/**
* Returns the schema for the field.
*
* This method is static because the field schema information is needed on
* creation of the field. FieldItemInterface objects instantiated at that
* time are not reliable as field instance settings might be missing.
*
* Computed fields having no schema should return an empty array.
*
* @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
* The field definition.
*
* @return array
* An empty array if there is no schema, or an associative array with the
* following key/value pairs:
* - columns: An array of Schema API column specifications, keyed by column
* name. The columns need to be a subset of the properties defined in
* getPropertyDefinitions(). It is recommended to avoid having the column
* definitions depend on field settings when possible. No assumptions
* should be made on how storage engines internally use the original
* column name to structure their storage.
* - indexes: (optional) An array of Schema API index definitions. Only
* columns that appear in the 'columns' array are allowed. Those indexes
* will be used as default indexes. Callers of field_create_field() can
* specify additional indexes or, at their own risk, modify the default
* indexes specified by the field-type module. Some storage engines might
* not support indexes.
* - foreign keys: (optional) An array of Schema API foreign key
* definitions. Note, however, that the field data is not necessarily
* stored in SQL. Also, the possible usage is limited, as you cannot
* specify another field as related, only existing SQL tables,
* such as {taxonomy_term_data}.
*/
public static function schema(FieldDefinitionInterface $field_definition);
/**
* Gets the entity that field belongs to.
*
......
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
......@@ -42,4 +43,19 @@ public function getPropertyDefinitions() {
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
),
),
);
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
/**
......@@ -43,4 +44,20 @@ public function getPropertyDefinitions() {
}
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => 20,
'not null' => FALSE,
),
),
);
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
......@@ -43,6 +44,39 @@ public function getPropertyDefinitions() {
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => EMAIL_MAX_LENGTH,
'not null' => FALSE,
),
),
);
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints = parent::getConstraints();
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Length' => array(
'max' => EMAIL_MAX_LENGTH,
'maxMessage' => t('%name: the e-mail address can not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => EMAIL_MAX_LENGTH)),
)
),
));
return $constraints;
}
/**
* {@inheritdoc}
......
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
......@@ -82,6 +83,43 @@ public function getPropertyDefinitions() {
return static::$propertyDefinitions[$key];
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
$target_type = $field_definition->getSetting('target_type');
$target_type_info = \Drupal::entityManager()->getDefinition($target_type);
if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
$columns = array(
'target_id' => array(
'description' => 'The ID of the target entity.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
);
}
else {
$columns = array(
'target_id' => array(
'description' => 'The ID of the target entity.',
'type' => 'varchar',
'length' => '255',
),
);
}
$schema = array(
'columns' => $columns,
'indexes' => array(
'target_id' => array('target_id'),
),
);
return $schema;
}
/**
* {@inheritdoc}
*/
......
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
......@@ -42,4 +43,19 @@ public function getPropertyDefinitions() {
}
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'float',
'not null' => FALSE,
),
),
);
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
......@@ -42,4 +43,19 @@ public function getPropertyDefinitions() {
}
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'int',
'not null' => TRUE,
),
),
);
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Language\Language;
use Drupal\Core\TypedData\DataDefinition;
......@@ -55,6 +56,21 @@ public function getPropertyDefinitions() {
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => 12,
'not null' => FALSE,
),
),
);
}
/**
* {@inheritdoc}
*/
......
......@@ -7,10 +7,10 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\PrepareCacheInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Field\ConfigFieldItemBase;
use Drupal\field\FieldInterface;
use Drupal\field\FieldInstanceInterface;
/**
......@@ -31,13 +31,13 @@ abstract class LegacyConfigFieldItem extends ConfigFieldItemBase implements Prep
/**
* {@inheritdoc}
*/
public static function schema(FieldInterface $field) {
$definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field->type);
public static function schema(FieldDefinitionInterface $field_definition) {
$definition = \Drupal::service('plugin.manager.field.field_type')->getDefinition($field_definition->type);
$module = $definition['provider'];
module_load_install($module);
$callback = "{$module}_field_schema";
if (function_exists($callback)) {
return $callback($field);
return $callback($field_definition);
}
}
......
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
/**
......@@ -21,6 +22,21 @@
*/
class MapItem extends FieldItemBase {
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'blob',
'size' => 'big',
'serialize' => TRUE,
),
),
);
}
/**
* {@inheritdoc}
*/
......
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
......@@ -17,6 +18,9 @@
* id = "string",
* label = @Translation("String"),
* description = @Translation("An entity field containing a string value."),
* settings = {
* "max_length" = "255"
* },
* configurable = FALSE
* )
*/
......@@ -42,4 +46,20 @@ public function getPropertyDefinitions() {
}
return static::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => $field_definition->getSetting('max_length'),
'not null' => FALSE,
),
),
);
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\Core\Field\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
......@@ -42,4 +43,18 @@ public function getPropertyDefinitions() {
return self::$propertyDefinitions;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
'type' => 'text',
'not null' => TRUE,
),
),
);
}
}
......@@ -8,7 +8,7 @@
namespace Drupal\comment\Plugin\Field\FieldType;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\field\FieldInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\ConfigFieldItemBase;
/**
......@@ -71,7 +71,7 @@ public function getPropertyDefinitions() {
/**
* {@inheritdoc}
*/
public static function schema(FieldInterface $field) {
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'status' => array(
......
......@@ -7,10 +7,9 @@
namespace Drupal\datetime\Plugin\Field\FieldType;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\PrepareCacheInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\field\FieldInterface;
use Drupal\Core\Field\ConfigFieldItemBase;
/**
......@@ -69,7 +68,7 @@ public function getPropertyDefinitions() {
/**
* {@inheritdoc}
*/
public static function schema(FieldInterface $field) {
public static function schema(FieldDefinitionInterface $field_definition) {
return array(
'columns' => array(
'value' => array(
......
......@@ -7,8 +7,8 @@
namespace Drupal\email;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem;
use Drupal\field\FieldInterface;
use Drupal\Core\Field\ConfigFieldItemInterface;
/**
......@@ -18,52 +18,6 @@
*/
class ConfigurableEmailItem extends EmailItem implements ConfigFieldItemInterface {
/**
* Defines the max length for an email address
*
* The maximum length of an e-mail address is 254 characters. RFC 3696
* specifies a total length of 320 characters, but mentions that
* addresses longer than 256 characters are not normally useful. Erratum
* 1690 was then released which corrected this value to 254 characters.
* @see http://tools.ietf.org/html/rfc3696#section-3
* @see http://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690
*/
const EMAIL_MAX_LENGTH = 254;
/**
* {@inheritdoc}
*/
public static function schema(FieldInterface $field) {
return array(
'columns' => array(
'value' => array(
'type' => 'varchar',
'length' => static::EMAIL_MAX_LENGTH,
'not null' => FALSE,
),
),
);
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
$constraints = parent::getConstraints();
$constraints[] = $constraint_manager->create('ComplexData', array(
'value' => array(
'Length' => array(
'max' => static::EMAIL_MAX_LENGTH,
'maxMessage' => t('%name: the e-mail address can not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => static::EMAIL_MAX_LENGTH)),
)
),
));
return $constraints;
}
/**
* {@inheritdoc}
*/
......
......@@ -8,7 +8,7 @@
namespace Drupal\entity_reference;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\field\FieldInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\ConfigEntityReferenceItemBase;
use Drupal\Core\Field\ConfigFieldItemInterface;
......@@ -66,43 +66,21 @@ public function getPropertyDefinitions() {
/**
* {@inheritdoc}
*/
public static function schema(FieldInterface $field) {
$target_type = $field->getSetting('target_type');
public static function schema(FieldDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
$target_type = $field_definition->getSetting('target_type');
$target_type_info = \Drupal::entityManager()->getDefinition($target_type);
if ($target_type_info->isSubclassOf('\Drupal\Core\Entity\ContentEntityInterface')) {
$columns = array(
'target_id' => array(
'description' => 'The ID of the target entity.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
),
'revision_id' => array(
'description' => 'The revision ID of the target entity.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE