Skip to content

Commit

Permalink
Add FluentVersioned Extension for Versioned collector
Browse files Browse the repository at this point in the history
  • Loading branch information
brettt89 committed Jul 19, 2021
1 parent 67ae35c commit f3eff9a
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 9 deletions.
23 changes: 14 additions & 9 deletions src/Collectors/VersionedCollector.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ protected function getRecordsForDeletion(array $classes): array
$recordLimit
);

$this->extend('getRecordsForDeletionQuery', $query);
$this->extend('modifyGetRecordsQuery', $query, $class);

$results = $query->execute();

Expand All @@ -213,6 +213,8 @@ protected function getRecordsForDeletion(array $classes): array
'id' => (int) $result['RecordID'],
];

$this->extend('modifyRecordsData', $class, $item, $result);

$data[] = $item;
}

Expand Down Expand Up @@ -283,7 +285,7 @@ protected function getVersionsForDeletion(array $records): array
]
);

$this->extend('getVersionsForDeletionQuery', $query);
$this->extend('modifyGetVersionsQuery', $query, $baseClass, $item);

$results = $query->execute();

Expand Down Expand Up @@ -332,7 +334,8 @@ protected function deleteVersionsQuery(string $class, int $recordId, array $vers
return 0;
}

$baseTables = $this->getTablesListForClass($class);
$tables = $this->getTablesListForClass($class);
$baseTables = $tables['base'];

if (count($baseTables) === 0) {
return 0;
Expand Down Expand Up @@ -372,7 +375,7 @@ protected function deleteVersionsQuery(string $class, int $recordId, array $vers
);
}

$this->extend('deleteVersionsQuery', $query);
$this->extend('modifyDeleteVersionsQuery', $query, $class, $item);

