git commit -m Issue
parent
675cade25d
commit
a234a4fa3b
|
@ -112,9 +112,6 @@ function hook_field_extra_fields_alter(&$info) {
|
||||||
* appears in edit forms, while @link field_formatter formatters @endlink
|
* appears in edit forms, while @link field_formatter formatters @endlink
|
||||||
* specify how the field appears in displayed entities.
|
* specify how the field appears in displayed entities.
|
||||||
*
|
*
|
||||||
* A third kind of pluggable handler, storage backends, is defined by the
|
|
||||||
* @link field_storage Field Storage API @endlink.
|
|
||||||
*
|
|
||||||
* See @link field Field API @endlink for information about the other parts of
|
* See @link field Field API @endlink for information about the other parts of
|
||||||
* the Field API.
|
* the Field API.
|
||||||
*/
|
*/
|
||||||
|
@ -541,11 +538,10 @@ function hook_field_update_forbid($field, $prior_field) {
|
||||||
/**
|
/**
|
||||||
* Acts when a field record is being purged.
|
* Acts when a field record is being purged.
|
||||||
*
|
*
|
||||||
* In field_purge_field(), after the field configuration has been removed from
|
* In field_purge_field(), after the field definition has been removed from the
|
||||||
* the database, the field storage module has had a chance to run its
|
* the system, the entity storage has purged stored field data, and the field
|
||||||
* hook_field_storage_purge_field(), and the field info cache has been cleared,
|
* info cache has been cleared, this hook is invoked on all modules to allow
|
||||||
* this hook is invoked on all modules to allow them to respond to the field
|
* them to respond to the field being purged.
|
||||||
* being purged.
|
|
||||||
*
|
*
|
||||||
* @param $field
|
* @param $field
|
||||||
* The field being purged.
|
* The field being purged.
|
||||||
|
@ -559,11 +555,10 @@ function hook_field_purge_field($field) {
|
||||||
/**
|
/**
|
||||||
* Acts when a field instance is being purged.
|
* Acts when a field instance is being purged.
|
||||||
*
|
*
|
||||||
* In field_purge_instance(), after the field instance has been removed from the
|
* In field_purge_instance(), after the instance definition has been removed
|
||||||
* database, the field storage module has had a chance to run its
|
* from the the system, the entity storage has purged stored field data, and the
|
||||||
* hook_field_storage_purge_instance(), and the field info cache has been
|
* field info cache has been cleared, this hook is invoked on all modules to
|
||||||
* cleared, this hook is invoked on all modules to allow them to respond to the
|
* allow them to respond to the field instance being purged.
|
||||||
* field instance being purged.
|
|
||||||
*
|
*
|
||||||
* @param $instance
|
* @param $instance
|
||||||
* The instance being purged.
|
* The instance being purged.
|
||||||
|
|
|
@ -48,22 +48,7 @@ use Drupal\entity\Entity\EntityFormDisplay;
|
||||||
* allows any module to act on Field Attach operations for any entity after the
|
* allows any module to act on Field Attach operations for any entity after the
|
||||||
* operation is complete, and access or modify all the field, form, or display
|
* operation is complete, and access or modify all the field, form, or display
|
||||||
* data for that entity and operation. For example, field_attach_view() invokes
|
* data for that entity and operation. For example, field_attach_view() invokes
|
||||||
* hook_field_attach_view_alter(). These all-module hooks are distinct from
|
* hook_field_attach_view_alter().
|
||||||
* those of the Field Types API, such as hook_field_load(), that are only
|
|
||||||
* invoked for the module that defines a specific field type.
|
|
||||||
*
|
|
||||||
* field_attach_load(), field_attach_insert(), and field_attach_update() also
|
|
||||||
* define pre-operation hooks, e.g. hook_field_attach_pre_load(). These hooks
|
|
||||||
* run before the corresponding Field Storage API and Field Type API operations.
|
|
||||||
* They allow modules to define additional storage locations (e.g.
|
|
||||||
* denormalizing, mirroring) for field data on a per-field basis. They also
|
|
||||||
* allow modules to take over field storage completely by instructing other
|
|
||||||
* implementations of the same hook and the Field Storage API itself not to
|
|
||||||
* operate on specified fields.
|
|
||||||
*
|
|
||||||
* The pre-operation hooks do not make the Field Storage API irrelevant. The
|
|
||||||
* Field Storage API is essentially the "fallback mechanism" for any fields that
|
|
||||||
* aren't being intercepted explicitly by pre-operation hooks.
|
|
||||||
*
|
*
|
||||||
* @link field_language Field language API @endlink provides information about
|
* @link field_language Field language API @endlink provides information about
|
||||||
* the structure of field objects.
|
* the structure of field objects.
|
||||||
|
|
|
@ -69,10 +69,6 @@ require_once __DIR__ . '/field.deprecated.inc';
|
||||||
* fields, instances, widgets, and related information defined by or with the
|
* fields, instances, widgets, and related information defined by or with the
|
||||||
* Field API.
|
* Field API.
|
||||||
*
|
*
|
||||||
* - @link field_storage Field Storage API @endlink: Provides a pluggable back
|
|
||||||
* -end storage system for actual field data. The default implementation,
|
|
||||||
* field_sql_storage.module, stores field data in the local SQL database.
|
|
||||||
*
|
|
||||||
* - @link field_purge Field API bulk data deletion @endlink: Cleans up after
|
* - @link field_purge Field API bulk data deletion @endlink: Cleans up after
|
||||||
* bulk deletion operations such as deletion of field or field_instance.
|
* bulk deletion operations such as deletion of field or field_instance.
|
||||||
*
|
*
|
||||||
|
@ -113,15 +109,11 @@ const FIELD_BEHAVIOR_CUSTOM = 0x0004;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the most recent version of an entity's field data.
|
* Load the most recent version of an entity's field data.
|
||||||
*
|
|
||||||
* @see field_attach_load().
|
|
||||||
*/
|
*/
|
||||||
const FIELD_LOAD_CURRENT = 'FIELD_LOAD_CURRENT';
|
const FIELD_LOAD_CURRENT = 'FIELD_LOAD_CURRENT';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load the version of an entity's field data specified in the entity.
|
* Load the version of an entity's field data specified in the entity.
|
||||||
*
|
|
||||||
* @see field_attach_load().
|
|
||||||
*/
|
*/
|
||||||
const FIELD_LOAD_REVISION = 'FIELD_LOAD_REVISION';
|
const FIELD_LOAD_REVISION = 'FIELD_LOAD_REVISION';
|
||||||
|
|
||||||
|
@ -159,10 +151,7 @@ function field_help($path, $arg) {
|
||||||
'#theme' => 'item_list',
|
'#theme' => 'item_list',
|
||||||
'#items' => $items['items'],
|
'#items' => $items['items'],
|
||||||
);
|
);
|
||||||
$output .= drupal_render($item_list) . '</dd>';
|
$output .= drupal_render($item_list);
|
||||||
$output .= '<dt>' . t('Managing field data storage') . '</dt>';
|
|
||||||
$output .= '<dd>' . t('Developers of field modules can either use the default <a href="@sql-store">Field SQL Storage module</a> to store data for their fields, or a contributed or custom module developed using the <a href="@storage-api">field storage API</a>.', array('@storage-api' => 'http://api.drupal.org/api/group/field_storage/8', '@sql-store' => url('admin/help/field_sql_storage'))) . '</dd>';
|
|
||||||
$output .= '</dl>';
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file
|
* @file
|
||||||
* Field CRUD API, handling field and field instance creation and deletion.
|
* Provides support for field data purge after mass deletion.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Drupal\field\Entity\Field;
|
use Drupal\field\Entity\Field;
|
||||||
|
@ -17,26 +17,21 @@ use Drupal\field\FieldException;
|
||||||
* entities as well as deleting entire fields or field instances in a single
|
* entities as well as deleting entire fields or field instances in a single
|
||||||
* operation.
|
* operation.
|
||||||
*
|
*
|
||||||
* Deleting field data items for an entity with field_attach_delete() involves
|
* When a single entity is deleted, the Entity storage controller performs the
|
||||||
* three separate operations:
|
* following operations:
|
||||||
* - Invoking the Field Type API hook_field_delete() for each field on the
|
* - Invoking the FieldInterface delete() method for each field on the
|
||||||
* entity. The hook for each field type receives the entity and the specific
|
* entity. A file field type might use this method to delete uploaded files
|
||||||
* field being deleted. A file field module might use this hook to delete
|
* from the filesystem.
|
||||||
* uploaded files from the filesystem.
|
* - Removing the data from storage.
|
||||||
* - Invoking the Field Storage API hook_field_storage_delete() to remove data
|
* - Invoking the global hook_entity_delete() for all modules that implement it.
|
||||||
* from the primary field storage. The hook implementation receives the entity
|
* Each hook implementation receives the entity being deleted and can operate
|
||||||
* being deleted and deletes data for all of the entity's bundle's fields.
|
* on whichever subset of the entity's bundle's fields it chooses to.
|
||||||
* - Invoking the global Field Attach API hook_field_attach_delete() for all
|
|
||||||
* modules that implement it. Each hook implementation receives the entity
|
|
||||||
* being deleted and can operate on whichever subset of the entity's bundle's
|
|
||||||
* fields it chooses to.
|
|
||||||
*
|
*
|
||||||
* These hooks are invoked immediately when field_attach_delete() is called.
|
* Similar operations are performed on deletion of a single entity revision.
|
||||||
* Similar operations are performed for field_attach_delete_revision().
|
|
||||||
*
|
*
|
||||||
* When a field, bundle, or field instance is deleted, it is not practical to
|
* When a field, bundle, or field instance is deleted, it is not practical to
|
||||||
* invoke these hooks immediately on every affected entity in a single page
|
* perform those operations immediately on every affected entity in a single
|
||||||
* request; there could be thousands or millions of them. Instead, the
|
* page request; there could be thousands or millions of them. Instead, the
|
||||||
* appropriate field data items, instances, and/or fields are marked as deleted
|
* appropriate field data items, instances, and/or fields are marked as deleted
|
||||||
* so that subsequent load or query operations will not return them. Later, a
|
* so that subsequent load or query operations will not return them. Later, a
|
||||||
* separate process cleans up, or "purges", the marked-as-deleted data by going
|
* separate process cleans up, or "purges", the marked-as-deleted data by going
|
||||||
|
@ -44,34 +39,18 @@ use Drupal\field\FieldException;
|
||||||
* field and instance records.
|
* field and instance records.
|
||||||
*
|
*
|
||||||
* Purging field data is made somewhat tricky by the fact that, while
|
* Purging field data is made somewhat tricky by the fact that, while
|
||||||
* field_attach_delete() has a complete entity to pass to the various deletion
|
* $entity->delete() has a complete entity to pass to the various deletion
|
||||||
* hooks, the Field API purge process only has the field data it has previously
|
* steps, the Field API purge process only has the field data it has previously
|
||||||
* stored. It cannot reconstruct complete original entities to pass to the
|
* stored. It cannot reconstruct complete original entities to pass to the
|
||||||
* deletion hooks. It is even possible that the original entity to which some
|
* deletion operations. It is even possible that the original entity to which
|
||||||
* Field API data was attached has been itself deleted before the field purge
|
* some Field API data was attached has been itself deleted before the field
|
||||||
* operation takes place.
|
* purge operation takes place.
|
||||||
*
|
*
|
||||||
* Field API resolves this problem by using "pseudo-entities" during purge
|
* Field API resolves this problem by using stub entities during purge
|
||||||
* operations. A pseudo-entity contains only the information from the original
|
* operations, containing only the information from the original entity that
|
||||||
* entity that Field API knows about: entity type, ID, revision ID, and bundle.
|
* Field API knows about: entity type, ID, revision ID, and bundle. It also
|
||||||
* It also contains the field data for whichever field instance is currently
|
* contains the field data for whichever field instance is currently being
|
||||||
* being purged. For example, suppose that the node type 'story' used to contain
|
* purged.
|
||||||
* a field called 'subtitle' but the field was deleted. If node 37 was a story
|
|
||||||
* with a subtitle, the pseudo-entity passed to the purge hooks would look
|
|
||||||
* something like this:
|
|
||||||
*
|
|
||||||
* @code
|
|
||||||
* $entity = stdClass Object(
|
|
||||||
* [nid] => 37,
|
|
||||||
* [vid] => 37,
|
|
||||||
* [type] => 'story',
|
|
||||||
* [subtitle] => array(
|
|
||||||
* [0] => array(
|
|
||||||
* 'value' => 'subtitle text',
|
|
||||||
* ),
|
|
||||||
* ),
|
|
||||||
* );
|
|
||||||
* @endcode
|
|
||||||
*
|
*
|
||||||
* See @link field Field API @endlink for information about the other parts of
|
* See @link field Field API @endlink for information about the other parts of
|
||||||
* the Field API.
|
* the Field API.
|
||||||
|
|
|
@ -7,13 +7,8 @@
|
||||||
|
|
||||||
namespace Drupal\field\Tests;
|
namespace Drupal\field\Tests;
|
||||||
|
|
||||||
use Drupal\Core\Language\Language;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit test class for storage-related field_attach_* functions.
|
* Unit test class for storage-related field behavior.
|
||||||
*
|
|
||||||
* All field_attach_* test work with all field_storage plugins and
|
|
||||||
* all hook_field_attach_pre_{load,insert,update}() hooks.
|
|
||||||
*/
|
*/
|
||||||
class FieldAttachStorageTest extends FieldUnitTestBase {
|
class FieldAttachStorageTest extends FieldUnitTestBase {
|
||||||
|
|
||||||
|
|
|
@ -1,463 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file
|
|
||||||
* Defines a field storage backend.
|
|
||||||
*/
|
|
||||||
|
|
||||||
use Drupal\Core\Entity\EntityInterface;
|
|
||||||
use Drupal\field\FieldInstanceInterface;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_info().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_info() {
|
|
||||||
return array(
|
|
||||||
'field_test_storage' => array(
|
|
||||||
'label' => t('Test storage'),
|
|
||||||
'description' => t('Dummy test storage backend. Stores field values in the variable table.'),
|
|
||||||
),
|
|
||||||
'field_test_storage_failure' => array(
|
|
||||||
'label' => t('Test storage failure'),
|
|
||||||
'description' => t('Dummy test storage backend. Always fails to create fields.'),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_details().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_details($field) {
|
|
||||||
$details = array();
|
|
||||||
|
|
||||||
// Add field columns.
|
|
||||||
$columns = array();
|
|
||||||
foreach ((array) $field['columns'] as $column_name => $attributes) {
|
|
||||||
$columns[$column_name] = $column_name;
|
|
||||||
}
|
|
||||||
return array(
|
|
||||||
'drupal_variables' => array(
|
|
||||||
'field_test_storage_data[FIELD_LOAD_CURRENT]' => $columns,
|
|
||||||
'field_test_storage_data[FIELD_LOAD_REVISION]' => $columns,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_details_alter().
|
|
||||||
*
|
|
||||||
* @see FieldAttachStorageTestCase::testFieldStorageDetailsAlter()
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_details_alter(&$details, $field) {
|
|
||||||
|
|
||||||
// For testing, storage details are changed only because of the field name.
|
|
||||||
if ($field['field_name'] == 'field_test_change_my_details') {
|
|
||||||
$columns = array();
|
|
||||||
foreach ((array) $field['columns'] as $column_name => $attributes) {
|
|
||||||
$columns[$column_name] = $column_name;
|
|
||||||
}
|
|
||||||
$details['drupal_variables'] = array(
|
|
||||||
FIELD_LOAD_CURRENT => array(
|
|
||||||
'moon' => $columns,
|
|
||||||
),
|
|
||||||
FIELD_LOAD_REVISION => array(
|
|
||||||
'mars' => $columns,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper function: stores or retrieves data from the 'storage backend'.
|
|
||||||
*/
|
|
||||||
function _field_test_storage_data($data = NULL) {
|
|
||||||
if (!isset($data)) {
|
|
||||||
return Drupal::state()->get('field_test.storage_data');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Drupal::state()->set('field_test.storage_data', $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_load().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_load($entity_type, $entities, $age, $fields, $options) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
$load_current = $age == FIELD_LOAD_CURRENT;
|
|
||||||
|
|
||||||
foreach ($fields as $field_id => $ids) {
|
|
||||||
$field = field_info_field_by_id($field_id);
|
|
||||||
$field_name = $field['field_name'];
|
|
||||||
$field_data = $data[$field['uuid']];
|
|
||||||
$sub_table = $load_current ? 'current' : 'revisions';
|
|
||||||
$delta_count = array();
|
|
||||||
foreach ($field_data[$sub_table] as $row) {
|
|
||||||
if ($row->type == $entity_type && (!$row->deleted || $options['deleted'])) {
|
|
||||||
if (($load_current && in_array($row->entity_id, $ids)) || (!$load_current && in_array($row->revision_id, $ids))) {
|
|
||||||
if (in_array($row->langcode, field_available_languages($entity_type, $field))) {
|
|
||||||
if (!isset($delta_count[$row->entity_id][$row->langcode])) {
|
|
||||||
$delta_count[$row->entity_id][$row->langcode] = 0;
|
|
||||||
}
|
|
||||||
if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED || $delta_count[$row->entity_id][$row->langcode] < $field['cardinality']) {
|
|
||||||
$item = array();
|
|
||||||
foreach ($field['columns'] as $column => $attributes) {
|
|
||||||
$item[$column] = $row->{$column};
|
|
||||||
}
|
|
||||||
$entities[$row->entity_id]->{$field_name}[$row->langcode][] = $item;
|
|
||||||
$delta_count[$row->entity_id][$row->langcode]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_write().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_write(EntityInterface $entity, $op, $fields) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
$id = $entity->id();
|
|
||||||
$vid = $entity->getRevisionId();
|
|
||||||
$bundle = $entity->bundle();
|
|
||||||
|
|
||||||
foreach ($fields as $field_id) {
|
|
||||||
$field = field_info_field_by_id($field_id);
|
|
||||||
$field_name = $field['field_name'];
|
|
||||||
$field_data = &$data[$field_id];
|
|
||||||
|
|
||||||
$all_langcodes = field_available_languages($entity->entityType(), $field);
|
|
||||||
$field_langcodes = array_intersect($all_langcodes, array_keys((array) $entity->$field_name));
|
|
||||||
|
|
||||||
// Delete and insert, rather than update, in case a value was added.
|
|
||||||
if ($op == FIELD_STORAGE_UPDATE) {
|
|
||||||
// Delete languages present in the incoming $entity->$field_name.
|
|
||||||
// Delete all languages if $entity->$field_name is empty.
|
|
||||||
$langcodes = !empty($entity->$field_name) ? $field_langcodes : $all_langcodes;
|
|
||||||
if ($langcodes) {
|
|
||||||
foreach ($field_data['current'] as $key => $row) {
|
|
||||||
if ($row->type == $entity->entityType() && $row->entity_id == $id && in_array($row->langcode, $langcodes)) {
|
|
||||||
unset($field_data['current'][$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isset($vid)) {
|
|
||||||
foreach ($field_data['revisions'] as $key => $row) {
|
|
||||||
if ($row->type == $entity->entityType() && $row->revision_id == $vid) {
|
|
||||||
unset($field_data['revisions'][$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($field_langcodes as $langcode) {
|
|
||||||
$items = (array) $entity->{$field_name}[$langcode];
|
|
||||||
$delta_count = 0;
|
|
||||||
foreach ($items as $delta => $item) {
|
|
||||||
$row = (object) array(
|
|
||||||
'field_id' => $field_id,
|
|
||||||
'type' => $entity->entityType(),
|
|
||||||
'entity_id' => $id,
|
|
||||||
'revision_id' => $vid,
|
|
||||||
'bundle' => $bundle,
|
|
||||||
'delta' => $delta,
|
|
||||||
'deleted' => FALSE,
|
|
||||||
'langcode' => $langcode,
|
|
||||||
);
|
|
||||||
foreach ($field['columns'] as $column => $attributes) {
|
|
||||||
$row->{$column} = isset($item[$column]) ? $item[$column] : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
$field_data['current'][] = $row;
|
|
||||||
if (isset($vid)) {
|
|
||||||
$field_data['revisions'][] = $row;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($field['cardinality'] != FIELD_CARDINALITY_UNLIMITED && ++$delta_count == $field['cardinality']) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_field_test_storage_data($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_delete().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_delete(EntityInterface $entity, $fields) {
|
|
||||||
// Note: reusing field_test_storage_purge(), like field_sql_storage.module
|
|
||||||
// does, is highly inefficient in our case...
|
|
||||||
foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
|
|
||||||
if (isset($fields[$instance['field_id']])) {
|
|
||||||
$field = $instance->getField();
|
|
||||||
field_test_field_storage_purge($entity, $field, $instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_purge().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_purge(EntityInterface $entity, $field, $instance) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
$field_data = &$data[$field['uuid']];
|
|
||||||
foreach (array('current', 'revisions') as $sub_table) {
|
|
||||||
foreach ($field_data[$sub_table] as $key => $row) {
|
|
||||||
if ($row->type == $entity->entityType() && $row->entity_id == $entity->id()) {
|
|
||||||
unset($field_data[$sub_table][$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_field_test_storage_data($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_delete_revision().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_delete_revision(EntityInterface $entity, $fields) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
foreach ($fields as $field_id) {
|
|
||||||
$field_data = &$data[$field_id];
|
|
||||||
foreach (array('current', 'revisions') as $sub_table) {
|
|
||||||
foreach ($field_data[$sub_table] as $key => $row) {
|
|
||||||
if ($row->type == $entity->entityType() && $row->entity_id == $entity->id() && $row->revision_id == $entity->getRevisionId()) {
|
|
||||||
unset($field_data[$sub_table][$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_field_test_storage_data($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_query().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_query($field_id, $conditions, $count, &$cursor = NULL, $age) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
$load_current = $age == FIELD_LOAD_CURRENT;
|
|
||||||
|
|
||||||
$field = field_info_field_by_id($field_id);
|
|
||||||
$field_columns = array_keys($field['columns']);
|
|
||||||
|
|
||||||
$field_data = $data[$field['uuid']];
|
|
||||||
$sub_table = $load_current ? 'current' : 'revisions';
|
|
||||||
// We need to sort records by entity type and entity id.
|
|
||||||
usort($field_data[$sub_table], '_field_test_field_storage_query_sort_helper');
|
|
||||||
|
|
||||||
// Initialize results array.
|
|
||||||
$return = array();
|
|
||||||
$entity_count = 0;
|
|
||||||
$rows_count = 0;
|
|
||||||
$rows_total = count($field_data[$sub_table]);
|
|
||||||
$skip = $cursor;
|
|
||||||
$skipped = 0;
|
|
||||||
|
|
||||||
foreach ($field_data[$sub_table] as $row) {
|
|
||||||
if ($count != FIELD_QUERY_NO_LIMIT && $entity_count >= $count) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($row->field_id == $field['uuid']) {
|
|
||||||
$match = TRUE;
|
|
||||||
$condition_deleted = FALSE;
|
|
||||||
// Add conditions.
|
|
||||||
foreach ($conditions as $condition) {
|
|
||||||
@list($column, $value, $operator) = $condition;
|
|
||||||
if (empty($operator)) {
|
|
||||||
$operator = is_array($value) ? 'IN' : '=';
|
|
||||||
}
|
|
||||||
switch ($operator) {
|
|
||||||
case '=':
|
|
||||||
$match = $match && $row->{$column} == $value;
|
|
||||||
break;
|
|
||||||
case '<>':
|
|
||||||
case '<':
|
|
||||||
case '<=':
|
|
||||||
case '>':
|
|
||||||
case '>=':
|
|
||||||
eval('$match = $match && ' . $row->{$column} . ' ' . $operator . ' '. $value);
|
|
||||||
break;
|
|
||||||
case 'IN':
|
|
||||||
$match = $match && in_array($row->{$column}, $value);
|
|
||||||
break;
|
|
||||||
case 'NOT IN':
|
|
||||||
$match = $match && !in_array($row->{$column}, $value);
|
|
||||||
break;
|
|
||||||
case 'BETWEEN':
|
|
||||||
$match = $match && $row->{$column} >= $value[0] && $row->{$column} <= $value[1];
|
|
||||||
break;
|
|
||||||
case 'STARTS_WITH':
|
|
||||||
case 'ENDS_WITH':
|
|
||||||
case 'CONTAINS':
|
|
||||||
// Not supported.
|
|
||||||
$match = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Track condition on 'deleted'.
|
|
||||||
if ($column == 'deleted') {
|
|
||||||
$condition_deleted = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exclude deleted data unless we have a condition on it.
|
|
||||||
if (!$condition_deleted && $row->deleted) {
|
|
||||||
$match = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($match) {
|
|
||||||
if (!isset($skip) || $skipped >= $skip) {
|
|
||||||
$cursor++;
|
|
||||||
// If querying all revisions and the entity type has revisions, we need
|
|
||||||
// to key the results by revision_ids.
|
|
||||||
$entity_type = entity_get_info($row->type);
|
|
||||||
$id = ($load_current || empty($entity_type['entity_keys']['revision'])) ? $row->entity_id : $row->revision_id;
|
|
||||||
|
|
||||||
if (!isset($return[$row->type][$id])) {
|
|
||||||
$return[$row->type][$id] = (object) array('entity_id' => $row->entity_id, 'revision_id' => $row->revision_id, 'bundle' => $row->bundle);
|
|
||||||
$entity_count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$skipped++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$rows_count++;
|
|
||||||
|
|
||||||
// The query is complete if we walked the whole array.
|
|
||||||
if ($count != FIELD_QUERY_NO_LIMIT && $rows_count >= $rows_total) {
|
|
||||||
$cursor = FIELD_QUERY_COMPLETE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sort helper for field_test_field_storage_query().
|
|
||||||
*
|
|
||||||
* Sorts by entity type and entity id.
|
|
||||||
*/
|
|
||||||
function _field_test_field_storage_query_sort_helper($a, $b) {
|
|
||||||
if ($a->type == $b->type) {
|
|
||||||
if ($a->entity_id == $b->entity_id) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return $a->entity_id < $b->entity_id ? -1 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return $a->type < $b->type ? -1 : 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_create_field().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_create_field($field) {
|
|
||||||
if ($field['storage']['type'] == 'field_test_storage_failure') {
|
|
||||||
throw new Exception('field_test_storage_failure engine always fails to create fields');
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
$data[$field['uuid']] = array(
|
|
||||||
'current' => array(),
|
|
||||||
'revisions' => array(),
|
|
||||||
);
|
|
||||||
|
|
||||||
_field_test_storage_data($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_delete_field().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_delete_field($field) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
$field_data = &$data[$field['uuid']];
|
|
||||||
foreach (array('current', 'revisions') as $sub_table) {
|
|
||||||
foreach ($field_data[$sub_table] as &$row) {
|
|
||||||
$row->deleted = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_field_test_storage_data($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_field_storage_delete_instance().
|
|
||||||
*/
|
|
||||||
function field_test_field_storage_delete_instance($instance) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
$field = $instance->getField();
|
|
||||||
$field_data = &$data[$field['uuid']];
|
|
||||||
foreach (array('current', 'revisions') as $sub_table) {
|
|
||||||
foreach ($field_data[$sub_table] as &$row) {
|
|
||||||
if ($row->bundle == $instance['bundle']) {
|
|
||||||
$row->deleted = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_field_test_storage_data($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_entity_bundle_rename().
|
|
||||||
*/
|
|
||||||
function field_test_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
// We need to account for deleted or inactive fields and instances.
|
|
||||||
$instances = field_read_instances(array('bundle' => $bundle_new), array('include_deleted' => TRUE, 'include_inactive' => TRUE));
|
|
||||||
foreach ($instances as $instance) {
|
|
||||||
$field = $instance->getField();
|
|
||||||
if ($field && $field['storage']['type'] == 'field_test_storage') {
|
|
||||||
$field_data = &$data[$field['uuid']];
|
|
||||||
foreach (array('current', 'revisions') as $sub_table) {
|
|
||||||
foreach ($field_data[$sub_table] as &$row) {
|
|
||||||
if ($row->bundle == $bundle_old) {
|
|
||||||
$row->bundle = $bundle_new;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_field_test_storage_data($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implements hook_ENTITY_TYPE_delete() for 'field_instance'.
|
|
||||||
*/
|
|
||||||
function field_test_field_instance_delete(FieldInstanceInterface $field_instance) {
|
|
||||||
$data = _field_test_storage_data();
|
|
||||||
|
|
||||||
$field = $field_instance->getField();
|
|
||||||
if ($field->storage['type'] == 'field_test_storage') {
|
|
||||||
$field_data = &$data[$field->uuid];
|
|
||||||
foreach (array('current', 'revisions') as $sub_table) {
|
|
||||||
foreach ($field_data[$sub_table] as &$row) {
|
|
||||||
if ($row->bundle == $field_instance->bundle) {
|
|
||||||
$row->deleted = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_field_test_storage_data($data);
|
|
||||||
}
|
|
|
@ -492,11 +492,10 @@ function hook_node_create(\Drupal\Core\Entity\EntityInterface $node) {
|
||||||
*
|
*
|
||||||
* This hook is invoked during node loading, which is handled by entity_load(),
|
* This hook is invoked during node loading, which is handled by entity_load(),
|
||||||
* via classes Drupal\node\NodeStorageController and
|
* via classes Drupal\node\NodeStorageController and
|
||||||
* Drupal\Core\Entity\DatabaseStorageController. After the node information is
|
* Drupal\Core\Entity\DatabaseStorageController. After the node information and
|
||||||
* read from the database or the entity cache, then field_attach_load_revision()
|
* field values are read from the database or the entity cache,
|
||||||
* or field_attach_load() is called, then hook_entity_load() is invoked on all
|
* hook_entity_load() is invoked on all implementing modules, and finally
|
||||||
* implementing modules, and finally hook_node_load() is invoked on all
|
* hook_node_load() is invoked on all implementing modules.
|
||||||
* implementing modules.
|
|
||||||
*
|
*
|
||||||
* @param $nodes
|
* @param $nodes
|
||||||
* An array of the nodes being loaded, keyed by nid.
|
* An array of the nodes being loaded, keyed by nid.
|
||||||
|
|
Loading…
Reference in New Issue