View source
<?php
namespace Drupal\salesforce_mapping\Commands;
use Drupal\Core\Config\ConfigFactory;
use Drupal\Core\Database\Connection;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\salesforce\Rest\RestClient;
use Drupal\salesforce\SelectQuery;
use Drush\Exceptions\UserAbortException;
use Symfony\Component\Console\Input\Input;
use Symfony\Component\Console\Output\Output;
class SalesforceMappingCommands extends SalesforceMappingCommandsBase {
protected $salesforceConfig;
protected $database;
public function __construct(RestClient $client, EntityTypeManagerInterface $etm, ConfigFactory $configFactory, Connection $database) {
parent::__construct($client, $etm);
$this->database = $database;
$this->salesforceConfig = $configFactory
->get('salesforce.settings');
}
public function interactPrune(Input $input, Output $output) {
if ($input
->getArgument('limit')) {
return;
}
$config_limit = $this->salesforceConfig
->get('limit_mapped_object_revisions');
while (TRUE) {
if (!($limit = $this
->io()
->ask('Enter a revision limit (integer). All revisions beyond this limit will be deleted, oldest first', $config_limit))) {
throw new UserAbortException();
}
elseif ($limit > 0) {
$input
->setArgument('limit', $limit);
return;
}
else {
$this
->logger()
->error('A positive integer limit is required.');
}
}
}
public function pruneRevisions($limit) {
$revision_table = $this->etm
->getDefinition('salesforce_mapped_object')
->getRevisionTable();
$ids = $this->database
->select($revision_table, 'r')
->fields('r', [
'id',
])
->having('COUNT(r.id) > ' . $limit)
->groupBy('r.id')
->execute()
->fetchCol();
if (empty($ids)) {
$this
->logger()
->warning(dt("No Mapped Objects with more than !limit revision(s). No action taken.", [
'!limit' => $limit,
]));
return;
}
$this
->logger()
->info(dt('Found !count mapped objects with excessive revisions. Will prune to revision(s) each. This may take a while.', [
'!count' => count($ids),
'!limit' => $limit,
]));
$total = count($ids);
$i = 0;
$buckets = ceil($total / 20);
if ($buckets == 0) {
$buckets = 1;
}
foreach ($ids as $id) {
if ($i++ % $buckets == 0) {
$this
->logger()
->info(dt("Pruned !i of !total records.", [
'!i' => $i,
'!total' => $total,
]));
}
$mapped_object = $this->mappedObjectStorage
->load($id);
$mapped_object
->pruneRevisions($this->mappedObjectStorage);
}
}
public function interactPurgeDrupal(Input $input, Output $output) {
return $this
->interactMapping($input, $output, 'Choose a Salesforce mapping', 'Purge All');
}
public function interactPurgeSalesforce(Input $input, Output $output) {
return $this
->interactMapping($input, $output, 'Choose a Salesforce mapping', 'Purge All');
}
public function interactPurgeMapping(Input $input, Output $output) {
return $this
->interactMapping($input, $output, 'Choose a Salesforce mapping', 'Purge All');
}
public function interactPurgeAll(Input $input, Output $output) {
return $this
->interactMapping($input, $output, 'Choose a Salesforce mapping', 'Purge All');
}
public function purgeDrupal($name) {
$mapped_obj_table = $this->etm
->getDefinition('salesforce_mapped_object')
->getBaseTable();
$query = $this->database
->select($mapped_obj_table, 'm')
->fields('m', [
'drupal_entity__target_type',
])
->distinct();
if ($name && strtoupper($name) != 'ALL') {
$query
->condition('salesforce_mapping', $name);
}
$entity_type_ids = $query
->execute()
->fetchCol();
if (empty($entity_type_ids)) {
$this
->logger()
->info('No orphaned mapped objects found by Drupal entities.');
return;
}
foreach ($entity_type_ids as $et_id) {
$query = $this->database
->select($mapped_obj_table, 'm')
->fields('m', [
'id',
]);
$query
->condition('drupal_entity__target_type', $et_id);
$entity_type = $this->etm
->getDefinition($et_id);
if ($entity_type) {
$id_key = $entity_type
->getKey('id');
$query
->addJoin("LEFT", $entity_type
->getBaseTable(), 'et', "et.{$id_key} = m.drupal_entity__target_id_int");
$query
->isNull("et.{$id_key}");
}
$mapped_obj_ids = $query
->execute()
->fetchCol();
if (empty($mapped_obj_ids)) {
$this
->logger()
->info('No orphaned mapped objects found for ' . $et_id . '.');
continue;
}
$this
->purgeConfirmAndDelete($mapped_obj_ids, 'entity type: ' . $et_id);
}
}
protected function purgeConfirmAndDelete(array $object_ids, $extra = '') {
if (empty($object_ids)) {
return;
}
$message = 'Delete ' . count($object_ids) . ' orphaned mapped objects';
if ($extra) {
$message .= ' for ' . $extra;
}
$message .= '?';
if (!$this
->io()
->confirm($message)) {
return;
}
$mapped_objs = $this->mappedObjectStorage
->loadMultiple($object_ids);
$this->mappedObjectStorage
->delete($mapped_objs);
}
protected function objectTypesByPrefix() {
$ret = [];
$describe = $this->client
->objects();
foreach ($describe as $object) {
$ret[$object['keyPrefix']] = $object;
}
return $ret;
}
public function purgeSalesforce($name) {
$object_types = $this
->objectTypesByPrefix();
$mapped_obj_table = $this->etm
->getDefinition('salesforce_mapped_object')
->getBaseTable();
$query = $this->database
->select($mapped_obj_table, 'm');
$query
->addExpression('distinct substr(salesforce_id, 1, 3)');
if ($name && strtoupper($name) != 'ALL') {
$query
->condition('salesforce_mapping', $name);
}
$sfid_prefixes = $query
->execute()
->fetchCol();
foreach ($sfid_prefixes as $prefix) {
if (empty($object_types[$prefix]['name'])) {
$query = $this->database
->select($mapped_obj_table, 'm')
->fields('m', [
'salesforce_id',
'id',
]);
$query
->condition('salesforce_id', $prefix . '%', 'LIKE');
$mapped_obj_ids = $query
->execute()
->fetchAllKeyed();
if (empty($mapped_obj_ids)) {
continue;
}
$this
->logger()
->warning(dt('Unknown object type for Salesforce ID prefix !prefix', [
'!prefix' => $prefix,
]));
$this
->purgeConfirmAndDelete($mapped_obj_ids, 'prefix ' . $prefix);
continue;
}
$query = $this->database
->select($mapped_obj_table, 'm')
->fields('m', [
'salesforce_id',
'id',
]);
if ($name && strtoupper($name) != 'ALL') {
$query
->condition('salesforce_mapping', $name);
}
else {
$query
->condition('salesforce_id', $prefix . '%', 'LIKE');
}
$sfids = $query
->execute()
->fetchAllKeyed();
$to_delete = $sfids;
foreach (array_chunk($sfids, 200, TRUE) as $chunk) {
$soql_query = new SelectQuery($object_types[$prefix]['name']);
$soql_query->fields[] = 'Id';
$soql_query
->addCondition('Id', array_keys($chunk));
$results = $this->client
->query($soql_query);
foreach ($results
->records() as $record) {
unset($to_delete[(string) $record
->id()]);
}
}
if (empty($to_delete)) {
$this
->logger()
->info(dt('No orphaned mapped objects found for SObject type !type', [
'!type' => $object_types[$prefix]['name'],
]));
continue;
}
$this
->purgeConfirmAndDelete(array_values($to_delete), 'SObject type *' . $object_types[$prefix]['name'] . '*');
}
}
public function purgeMapping($name) {
$mapped_obj_table = $this->etm
->getDefinition('salesforce_mapped_object')
->getBaseTable();
$query = $this->database
->select($mapped_obj_table, 'm')
->fields('m', [
'salesforce_mapping',
])
->distinct();
if ($name && strtoupper($name) != 'ALL') {
$query
->condition('salesforce_mapping', $name);
}
$mapping_ids = $query
->execute()
->fetchCol();
if (empty($entity_type_ids)) {
$this
->logger()
->info('No orphaned mapped objects found by mapping.');
return;
}
foreach ($mapping_ids as $mapping_id) {
$mapping = $this->mappingStorage
->load($mapping_id);
if ($mapping) {
continue;
}
$query = $this->database
->select($mapped_obj_table, 'm')
->fields('m', [
'id',
]);
$query
->condition('salesforce_mapping', $mapping_id);
$mapped_obj_ids = $query
->distinct()
->execute()
->fetchCol();
$this
->purgeConfirmAndDelete($mapped_obj_ids, 'missing mapping: ' . $mapping_id);
}
}
public function purgeAll($name) {
$this
->purgeDrupal($name);
$this
->purgeSalesforce($name);
$this
->purgeMapping($name);
}
}