return $query;
}
Expand All @@ -383,15 +386,17 @@ protected function deleteVersionsQuery(string $class, int $recordId, array $vers
* @param string $class
* @return array[]
*/
protected function getTablesListForClass(string $class): array
public function getTablesListForClass(string $class): array
{
$classes = ClassInfo::ancestry($class, true);
$tables = [];
$tables = [ 'base' => [] ];

foreach ($classes as $currentClass) {
$tables[] = $this->getTableNameForClass($currentClass);
$tables['base'][] = $this->getVersionTableName($this->getTableNameForClass($currentClass));
}

$this->extend('modifyTablesListForClass', $class, $tables);

return $tables;
}

Expand All @@ -401,7 +406,7 @@ protected function getTablesListForClass(string $class): array
* @param string $class
* @return string
*/
protected function getTableNameForClass(string $class): string
public function getTableNameForClass(string $class): string
{
$table = DataObject::singleton($class)
->config()
Expand All @@ -417,7 +422,7 @@ protected function getTableNameForClass(string $class): string
* @param string $table
* @return string
*/
protected function getVersionTableName(string $table): string
public function getVersionTableName(string $table): string
{
return $table . '_Versions';
}
Expand Down
197 changes: 197 additions & 0 deletions src/Extensions/FluentVersionedCollectorExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
<?php

namespace SilverStripe\GarbageCollector\Extensions;

use SilverStripe\Core\Extension;
use SilverStripe\ORM\Queries\SQLDelete;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\GarbageCollector\Collectors\VersionedCollector;
use TractorCow\Fluent\Extension\FluentExtension;
use TractorCow\Fluent\Extension\FluentVersionedExtension;

class FluentVersionedCollectorExtension extends Extension
{
/**
* Modify getRecordsQuery to join with Localised tables
*
* @param SQLSelect $query
* @param string $class
*/
public function modifyGetRecordsQuery(SQLSelect $query, string $class): void
{
if ($this->isLocalised($class)) {
$mainTable = $this->owner->getTableNameForClass($class);
$baseTable = sprintf('"%s"', $this->owner->getVersionTableName($mainTable));

$localisedTableRaw = $this->getVersionLocalisedTableName($mainTable);
$localisedTable = sprintf('"%s"', $localisedTableRaw);
$query
// Join through to the localised table
// Version numbers map one to one
->addInnerJoin(
$localisedTableRaw,
sprintf(
'%1$s."RecordID" = %2$s."RecordID" AND %1$s."Version" = %2$s."Version"',
$baseTable,
$localisedTable
)
)
->addSelect([
// We will need the locale information as well later on
$localisedTable . '."Locale"',
])
->addGroupBy([
// Grouping has to be extended to locales
// as we want to keep the minimum number of versions per locale
$localisedTable . '."Locale"',
])
->addOrderBy([
// Extends consistent ordering to locales
$localisedTable . '."Locale"',
]);
}
}

/**
* Modify getRecords return data to include Locale data
*
* @param string $class Classname of records being modified
* @param array $item Item details for returning
* @param array $result Query result data
*/
public function modifyRecordsData(string $class, array $item, array $result): void
{
if ($this->isLocalised($class)) {
// Add additional locale data for localised records
$item['locale'] = $result['Locale'];
}
}

/**
* Modify getRecords return data to include Locale data
*
* @param SQLSelect $query Select query for Versions records
* @param string $class Classname of records
* @param array $item Item details
*/
public function modifyGetVersionsQuery(SQLSelect $query, string $class, array $item): void
{
$locale = array_key_exists('locale', $item)
? $item['locale']
: null;

$mainTable = $this->owner->getTableNameForClass($class);
$baseTable = sprintf('"%s"', $this->owner->getVersionTableName($mainTable));

if ($locale) {
$localisedTableRaw = $this->getVersionLocalisedTableName($mainTable);
$localisedTable = sprintf('"%s"', $localisedTableRaw);
$query
// Join through to the localised table
// Version numbers map one to one
->addInnerJoin(
$localisedTableRaw,
sprintf(
'%1$s."RecordID" = %2$s."RecordID" AND %1$s."Version" = %2$s."Version"',
$baseTable,
$localisedTable
)
)
->addWhere([
// Narrow down the search to specific locale, this ensures that we keep minimum
// required versions per locale
$localisedTable . '."Locale"' => $locale,
]);
}

}

/**
* Modify getRecords return data to include Locale data
*
* @param SQLDelete $query Delete query for Version records
* @param string $class Classname of records
* @param array $item Item details
*/
public function modifyDeleteVersionsQuery(SQLDelete $query, string $class, array $item): void
{
$tables = $this->owner->getTablesListForClass($class);
$baseTables = $tables['base'];
$localisedTables = $tables['localised'];

// Add localised table to the join and deletion
if (count($localisedTables) > 0) {
$localisedTablesRaw = $localisedTables;

array_walk($localisedTables, static function (&$item): void {
$item = sprintf('"%s"', $item);
});

// Register localised tables for deletion so we delete records from it
$query->addDelete($localisedTables);

foreach ($localisedTablesRaw as $table) {
$query->addLeftJoin(
$table,
sprintf(
'%1$s."RecordID" = "%2$s"."RecordID" AND %1$s."Version" = "%2$s"."Version"',
$baseTable,
$table
)
);
}
}
}

/**
* Modify getRecords return data to include Locale data
*
* @param string $class Classname of records
* @param array $tables Tables list data for class
*/
public function modifyTablesListForClass(string $class, array $tables)
{
// Include localised tables if needed
if ($singleton->hasExtension(FluentVersionedExtension::class)) {
$localisedTables = [];
$localisedDataTables = array_keys($singleton->getLocalisedTables());

foreach ($tables as $table) {
if (!in_array($table, $localisedDataTables)) {
// Skip any tables that do not contain localised data
continue;
}

$localisedTables[] = $this->getVersionLocalisedTableName($table);
}

// Add localised Table data to Tables List
$tables['localised'] = $localisedTables;
}
}

/**
* Check if Class is Localised (Has Fluent extension and Localised fields)
*
* @param string $class
* @return bool
*/
protected function isLocalised(string $class): bool
{
/** @var DataObject|FluentExtension $singleton */
$singleton = DataObject::singleton($class);
return $singleton->hasExtension(FluentVersionedExtension::class)
&& count($singleton->getLocalisedFields()) > 0;
}

/**
* Determine the name of localised version table
*
* @param string $table
* @return string
*/
protected function getVersionLocalisedTableName(string $table): string
{
return $table . '_Localised_Versions';
}
}
41 changes: 41 additions & 0 deletions tests/php/Extensions/FluentVersionedCollectorExtensionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace SilverStripe\GarbageCollector\Tests\Extensions;

use SilverStripe\Dev\SapphireTest;
use SilverStripe\GarbageCollector\Tests\Ship;
use SilverStripe\Versioned\Versioned;
use TractorCow\Fluent\Extension\FluentVersionedExtension;

class FluentVersionedCollectorExtensionTest extends SapphireTest
{
/**
* @var string
*/
protected static $fixture_file = [
'tests/php/Models.yml',
'tests/php/Fluent.yml'
];

/**
* @var string[]
*/
protected static $extra_dataobjects = [
Ship::class,
];

/**
* @var string[][]
*/
protected static $required_extensions = [
Ship::class => [
Versioned::class,
FluentVersionedExtension::class
],
];

public function testModifyRecordsExtension()
{

}
}
14 changes: 14 additions & 0 deletions tests/php/Fluent.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
TractorCow\Fluent\Model\Locale:
nz:
Locale: en_001
APIURLSegment: en
Title: 'International'
URLSegment: int
AlacrityMarket: en
us:
Locale: en_US
Title: 'United States'
URLSegment: us
AlacrityMarket: us
Fallbacks:
- =>TractorCow\Fluent\Model\Locale.nz

0 comments on commit f3eff9a

Please sign in to comment.