class Field extends ConfigEntityBase implements FieldInterface {
const ID_MAX_LENGTH = 32;
public $id;
public $uuid;
public $type;
public $module;
public $active;
public $settings = array();
public $cardinality = 1;
public $translatable = FALSE;
public $entity_types = array();
public $locked = FALSE;
public $storage = array();
public $indexes = array();
public $deleted = FALSE;
protected $schema;
protected $storageDetails;
public function __construct(array $values, $entity_type = 'field_entity') {
if (empty($values['type'])) {
throw new FieldException('Attempt to create a field with no type.');
}
if (empty($values['field_name']) && empty($values['id'])) {
throw new FieldException('Attempt to create an unnamed field.');
}
if (empty($values['id'])) {
$values['id'] = $values['field_name'];
unset($values['field_name']);
}
if (!preg_match('/^[_a-z]+[_a-z0-9]*$/', $values['id'])) {
throw new FieldException('Attempt to create a field with invalid characters. Only lowercase alphanumeric characters and underscores are allowed, and only lowercase letters and underscore are allowed as the first character');
}
parent::__construct($values, $entity_type);
}
public function getExportProperties() {
$names = array(
'id',
'uuid',
'status',
'langcode',
'type',
'settings',
'module',
'active',
'entity_types',
'storage',
'locked',
'cardinality',
'translatable',
'indexes',
);
$properties = array();
foreach ($names as $name) {
$properties[$name] = $this
->get($name);
}
return $properties;
}
public function save() {
unset($this->schema, $this->storageDetails);
if ($this
->isNew()) {
return $this
->saveNew();
}
else {
return $this
->saveUpdated();
}
}
protected function saveNew() {
$module_handler = \Drupal::moduleHandler();
$entity_manager = \Drupal::entityManager();
$storage_controller = $entity_manager
->getStorageController($this->entityType);
if (drupal_strlen($this->id) > static::ID_MAX_LENGTH) {
throw new FieldException(format_string('Attempt to create a field with an ID longer than @max characters: %id', array(
'@max' => static::ID_MAX_LENGTH,
'%id' => $this->id,
)));
}
if ($prior_field = current($storage_controller
->load(array(
$this->id,
)))) {
$message = $prior_field->active ? 'Attempt to create field name %id which already exists and is active.' : 'Attempt to create field name %id which already exists, although it is inactive.';
throw new FieldException(format_string($message, array(
'%id' => $this->id,
)));
}
foreach ($entity_manager
->getDefinitions() as $type => $info) {
if (in_array($this->id, $info['entity_keys'])) {
throw new FieldException(format_string('Attempt to create field %id which is reserved by entity type %type.', array(
'%id' => $this->id,
'%type' => $type,
)));
}
}
$field_type = field_info_field_types($this->type);
if (!$field_type) {
throw new FieldException(format_string('Attempt to create a field of unknown type %type.', array(
'%type' => $this->type,
)));
}
$this->module = $field_type['module'];
$this->active = TRUE;
$this->settings += $field_type['settings'];
$this->storage += array(
'type' => variable_get('field_storage_default', 'field_sql_storage'),
'settings' => array(),
);
$storage_type = field_info_storage_types($this->storage['type']);
if (!$storage_type) {
throw new FieldException(format_string('Attempt to create a field with unknown storage type %type.', array(
'%type' => $this->storage['type'],
)));
}
$this->storage['module'] = $storage_type['module'];
$this->storage['active'] = TRUE;
$this->storage['settings'] += $storage_type['settings'];
$module_handler
->invoke($this->storage['module'], 'field_storage_create_field', array(
$this,
));
$result = parent::save();
field_cache_clear();
$module_handler
->invokeAll('field_create_field', array(
$this,
));
return $result;
}
protected function saveUpdated() {
$module_handler = \Drupal::moduleHandler();
$storage_controller = \Drupal::entityManager()
->getStorageController($this->entityType);
$original = $storage_controller
->loadUnchanged($this
->id());
if ($this->type != $original->type) {
throw new FieldException("Cannot change an existing field's type.");
}
if ($this->entity_types != $original->entity_types) {
throw new FieldException("Cannot change an existing field's entity_types property.");
}
if ($this->storage['type'] != $original->storage['type']) {
throw new FieldException("Cannot change an existing field's storage type.");
}
$this->settings += $original->settings;
$has_data = field_has_data($this);
$module_handler
->invokeAll('field_update_forbid', array(
$this,
$original,
$has_data,
));
$module_handler
->invoke($this->storage['module'], 'field_storage_update_field', array(
$this,
$original,
$has_data,
));
$result = parent::save();
field_cache_clear();
$module_handler
->invokeAll('field_update_field', array(
$this,
$original,
$has_data,
));
return $result;
}
public function delete() {
if (!$this->deleted) {
$module_handler = \Drupal::moduleHandler();
$instance_controller = \Drupal::entityManager()
->getStorageController('field_instance');
$state = \Drupal::state();
$instance_ids = array();
foreach ($this
->getBundles() as $entity_type => $bundles) {
foreach ($bundles as $bundle) {
$instance_ids[] = "{$entity_type}.{$bundle}.{$this->id}";
}
}
foreach ($instance_controller
->load($instance_ids) as $instance) {
$instance
->delete(FALSE);
}
$module_handler
->invoke($this->storage['module'], 'field_storage_delete_field', array(
$this,
));
$deleted_fields = $state
->get('field.field.deleted') ?: array();
$config = $this
->getExportProperties();
$config['deleted'] = TRUE;
$deleted_fields[$this->uuid] = $config;
$state
->set('field.field.deleted', $deleted_fields);
parent::delete();
field_cache_clear();
$module_handler
->invokeAll('field_delete_field', array(
$this,
));
}
}
public function getSchema() {
if (!isset($this->schema)) {
$module_handler = \Drupal::moduleHandler();
module_load_install($this->module);
$schema = (array) $module_handler
->invoke($this->module, 'field_schema', array(
$this,
));
$schema += array(
'columns' => array(),
'indexes' => array(),
'foreign keys' => array(),
);
if (array_intersect(array_keys($schema['columns']), static::getReservedColumns())) {
throw new FieldException('Illegal field type columns.');
}
$schema['indexes'] = $this->indexes + $schema['indexes'];
$this->schema = $schema;
}
return $this->schema;
}
public function getStorageDetails() {
if (!isset($this->storageDetails)) {
$module_handler = \Drupal::moduleHandler();
$details = (array) $module_handler
->invoke($this->storage['module'], 'field_storage_details', array(
$this,
));
$module_handler
->alter('field_storage_details', $details, $this);
$this->storageDetails = $details;
}
return $this->storageDetails;
}
public function getBundles() {
if (empty($this->deleted)) {
$map = field_info_field_map();
if (isset($map[$this->id]['bundles'])) {
return $map[$this->id]['bundles'];
}
}
return array();
}
public function offsetExists($offset) {
return isset($this->{$offset}) || in_array($offset, array(
'columns',
'foreign keys',
'bundles',
'storage_details',
));
}
public function &offsetGet($offset) {
switch ($offset) {
case 'id':
return $this->uuid;
case 'field_name':
return $this->id;
case 'columns':
$this
->getSchema();
return $this->schema['columns'];
case 'foreign keys':
$this
->getSchema();
return $this->schema['foreign keys'];
case 'bundles':
$bundles = $this
->getBundles();
return $bundles;
case 'storage_details':
$this
->getStorageDetails();
return $this->storageDetails;
}
return $this->{$offset};
}
public function offsetSet($offset, $value) {
if (!in_array($offset, array(
'columns',
'foreign keys',
'bundles',
'storage_details',
))) {
$this->{$offset} = $value;
}
}
public function offsetUnset($offset) {
if (!in_array($offset, array(
'columns',
'foreign keys',
'bundles',
'storage_details',
))) {
unset($this->{$offset});
}
}
public function serialize() {
return serialize($this
->getExportProperties());
}
public function unserialize($serialized) {
$this
->__construct(unserialize($serialized));
}
public static function getReservedColumns() {
return array(
'deleted',
);
}
}