Commit 1648a479 authored by alexpott's avatar alexpott

Issue #1893772 by chx, fubhy, slashrsm: Move entity-type specific storage...

Issue #1893772 by chx, fubhy, slashrsm: Move entity-type specific storage logic into entity classes.
parent d9f7b3a3
......@@ -278,6 +278,7 @@ protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
*/
public function create(array $values) {
$class = $this->entityInfo['class'];
$class::preCreate($this, $values);
// Set default language to site default if not provided.
$values += array('langcode' => language_default()->langcode);
......@@ -292,6 +293,7 @@ public function create(array $values) {
$uuid = new Uuid();
$entity->{$this->uuidKey} = $uuid->generate();
}
$entity->postCreate($this);
// Modules might need to add or change the data initially held by the new
// entity object, for instance to fill-in default values.
......@@ -314,7 +316,8 @@ public function delete(array $entities) {
return;
}
$this->preDelete($entities);
$entity_class = $this->entityInfo['class'];
$entity_class::preDelete($this, $entities);
foreach ($entities as $id => $entity) {
$this->invokeHook('predelete', $entity);
}
......@@ -324,7 +327,7 @@ public function delete(array $entities) {
$config->delete();
}
$this->postDelete($entities);
$entity_class::postDelete($this, $entities);
foreach ($entities as $id => $entity) {
$this->invokeHook('delete', $entity);
}
......@@ -367,7 +370,7 @@ public function save(EntityInterface $entity) {
$this->configFactory->rename($prefix . $id, $prefix . $entity->id());
}
$this->preSave($entity);
$entity->preSave($this);
$this->invokeHook('presave', $entity);
// Retrieve the desired properties and set them in config.
......@@ -378,7 +381,7 @@ public function save(EntityInterface $entity) {
if (!$is_new) {
$return = SAVED_UPDATED;
$config->save();
$this->postSave($entity, TRUE);
$entity->postSave($this, TRUE);
$this->invokeHook('update', $entity);
// Immediately update the original ID.
......@@ -388,7 +391,7 @@ public function save(EntityInterface $entity) {
$return = SAVED_NEW;
$config->save();
$entity->enforceIsNew(FALSE);
$this->postSave($entity, FALSE);
$entity->postSave($this, FALSE);
$this->invokeHook('insert', $entity);
}
......@@ -397,45 +400,6 @@ public function save(EntityInterface $entity) {
return $return;
}
/**
* Acts on an entity before the presave hook is invoked.
*
* Used before the entity is saved and before invoking the presave hook.
*/
protected function preSave(EntityInterface $entity) {
}
/**
* Acts on a saved entity before the insert or update hook is invoked.
*
* Used after the entity is saved, but before invoking the insert or update
* hook.
*
* @param EntityInterface $entity
* The entity to act on.
* @param $update
* (bool) TRUE if the entity has been updated, or FALSE if it has been
* inserted.
*/
protected function postSave(EntityInterface $entity, $update) {
}
/**
* Acts on entities before they are deleted.
*
* Used before the entities are deleted and before invoking the delete hook.
*/
protected function preDelete($entities) {
}
/**
* Acts on deleted entities before the delete hook is invoked.
*
* Used after the entities are deleted but before invoking the delete hook.
*/
protected function postDelete($entities) {
}
/**
* Implements Drupal\Core\Entity\EntityStorageControllerInterface::getFieldDefinitions().
*/
......
......@@ -370,15 +370,17 @@ protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
* Implements \Drupal\Core\Entity\EntityStorageControllerInterface::create().
*/
public function create(array $values) {
$class = $this->entityInfo['class'];
$entity_class = $this->entityInfo['class'];
$entity_class::preCreate($this, $values);
$entity = new $class($values, $this->entityType);
$entity = new $entity_class($values, $this->entityType);
// Assign a new UUID if there is none yet.
if ($this->uuidKey && !isset($entity->{$this->uuidKey})) {
$uuid = new Uuid();
$entity->{$this->uuidKey} = $uuid->generate();
}
$entity->postCreate($this);
// Modules might need to add or change the data initially held by the new
// entity object, for instance to fill-in default values.
......@@ -398,7 +400,8 @@ public function delete(array $entities) {
$transaction = $this->database->startTransaction();
try {
$this->preDelete($entities);
$entity_class = $this->entityInfo['class'];
$entity_class::preDelete($this, $entities);
foreach ($entities as $id => $entity) {
$this->invokeHook('predelete', $entity);
}
......@@ -417,7 +420,7 @@ public function delete(array $entities) {
// Reset the cache as soon as the changes have been applied.
$this->resetCache($ids);
$this->postDelete($entities);
$entity_class::postDelete($this, $entities);
foreach ($entities as $id => $entity) {
$this->invokeHook('delete', $entity);
}
......@@ -442,7 +445,7 @@ public function save(EntityInterface $entity) {
$entity->original = entity_load_unchanged($this->entityType, $entity->id());
}
$this->preSave($entity);
$entity->preSave($this);
$this->invokeHook('presave', $entity);
if (!$entity->isNew()) {
......@@ -458,7 +461,7 @@ public function save(EntityInterface $entity) {
$this->saveRevision($entity);
}
$this->resetCache(array($entity->id()));
$this->postSave($entity, TRUE);
$entity->postSave($this, TRUE);
$this->invokeHook('update', $entity);
}
else {
......@@ -470,7 +473,7 @@ public function save(EntityInterface $entity) {
$this->resetCache(array());
$entity->enforceIsNew(FALSE);
$this->postSave($entity, FALSE);
$entity->postSave($this, FALSE);
$this->invokeHook('insert', $entity);
}
......@@ -507,7 +510,7 @@ protected function saveRevision(EntityInterface $entity) {
// Cast to object as preSaveRevision() expects one to be compatible with the
// upcoming NG storage controller.
$record = (object) $record;
$this->preSaveRevision($record, $entity);
$entity->preSaveRevision($this, $record);
$record = (array) $record;
if ($entity->isNewRevision()) {
......@@ -527,49 +530,6 @@ protected function saveRevision(EntityInterface $entity) {
$entity->{$this->revisionKey} = $record[$this->revisionKey];
}
/**
* Acts on an entity before the presave hook is invoked.
*
* Used before the entity is saved and before invoking the presave hook.
*/
protected function preSave(EntityInterface $entity) { }
/**
* Acts on a saved entity before the insert or update hook is invoked.
*
* Used after the entity is saved, but before invoking the insert or update
* hook.
*
* @param $update
* (bool) TRUE if the entity has been updated, or FALSE if it has been
* inserted.
*/
protected function postSave(EntityInterface $entity, $update) { }
/**
* Acts on entities before they are deleted.
*
* Used before the entities are deleted and before invoking the delete hook.
*/
protected function preDelete($entities) { }
/**
* Acts on deleted entities before the delete hook is invoked.
*
* Used after the entities are deleted but before invoking the delete hook.
*/
protected function postDelete($entities) { }
/**
* Act on a revision before being saved.
*
* @param \stdClass $record
* The revision object.
* @param Drupal\Core\Entity\EntityInterface $entity
* The entity object.
*/
protected function preSaveRevision(\stdClass $record, EntityInterface $entity) { }
/**
* Invokes a hook on behalf of the entity.
*
......
......@@ -98,6 +98,9 @@ public function __construct($entity_type, array $entity_info, Connection $databa
* A new entity object.
*/
public function create(array $values) {
$entity_class = $this->entityClass;
$entity_class::preCreate($this, $values);
// We have to determine the bundle first.
$bundle = FALSE;
if ($this->bundleKey) {
......@@ -118,6 +121,7 @@ public function create(array $values) {
$uuid = new Uuid();
$entity->{$this->uuidKey} = $uuid->generate();
}
$entity->postCreate($this);
// Modules might need to add or change the data initially held by the new
// entity object, for instance to fill-in default values.
......@@ -354,7 +358,7 @@ public function save(EntityInterface $entity) {
$entity->original = entity_load_unchanged($this->entityType, $entity->id());
}
$this->preSave($entity);
$entity->preSave($this);
$this->invokeHook('presave', $entity);
// Create the storage record to be saved.
......@@ -376,7 +380,7 @@ public function save(EntityInterface $entity) {
$this->savePropertyData($entity);
}
$this->resetCache(array($entity->id()));
$this->postSave($entity, TRUE);
$entity->postSave($this, TRUE);
$this->invokeHook('update', $entity);
}
else {
......@@ -394,7 +398,7 @@ public function save(EntityInterface $entity) {
$this->resetCache(array());
$entity->enforceIsNew(FALSE);
$this->postSave($entity, FALSE);
$entity->postSave($this, FALSE);
$this->invokeHook('insert', $entity);
}
......@@ -445,7 +449,7 @@ protected function saveRevision(EntityInterface $entity) {
$record->{$this->revisionKey} = NULL;
}
$this->preSaveRevision($record, $entity);
$entity->preSaveRevision($this, $record);
if ($entity->isNewRevision()) {
drupal_write_record($this->revisionTable, $record);
......@@ -596,12 +600,14 @@ public function delete(array $entities) {
$transaction = $this->database->startTransaction();
try {
$entity_class = $this->entityClass;
$entity_class::preDelete($this, $entities);
// Ensure we are dealing with the actual entities.
foreach ($entities as $id => $entity) {
$entities[$id] = $entity->getNGEntity();
}
$this->preDelete($entities);
foreach ($entities as $id => $entity) {
$this->invokeHook('predelete', $entity);
}
......@@ -626,7 +632,7 @@ public function delete(array $entities) {
// Reset the cache as soon as the changes have been applied.
$this->resetCache($ids);
$this->postDelete($entities);
$entity_class::postDelete($this, $entities);
foreach ($entities as $id => $entity) {
$this->invokeHook('delete', $entity);
}
......
......@@ -531,4 +531,52 @@ public function isTranslatable() {
return !empty($bundles[$this->bundle()]['translatable']);
}
/**
* {@inheritdoc}
*/
public function preSave(EntityStorageControllerInterface $storage_controller) {
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
}
/**
* {@inheritdoc}
*/
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
}
/**
* {@inheritdoc}
*/
public function postCreate(EntityStorageControllerInterface $storage_controller) {
}
/**
* {@inheritdoc}
*/
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
}
/**
* {@inheritdoc}
*/
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
}
/**
* {@inheritdoc}
*/
public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities) {
}
/**
* {@inheritdoc}
*/
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
}
}
......@@ -533,4 +533,53 @@ public function onChange($property_name) {
public function isTranslatable() {
return $this->decorated->isTranslatable();
}
/**
* {@inheritdoc}
*/
public function preSave(EntityStorageControllerInterface $storage_controller) {
$this->decorated->preSave($storage_controller);
}
/**
* {@inheritdoc}
*/
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record) {
$this->decorated->preSave($storage_controller, $record);
}
/**
* {@inheritdoc}
*/
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
$this->decorated->postSave($storage_controller, $update);
}
/**
* {@inheritdoc}
*/
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values) {
}
public function postCreate(EntityStorageControllerInterface $storage_controller) {
$this->decorated->postCreate($storage_controller);
}
/**
* {@inheritdoc}
*/
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
}
/**
* {@inheritdoc}
*/
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities) {
}
/**
* {@inheritdoc}
*/
public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities) {
}
}
......@@ -162,6 +162,94 @@ public function save();
*/
public function delete();
/**
* Acts on an entity before the presave hook is invoked.
*
* Used before the entity is saved and before invoking the presave hook.
*
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
* The entity storage controller object.
*/
public function preSave(EntityStorageControllerInterface $storage_controller);
/**
* Acts on a revision before it gets saved.
*
* @param EntityStorageControllerInterface $storage_controller
* The entity storage controller object.
* @param \stdClass $record
* The revision object.
*/
public function preSaveRevision(EntityStorageControllerInterface $storage_controller, \stdClass $record);
/**
* Acts on a saved entity before the insert or update hook is invoked.
*
* Used after the entity is saved, but before invoking the insert or update
* hook.
*
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
* The entity storage controller object.
* @param bool $update
* TRUE if the entity has been updated, or FALSE if it has been inserted.
*/
public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE);
/**
* Changes the values of an entity before it is created.
*
* Load defaults for example.
*
* @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage_controller
* The entity storage controller object.
* @param array $values
* An array of values to set, keyed by property name. If the entity type has
* bundles the bundle key has to be specified.
*/
public static function preCreate(EntityStorageControllerInterface $storage_controller, array &$values);
/**
* Acts on an entity after it is created but before hooks are invoked.
*
* @param EntityStorageControllerInterface $storage_controller
* The entity storage controller object.
*/
public function postCreate(EntityStorageControllerInterface $storage_controller);
/**
* Acts on entities before they are deleted and before hooks are invoked.
*
* Used before the entities are deleted and before invoking the delete hook.
*
* @param EntityStorageControllerInterface $storage_controller
* The entity storage controller object.
* @param array $entities
* An array of entities.
*/
public static function preDelete(EntityStorageControllerInterface $storage_controller, array $entities);
/**
* Acts on deleted entities before the delete hook is invoked.
*
* Used after the entities are deleted but before invoking the delete hook.
*
* @param EntityStorageControllerInterface $storage_controller
* The entity storage controller object.
* @param array $entities
* An array of entities.
*/
public static function postDelete(EntityStorageControllerInterface $storage_controller, array $entities);
/**
* Acts on loaded entities before the load hook is invoked.
*
* @param EntityStorageControllerInterface $storage_controller
* The entity storage controller object.
* @param array $entities
* An array of entities.
*/
public static function postLoad(EntityStorageControllerInterface $storage_controller, array $entities);
/**
* Creates a duplicate of the entity.
*
......
......@@ -33,7 +33,7 @@ public function resetCache(array $ids = NULL);
* Loads one or more entities.
*
* @param $ids
* An array of entity IDs, or FALSE to load all entities.
* An array of entity IDs, or NULL to load all entities.
*
* @return
* An array of entity objects indexed by their ids.
......
......@@ -8,6 +8,7 @@
namespace Drupal\aggregator;
use Drupal\Core\Entity\DatabaseStorageControllerNG;
use Drupal\aggregator\Plugin\Core\Entity\Feed;
use Drupal\Core\Entity\EntityInterface;
/**
......@@ -16,101 +17,14 @@
* This extends the Drupal\Core\Entity\DatabaseStorageController class, adding
* required special handling for feed entities.
*/
class FeedStorageController extends DatabaseStorageControllerNG {
/**
* Overrides Drupal\Core\Entity\DataBaseStorageController::create().
*/
public function create(array $values) {
$values += array(
'link' => '',
'description' => '',
'image' => '',
);
return parent::create($values);
}
class FeedStorageController extends DatabaseStorageControllerNG implements FeedStorageControllerInterface {
/**
* Overrides Drupal\Core\Entity\DataBaseStorageController::attachLoad().
*/
protected function attachLoad(&$queried_entities, $load_revision = FALSE) {
parent::attachLoad($queried_entities, $load_revision);
foreach ($queried_entities as $item) {
$item->categories = db_query('SELECT c.cid, c.title FROM {aggregator_category} c JOIN {aggregator_category_feed} f ON c.cid = f.cid AND f.fid = :fid ORDER BY title', array(':fid' => $item->id()))->fetchAllKeyed();
}
}
/**
* Overrides Drupal\Core\Entity\DataBaseStorageController::preDelete().
*/
protected function preDelete($entities) {
parent::preDelete($entities);
// Invalidate the block cache to update aggregator feed-based derivatives.
if (module_exists('block')) {
\Drupal::service('plugin.manager.block')->clearCachedDefinitions();
}
foreach ($entities as $entity) {
// Notify processors to remove stored items.
$manager = \Drupal::service('plugin.manager.aggregator.processor');
foreach ($manager->getDefinitions() as $id => $definition) {
$manager->createInstance($id)->remove($entity);
}
}
}
/**
* Overrides Drupal\Core\Entity\DataBaseStorageController::postDelete().
*/
protected function postDelete($entities) {
parent::postDelete($entities);
foreach ($entities as $entity) {
// Make sure there is no active block for this feed.
$block_configs = config_get_storage_names_with_prefix('plugin.core.block');
foreach ($block_configs as $config_id) {
$config = config($config_id);
if ($config->get('id') == 'aggregator_feed_block:' . $entity->id()) {
$config->delete();
}
}
}
}
/**
* Overrides Drupal\Core\Entity\DataBaseStorageController::preSave().
*/
protected function preSave(EntityInterface $entity) {
parent::preSave($entity);
// Invalidate the block cache to update aggregator feed-based derivatives.
if (module_exists('block')) {
drupal_container()->get('plugin.manager.block')->clearCachedDefinitions();
}
// An existing feed is being modified, delete the category listings.
db_delete('aggregator_category_feed')
->condition('fid', $entity->id())
->execute();
}
/**
* Overrides Drupal\Core\Entity\DataBaseStorageController::postSave().
*/
protected function postSave(EntityInterface $entity, $update) {
parent::postSave($entity, $update);
if (!empty($entity->categories)) {
foreach ($entity->categories as $cid => $value) {
if ($value) {
db_insert('aggregator_category_feed')
->fields(array(
'fid' => $entity->id(),
'cid' => $cid,
))
->execute();
}
}
}
$this->loadCategories($queried_entities);
}
/**
......@@ -191,4 +105,39 @@ public function baseFieldDefinitions() {
return $fields;
}
/**
* {@inheritdoc}
*/
public function loadCategories(array $feeds) {
foreach ($feeds as $feed) {
$feed->categories = $this->database->query('SELECT c.cid, c.title FROM {aggregator_category} c JOIN {aggregator_category_feed} f ON c.cid = f.cid AND f.fid = :fid ORDER BY title', array(':fid' => $feed->id()))->fetchAllKeyed();
}
}
/**
* {@inheritdoc}
*/
public function saveCategories(Feed $feed, array $categories) {
foreach ($categories as $cid => $value) {
if ($value) {
$this->database->insert('aggregator_category_feed')
->fields(array(
'fid' => $feed->id(),
'cid' => $cid,
))
->execute();
}
}
}
/**
* {@inheritdoc}
*/
public function deleteCategories(array $feeds) {
// An existing feed is being modified, delete the category listings.
$this->database->delete('aggregator_category_feed')
->condition('fid', array_keys($feeds))
->execute();
}
}