Skip to content

Commit

Permalink
NEW Add new methods for querying data in various version modes
Browse files Browse the repository at this point in the history
Needed to convert existing CMSSiteTreeFilter implementations from using
filterByCallback() to instead perform all filtering inside the database.
  • Loading branch information
GuySartorelli committed Feb 19, 2025
1 parent f6b20b1 commit 7fef5cb
Show file tree
Hide file tree
Showing 3 changed files with 472 additions and 18 deletions.
151 changes: 134 additions & 17 deletions src/Versioned.php
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,12 @@ protected function augmentSQL(SQLSelect $query, ?DataQuery $dataQuery = null)
case 'archive':
$this->augmentSQLVersionedArchive($query, $dataQuery);
break;
case 'archive_only':
$this->augmentSQLVersionedArchiveOnly($query, $dataQuery);
break;
case 'removed_on_draft':
$this->augmentSQLVersionedRemovedOnDraft($query, $dataQuery);
break;
case 'latest_version_single':
$this->augmentSQLVersionedLatestSingle($query, $dataQuery);
break;
Expand Down Expand Up @@ -744,6 +750,42 @@ protected function augmentSQLVersionedArchive(SQLSelect $query, DataQuery $dataQ
);
}

/**
* Only include records which are archived - that is they have been removed from both the draft and live stages.
*/
protected function augmentSQLVersionedArchiveOnly(SQLSelect $query, DataQuery $dataQuery): void
{
$baseTable = $this->baseTable();
$liveTable = $this->stageTable($baseTable, Versioned::LIVE);

$query->addLeftJoin(
$liveTable,
"\"{$baseTable}\".\"ID\" = \"{$liveTable}\".\"ID\""
);
$query->addWhere("\"{$liveTable}\".\"ID\" IS NULL");

$this->augmentSQLVersionedRemovedOnDraft($query, $dataQuery);
}

/**
* Only include records which are removed from draft - these might be archived but might also still be in the live stage.
*/
protected function augmentSQLVersionedRemovedOnDraft(SQLSelect $query, DataQuery $dataQuery): void
{
$baseTable = $this->baseTable();

// Join a temporary alias BaseTable_Draft, renaming this on execution to BaseTable
// See Versioned::augmentSQLVersioned() For reference on this alias
$query->addLeftJoin(
"{$baseTable}_Draft",
"\"{$baseTable}\".\"ID\" = \"{$baseTable}_Draft\".\"ID\""
);

$query->addWhere("\"{$baseTable}_Draft\".\"ID\" IS NULL");

$this->augmentSQLVersionedLatest($query, $dataQuery);
}

/**
* Return latest version instance, regardless of whether it is on a particular stage.
* This is similar to augmentSQLVersionedLatest() below, except it only returns a single value
Expand Down Expand Up @@ -2479,24 +2521,29 @@ public static function prepopulate_versionnumber_cache($class, $stage, $idList =
* @template T of DataObject
* @param class-string<T> $class The name of the class.
* @param string $stage The name of the stage.
* @param string $filter A filter to be inserted into the WHERE clause.
* @param string $sort A sort expression to be inserted into the ORDER BY clause.
* @param int $limit A limit on the number of records returned from the database.
* @param string $containerClass The container class for the result set (default is DataList)
*
* @return DataList<T> A modified DataList designated to the specified stage
*/
public static function get_by_stage(
$class,
$stage,
$filter = '',
$sort = '',
$limit = null,
$containerClass = DataList::class
) {
ReadingMode::validateStage($stage);
string $class,
string $stage,
string|array $filter = '',
string|array|null $sort = '',
string|array|null $limit = null,
string $containerClass = DataList::class
): DataList {
$result = DataObject::get($class, $filter, $sort, $limit, $containerClass);
return $result->setDataQueryParam([
return static::updateListToIncludeStage($result, $stage);
}

/**
* Update an existing DataList to include records for a given stage.
*/
public static function updateListToIncludeStage(DataList $list, string $stage): DataList
{
ReadingMode::validateStage($stage);
return $list->setDataQueryParam([
'Versioned.mode' => 'stage',
'Versioned.stage' => $stage
]);
Expand Down Expand Up @@ -2784,11 +2831,11 @@ public function isModifiedOnDraft()
*
* @template T of DataObject
* @param class-string<T> $class
* @param string $filter
* @param string $sort
* @param string $filter Explicitly used in where clause as raw SQL - use with caution!
* @param string $sort Explicitly used in orderBy clause as raw SQL - use with caution!
* @return DataList<T>
*/
public static function get_including_deleted($class, $filter = "", $sort = "")
public static function get_including_deleted(string $class, string|array $filter = '', string $sort = ''): DataList
{
$list = DataList::create($class);
if (!empty($filter)) {
Expand All @@ -2797,8 +2844,78 @@ public static function get_including_deleted($class, $filter = "", $sort = "")
if (!empty($sort)) {
$list = $list->orderBy($sort);
}
$list = $list->setDataQueryParam("Versioned.mode", "latest_versions");
return $list;
return static::updateListToAlsoIncludeDeleted($list);
}

/**
* Update a DataList to query the latest version of each record stored in the (class)_Versions tables.
*
* In particular, this will query deleted records as well as active ones.
*/
public static function updateListToAlsoIncludeDeleted(DataList $list): DataList
{
return $list->setDataQueryParam('Versioned.mode', 'latest_versions');
}

/**
* Return the equivalent of a DataList::create() call, querying only records which have been removed
* from draft. This includes archived records and records which were published but have no draft record.
*
* @template T of DataObject
* @param class-string<T> $class
* @param string $where Explicitly used in where clause as raw SQL - use with caution!
* @param string $orderBy Explicitly used in orderBy clause as raw SQL - use with caution!
* @return DataList<T>
*/
public static function getRemovedFromDraft(string $class, string|array $where = '', string $orderBy = ''): DataList
{
$list = DataList::create($class);
if (!empty($where)) {
$list = $list->where($where);
}
if (!empty($orderBy)) {
$list = $list->orderBy($orderBy);
}
return static::updateListToOnlyIncludeRemovedFromDraft($list);
}

/**
* Update a DataList to query only records which have been removed from draft.
* This includes archived records and records which were published but have no draft record.
*/
public static function updateListToOnlyIncludeRemovedFromDraft(DataList $list): DataList
{
return $list->setDataQueryParam('Versioned.mode', 'removed_on_draft');
}

/**
* Return the equivalent of a DataList::create() call, querying only records which are archived
* (i.e. removed in both draft and live)
*
* @template T of DataObject
* @param class-string<T> $class
* @param string $where Explicitly used in where clause as raw SQL - use with caution!
* @param string $orderBy Explicitly used in orderBy clause as raw SQL - use with caution!
* @return DataList<T>
*/
public static function getArchivedOnly(string $class, string|array $where = '', string $orderBy = ''): DataList
{
$list = DataList::create($class);
if (!empty($where)) {
$list = $list->where($where);
}
if (!empty($orderBy)) {
$list = $list->orderBy($orderBy);
}
return static::updateListToOnlyIncludeArchived($list);
}

/**
* Update a DataList to query only records which are archived (i.e. removed in both draft and live)
*/
public static function updateListToOnlyIncludeArchived(DataList $list): DataList
{
return $list->setDataQueryParam('Versioned.mode', 'archive_only');
}

/**
Expand Down
Loading

0 comments on commit 7fef5cb

Please sign in to comment.