Skip to content

Commit

Permalink
add table.remove hook for soft/hard delete (Closes #1585)
Browse files Browse the repository at this point in the history
  • Loading branch information
wellingguzman committed Aug 27, 2017
1 parent 76e7262 commit 1f190e5
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 32 deletions.
6 changes: 4 additions & 2 deletions api/core/Directus/Bootstrap.php
Expand Up @@ -151,7 +151,8 @@ private static function app()
Twig::$twigTemplateDirs = $templatesPaths;

Twig::$twigExtensions = [
new DirectusTwigExtension()
new DirectusTwigExtension(),
new \Twig_Extension_Debug()
];

$app->container->singleton('hookEmitter', function () {
Expand Down Expand Up @@ -391,7 +392,8 @@ private static function zenddb()
$db = new Connection($dbConfig);
$db->connect();
} catch (\Exception $e) {
echo 'Database connection failed.';
echo $e->getMessage();
// echo 'Database connection failed.';
exit;
}

Expand Down
49 changes: 41 additions & 8 deletions api/core/Directus/Config/Config.php
Expand Up @@ -15,25 +15,58 @@ class Config extends Collection implements ConfigInterface
* @return array
*/
public function getPublishedStatuses($statusMapping = [])
{
$visibleStatus = $this->getStatuses('published', $statusMapping);

if (empty($visibleStatus) && defined('STATUS_ACTIVE_NUM')) {
$visibleStatus[] = STATUS_ACTIVE_NUM;
}

return $visibleStatus;
}

/**
* Get a list of hard-deleted statuses
*
* @param array $statusMapping
*
* @return array
*/
public function getDeletedStatuses($statusMapping = [])
{
$visibleStatus = $this->getStatuses('hard_delete', $statusMapping);

if (empty($visibleStatus) && defined('STATUS_DELETED_NUM')) {
$visibleStatus[] = STATUS_DELETED_NUM;
}

return $visibleStatus;
}

/**
* Get statuses list by the given type
*
* @param $type
* @param array $statusMapping
*
* @return array
*/
protected function getStatuses($type, $statusMapping = [])
{
if (empty($statusMapping)) {
$statusMapping = $this->get('statusMapping', []);
}

$visibleStatus = [];
$statuses = [];

foreach ($statusMapping as $value => $status) {
$isPublished = ArrayUtils::get($status, 'published', false);
$isPublished = ArrayUtils::get($status, $type, false);

if (is_array($status) && $isPublished) {
$visibleStatus[] = $value;
$statuses[] = $value;
}
}

if (empty($visibleStatus)) {
$visibleStatus[] = defined('STATUS_ACTIVE_NUM') ? STATUS_ACTIVE_NUM : 1;
}

return $visibleStatus;
return $statuses;
}
}
149 changes: 127 additions & 22 deletions api/core/Directus/Database/TableGateway/BaseTableGateway.php
Expand Up @@ -355,12 +355,6 @@ public function addOrUpdateRecordByArray(array $recordData, $tableName = null)
}

