2009-02-05 03:42:58 +00:00
< ? php
2009-02-08 21:22:59 +00:00
/**
* @ file
2013-09-02 20:26:08 +00:00
* Provides support for field data purge after mass deletion .
2009-02-08 21:22:59 +00:00
*/
2017-11-14 13:54:31 +00:00
use Drupal\Core\Field\FieldDefinitionInterface ;
2014-08-26 03:44:40 +00:00
use Drupal\Core\Field\FieldException ;
2017-11-14 13:54:31 +00:00
use Drupal\Core\Field\FieldStorageDefinitionInterface ;
2012-05-07 02:56:49 +00:00
2009-12-03 02:30:18 +00:00
/**
2009-08-11 14:59:40 +00:00
* @ defgroup field_purge Field API bulk data deletion
* @ {
2012-09-27 15:44:17 +00:00
* Cleans up after Field API bulk deletion operations .
2009-08-11 14:59:40 +00:00
*
* Field API provides functions for deleting data attached to individual
2014-09-20 02:45:52 +00:00
* entities as well as deleting entire fields or field storages in a single
2009-08-11 14:59:40 +00:00
* operation .
*
2014-03-27 11:54:40 +00:00
* When a single entity is deleted , the Entity storage performs the
2013-09-02 20:26:08 +00:00
* following operations :
2016-09-23 09:58:55 +00:00
* - Invoking the method \Drupal\Core\Field\FieldItemListInterface :: delete () for
* each field on the entity . A file field type might use this method to delete
* uploaded files from the filesystem .
2013-09-02 20:26:08 +00:00
* - Removing the data from storage .
* - Invoking the global hook_entity_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 .
2009-08-11 14:59:40 +00:00
*
2013-09-02 20:26:08 +00:00
* Similar operations are performed on deletion of a single entity revision .
2009-08-11 14:59:40 +00:00
*
2014-09-20 02:45:52 +00:00
* When a bundle , field or field storage is deleted , it is not practical to
2013-09-02 20:26:08 +00:00
* perform those operations immediately on every affected entity in a single
* page request ; there could be thousands or millions of them . Instead , the
2014-09-20 02:45:52 +00:00
* appropriate field data items , fields , and / or field storages are marked as
* deleted 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 through the three - step process described above and , finally ,
* removing deleted field storage and field records .
2009-08-11 14:59:40 +00:00
*
* Purging field data is made somewhat tricky by the fact that , while
2013-09-02 20:26:08 +00:00
* $entity -> delete () has a complete entity to pass to the various deletion
* steps , the Field API purge process only has the field data it has previously
2010-02-12 05:38:10 +00:00
* stored . It cannot reconstruct complete original entities to pass to the
2013-09-02 20:26:08 +00:00
* deletion operations . It is even possible that the original entity to which
* some Field API data was attached has been itself deleted before the field
* purge operation takes place .
2009-08-11 14:59:40 +00:00
*
2013-09-02 20:26:08 +00:00
* Field API resolves this problem by using stub entities during purge
* operations , containing only the information from the original entity that
* Field API knows about : entity type , ID , revision ID , and bundle . It also
2014-09-20 02:45:52 +00:00
* contains the field data for whichever field is currently being purged .
2011-11-30 02:42:42 +00:00
*
2011-12-22 09:32:53 +00:00
* See @ link field Field API @ endlink for information about the other parts of
* the Field API .
2009-08-11 14:59:40 +00:00
*/
/**
2014-09-20 02:45:52 +00:00
* Purges a batch of deleted Field API data , field storages , or fields .
2009-08-11 14:59:40 +00:00
*
2012-03-23 19:45:06 +00:00
* This function will purge deleted field data in batches . The batch size
* is defined as an argument to the function , and once each batch is finished ,
* it continues with the next batch until all have completed . If a deleted field
2014-09-20 02:45:52 +00:00
* with no remaining data records is found , the field itself will
* be purged . If a deleted field storage with no remaining fields is found , the
* field storage itself will be purged .
2009-08-11 14:59:40 +00:00
*
2017-11-14 13:54:31 +00:00
* @ param int $batch_size
2009-08-11 14:59:40 +00:00
* The maximum number of field data records to purge before returning .
2017-11-14 13:54:31 +00:00
* @ param string $field_storage_unique_id
* ( optional ) Limit the purge to a specific field storage . Defaults to NULL .
2009-08-11 14:59:40 +00:00
*/
2017-11-14 13:54:31 +00:00
function field_purge_batch ( $batch_size , $field_storage_unique_id = NULL ) {
/** @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface $deleted_fields_repository */
$deleted_fields_repository = \Drupal :: service ( 'entity_field.deleted_fields_repository' );
$fields = $deleted_fields_repository -> getFieldDefinitions ( $field_storage_unique_id );
2014-07-18 18:56:27 +00:00
2014-01-01 10:08:57 +00:00
$info = \Drupal :: entityManager () -> getDefinitions ();
2014-09-20 02:45:52 +00:00
foreach ( $fields as $field ) {
2015-02-10 13:39:31 +00:00
$entity_type = $field -> getTargetEntityTypeId ();
2013-04-13 17:06:40 +00:00
2013-09-09 16:39:09 +00:00
// We cannot purge anything if the entity type is unknown (e.g. the
// providing module was uninstalled).
2015-05-24 20:08:46 +00:00
// @todo Revisit after https://www.drupal.org/node/2080823.
2013-09-09 16:39:09 +00:00
if ( ! isset ( $info [ $entity_type ])) {
2017-10-27 23:02:43 +00:00
\Drupal :: logger ( 'field' ) -> warning ( " Cannot remove field @field_name because the entity type is unknown: %entity_type " , [ '@field_name' => $field -> getName (), '%entity_type' => $entity_type ]);
2013-09-09 16:39:09 +00:00
continue ;
}
2014-09-20 02:45:52 +00:00
$count_purged = \Drupal :: entityManager () -> getStorage ( $entity_type ) -> purgeFieldData ( $field , $batch_size );
2014-07-01 12:58:10 +00:00
if ( $count_purged < $batch_size || $count_purged == 0 ) {
2014-09-20 02:45:52 +00:00
// No field data remains for the field, so we can remove it.
field_purge_field ( $field );
2009-08-11 14:59:40 +00:00
}
2014-07-01 12:58:10 +00:00
$batch_size -= $count_purged ;
// Only delete up to the maximum number of records.
if ( $batch_size == 0 ) {
break ;
}
2009-08-11 14:59:40 +00:00
}
2014-09-20 02:45:52 +00:00
// Retrieve all deleted field storages. Any that have no fields can be purged.
2017-11-14 13:54:31 +00:00
foreach ( $deleted_fields_repository -> getFieldStorageDefinitions () as $field_storage ) {
if ( $field_storage_unique_id && $field_storage -> getUniqueStorageIdentifier () != $field_storage_unique_id ) {
2014-04-22 19:50:19 +00:00
// If a specific UUID is provided, only purge the corresponding field.
continue ;
}
2013-09-09 16:39:09 +00:00
// We cannot purge anything if the entity type is unknown (e.g. the
// providing module was uninstalled).
2015-05-24 20:08:46 +00:00
// @todo Revisit after https://www.drupal.org/node/2080823.
2015-02-10 13:39:31 +00:00
if ( ! isset ( $info [ $field_storage -> getTargetEntityTypeId ()])) {
2013-09-09 16:39:09 +00:00
continue ;
}
2017-11-14 13:54:31 +00:00
$fields = $deleted_fields_repository -> getFieldDefinitions ( $field_storage -> getUniqueStorageIdentifier ());
2014-09-20 02:45:52 +00:00
if ( empty ( $fields )) {
2014-07-18 18:56:27 +00:00
field_purge_field_storage ( $field_storage );
2009-08-11 14:59:40 +00:00
}
}
}
/**
2014-09-20 02:45:52 +00:00
* Purges a field record from the database .
2009-08-11 14:59:40 +00:00
*
2014-09-20 02:45:52 +00:00
* This function assumes all data for the field has already been purged and
2009-08-11 14:59:40 +00:00
* should only be called by field_purge_batch () .
*
2017-11-14 13:54:31 +00:00
* @ param \Drupal\Core\Field\FieldDefinitionInterface $field
* The field to purge .
2009-08-11 14:59:40 +00:00
*/
2017-11-14 13:54:31 +00:00
function field_purge_field ( FieldDefinitionInterface $field ) {
/** @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface $deleted_fields_repository */
$deleted_fields_repository = \Drupal :: service ( 'entity_field.deleted_fields_repository' );
$deleted_fields_repository -> removeFieldDefinition ( $field );
2013-04-13 17:06:40 +00:00
2009-08-11 14:59:40 +00:00
// Invoke external hooks after the cache is cleared for API consistency.
2017-03-04 01:20:24 +00:00
\Drupal :: moduleHandler () -> invokeAll ( 'field_purge_field' , [ $field ]);
2009-08-11 14:59:40 +00:00
}
/**
2009-12-03 02:30:18 +00:00
* Purges a field record from the database .
2009-08-11 14:59:40 +00:00
*
2014-09-20 02:45:52 +00:00
* This function assumes all fields for the field storage has already been
* purged , and should only be called by field_purge_batch () .
2009-08-11 14:59:40 +00:00
*
2017-11-14 13:54:31 +00:00
* @ param \Drupal\Core\Field\FieldStorageDefinitionInterface $field_storage
2014-07-18 18:56:27 +00:00
* The field storage to purge .
2014-09-20 02:45:52 +00:00
*
2017-11-14 13:54:31 +00:00
* @ throws \Drupal\Core\Field\FieldException
2009-08-11 14:59:40 +00:00
*/
2017-11-14 13:54:31 +00:00
function field_purge_field_storage ( FieldStorageDefinitionInterface $field_storage ) {
/** @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface $deleted_fields_repository */
$deleted_fields_repository = \Drupal :: service ( 'entity_field.deleted_fields_repository' );
$fields = $deleted_fields_repository -> getFieldDefinitions ( $field_storage -> getUniqueStorageIdentifier ());
2014-09-20 02:45:52 +00:00
if ( count ( $fields ) > 0 ) {
2017-03-04 01:20:24 +00:00
throw new FieldException ( t ( 'Attempt to purge a field storage @field_name that still has fields.' , [ '@field_name' => $field_storage -> getName ()]));
2009-08-11 14:59:40 +00:00
}
2017-11-14 13:54:31 +00:00
$deleted_fields_repository -> removeFieldStorageDefinition ( $field_storage );
2009-08-11 14:59:40 +00:00
2013-09-01 06:20:08 +00:00
// Notify the storage layer.
2015-02-10 13:39:31 +00:00
\Drupal :: entityManager () -> getStorage ( $field_storage -> getTargetEntityTypeId ()) -> finalizePurge ( $field_storage );
2009-08-11 14:59:40 +00:00
// Invoke external hooks after the cache is cleared for API consistency.
2017-03-04 01:20:24 +00:00
\Drupal :: moduleHandler () -> invokeAll ( 'field_purge_field_storage' , [ $field_storage ]);
2009-08-11 14:59:40 +00:00
}
/**
* @ } End of " defgroup field_purge " .
*/