if ($rowExists) {
$recordData = $this->applyHook('table.update:before', $recordData, [
'tableName' => $tableName
]);

$recordData = $this->applyHook('table.update.' . $tableName . ':before', $recordData);

$Update = new Update($tableName);
$Update->set($recordData);
$Update->where([
Expand Down Expand Up @@ -825,14 +819,12 @@ protected function executeUpdate(Update $update)
$updateData = $updateState['set'];

try {
// @TODO: Move hook filters here
$this->runHook('table.update:before', [$updateTable, $updateData]);
$this->runHook('table.update.' . $updateTable . ':before', [$updateData]);
$isSoftDelete = $this->isSoftDelete($updateData);

$updateData = $this->runBeforeUpdateHooks($updateTable, $updateData, $isSoftDelete);
$update->set($updateData);
$result = parent::executeUpdate($update);
$this->runHook('table.update', [$updateTable, $updateData]);
$this->runHook('table.update:after', [$updateTable, $updateData]);
$this->runHook('table.update.' . $updateTable, [$updateData]);
$this->runHook('table.update.' . $updateTable . ':after', [$updateData]);
$this->runAfterUpdateHooks($updateTable, $updateData, $isSoftDelete);

return $result;
} catch (InvalidQueryException $e) {
Expand Down Expand Up @@ -868,15 +860,30 @@ protected function executeDelete(Delete $delete)

$deleteState = $delete->getRawState();
$deleteTable = $this->getRawTableNameFromQueryStateTable($deleteState['table']);
// NOTE: this is used to send the "delete" data to table.remove hook
// on update this hook pass the updated data array, on delete has nothing,
// a empty array is passed instead
// TODO: Add the ID of the record being deleted
$deleteData = [];

try {
$this->runHook('table.delete:before', [$deleteTable]);
$this->runHook('table.delete.' . $deleteTable . ':before');
$this->runHook('table.remove:before', [$deleteTable, $deleteData, 'soft' => false]);
$this->runHook('table.remove.' . $deleteTable . ':before', [$deleteData, 'soft' => false]);

$result = parent::executeDelete($delete);

$this->runHook('table.delete', [$deleteTable]);
$this->runHook('table.delete:after', [$deleteTable]);
$this->runHook('table.delete.' . $deleteTable);
$this->runHook('table.delete.' . $deleteTable . ':after');

$this->runHook('table.remove', [$deleteTable, $deleteData, 'soft' => false]);
$this->runHook('table.remove:after', [$deleteTable, $deleteData, 'soft' => false]);
$this->runHook('table.remove.' . $deleteTable, [$deleteData, 'soft' => false]);
$this->runHook('table.remove.' . $deleteTable . ':after', [$deleteData, 'soft' => false]);

return $result;
} catch (InvalidQueryException $e) {
if ('production' !== DIRECTUS_ENV) {
Expand All @@ -887,6 +894,31 @@ protected function executeDelete(Delete $delete)
}
}

/**
* Check whether the data will perform soft delete
*
* @param array $data
*
* @return bool
*/
protected function isSoftDelete(array $data)
{
$isSoftDelete = false;
$tableSchema = $this->getTableSchema();
$statusColumnName = $tableSchema->getStatusColumn();
$hasStatusColumnData = ArrayUtils::has($data, $statusColumnName);
$deletedValues = $this->getDeletedStatuses();

if ($hasStatusColumnData) {
$statusColumnObject = $tableSchema->getColumn($statusColumnName);
$deletedValues[] = $statusColumnObject->getOptions('delete_value') ?: STATUS_DELETED_NUM;
$statusValue = ArrayUtils::get($data, $this->getStatusColumnName());
$isSoftDelete = in_array($statusValue, $this->getDeletedStatuses());
}

return $isSoftDelete;
}

protected function getRawTableNameFromQueryStateTable($table)
{
if (is_string($table)) {
Expand Down Expand Up @@ -1184,7 +1216,7 @@ public function getColumnIdentifier($column, $table = null)
$platform = $this->getAdapter()->getPlatform();

// TODO: find a common place to share this code
// It is duplicated code in Builder.php
// It is a duplicated code from Builder.php
if (strpos($column, $platform->getIdentifierSeparator()) === false) {
$column = implode($platform->getIdentifierSeparator(), [$table, $column]);
}
Expand Down Expand Up @@ -1325,6 +1357,66 @@ public function applyHook($name, $data = null, array $attributes = [])
return $data;
}

/**
* Run before table update hooks and filters
*
* @param string $updateTable
* @param array $updateData
* @param bool $isSoftDelete
*
* @return array|\ArrayObject
*/
protected function runBeforeUpdateHooks($updateTable, $updateData, $isSoftDelete)
{
// Filters
$updateData = $this->applyHook('table.update:before', $updateData, [
'tableName' => $updateTable
]);
$updateData = $this->applyHook('table.update.' . $updateTable . ':before', $updateData);

// Hooks
$this->runHook('table.update:before', [$updateTable, $updateData]);
$this->runHook('table.update.' . $updateTable . ':before', [$updateData]);

if ($isSoftDelete === true) {
$updateData = $this->applyHook('table.remove:before', $updateData, [
'tableName' => $updateTable,
'soft' => true
]);

$updateData = $this->applyHook('table.remove.' . $updateTable . ':before', $updateData, [
'soft' => true
]);

$this->runHook('table.remove:before', [$updateTable, $updateData, 'soft' => true]);
$this->runHook('table.remove.' . $updateTable . ':before', [$updateData, 'soft' => true]);
}

return $updateData;
}

/**
* Run after table update hooks and filters
*
* @param string $updateTable
* @param string $updateData
* @param bool $isSoftDelete
*/
protected function runAfterUpdateHooks($updateTable, $updateData, $isSoftDelete)
{
$this->runHook('table.update', [$updateTable, $updateData]);
$this->runHook('table.update:after', [$updateTable, $updateData]);
$this->runHook('table.update.' . $updateTable, [$updateData]);
$this->runHook('table.update.' . $updateTable . ':after', [$updateData]);

if ($isSoftDelete === true) {
$this->runHook('table.remove', [$updateTable, $updateData, 'soft' => true]);
$this->runHook('table.remove.' . $updateTable, [$updateData, 'soft' => true]);
$this->runHook('table.remove:after', [$updateTable, $updateData, 'soft' => true]);
$this->runHook('table.remove.' . $updateTable . ':after', [$updateData, 'soft' => true]);
}
}

/**
* Gets Directus settings (from DB)
*
Expand All @@ -1345,19 +1437,32 @@ public function getSettings($key = null)

public function getPublishedStatuses()
{
$publishedStatuses = [];
return $this->getStatuses('published');
}

public function getDeletedStatuses()
{
return $this->getStatuses('deleted');
}

protected function getStatuses($type)
{
$statuses = [];

if (static::$container && TableSchema::hasStatusColumn($this->table, true)) {
$config = static::$container->get('config');
$statusMapping = $this->getTableSchema()->getStatusMapping();

if ($statusMapping) {
$publishedStatuses = $config->getPublishedStatuses($statusMapping);
} else {
$publishedStatuses = $config->getPublishedStatuses();
$statusMapping = $this->getTableSchema()->getStatusMapping() ?: [];

switch ($type) {
case 'published':
$statuses = $config->getPublishedStatuses($statusMapping);
break;
case 'deleted':
$statuses = $config->getDeletedStatuses($statusMapping);
break;
}
}

return $publishedStatuses;
return $statuses;
}
}

0 comments on commit 1f190e5

Please sign in to comment.