Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save NightJar/eb0565d6d3ad41a5c5e6fcbfe6a54f4b to your computer and use it in GitHub Desktop.
Save NightJar/eb0565d6d3ad41a5c5e6fcbfe6a54f4b to your computer and use it in GitHub Desktop.
<?php
namespace Nightjar\SS4UpgradeTasks;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Psr\Log\LoggerInterface;
use SilverStripe\Assets\File;
use SilverStripe\Control\Director;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\BuildTask;
use SilverStripe\Logging\PreformattedEchoHandler;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectSchema;
use SilverStripe\Versioned\Versioned;
use CWP\AgencyExtensions\Model\CarouselItem;
/**
* Can be deleted from project once run. One time use script.
*/
class UpgradeMigrationPublishNewlyVersionedDataObjects extends BuildTask
{
/**
* Allows classes to be ingored by this automated procedure
* e.g. in the case that there is a more complex migration task performed elsewhere.
*
* @config
* @var array
*/
private static $ignore_classes = [
File::class, // Handled by the File Migration task in `silverstripe/assets`
];
/**
* Apply extra filters to select what will be published from the unversioned records
* Map of:
* PHP class name (fully qualified with namespace) => WHERE clasue (escaped SQL string)
*
* @config
* @var array
*/
private static $extra_filters = [
CarouselItem::class => [
'Archived' => 0,
'Parent.ID:GreaterThan' => 0,
],
];
private static $segment = 'UpgradeMigrationPublishNewlyVersionedDataObjects';
protected $description = 'Ensure that previously unversioned (always "live") items are still accessible to a'
. ' visitor by publishing them all, as the "main" table is now the "draft" table.';
private static $dependencies = [
'Logger' => '%$' . LoggerInterface::class,
];
/**
* @var LoggerInterface
*/
private $logger;
/**
* @param \Psr\Log\LoggerInterface $logger
* @return self
*/
public function setLogger(LoggerInterface $logger): self
{
$this->logger = $logger;
return $this;
}
public function run($request)
{
$this->addConfigurationToLogger();
$this->logger->info('Beginning task ' . __CLASS__);
$ignoredClasses = $this->config()->get('ignore_classes');
$versionedClasses = array_filter($this->findVersionedClasses(), function ($class) use ($ignoredClasses) {
return !in_array($class, $ignoredClasses, true);
});
$extraFilters = $this->config()->get('extra_filters');
foreach ($versionedClasses as $class) {
$table = Injector::inst()->get(DataObjectSchema::class)->baseDataTable($class);
$versionTable = "${table}_Versions";
$unversioned = Versioned::get_by_stage($class, Versioned::DRAFT)
->leftJoin($versionTable, "\"$versionTable\".\"RecordID\" = \"$table\".\"ID\"")
->where("\"$versionTable\".\"ID\" IS NULL");
$unversionedToPublish = $unversioned;
$unversionedToSave = null;
if (isset($extraFilters[$class])) {
$unversionedToPublish = $unversioned->filter($extraFilters[$class]);
$unversionedToSave = $unversioned->exclude($extraFilters[$class]);
}
$count = $unversioned->count();
if ($count) {
$this->logger->info("$class - $count unversioned records");
}
$unversionedToPublish->each(function ($instance) use ($class) {
/** @var DataObject $instance */
$instance->publishSingle();
$this->logger->info(" - Published $class # $instance->ID");
});
if ($unversionedToSave) {
// Even if this these items should not be live, they should still have versions.
$unversionedToSave->each(function ($instance) use ($class) {
/** @var DataObject $instance */
$instance->write(false, false, true);
$this->logger->info(" - Wrote unpublished version for $class # $instance->ID");
// These instances could then be archived, but are left as draft.
// Archiving would be a bit disjointing as an upgrade, in terms of UI expectations.
});
}
}
$this->logger->info('Finished task ' . __CLASS__);
}
private function addConfigurationToLogger()
{
if (Director::is_cli()) {
$this->logger->pushHandler(new StreamHandler('php://stdout'));
$this->logger->pushHandler(new StreamHandler('php://stderr', Logger::WARNING));
} else {
$this->logger->pushHandler(new PreformattedEchoHandler());
}
}
private function findVersionedClasses()
{
return array_filter(
ClassInfo::subclassesFor(DataObject::class),
function ($class) {
return (
DataObject::has_extension($class, Versioned::class)
&& in_array(
Versioned::class,
(array)Config::forClass($class)->uninherited('extensions'),
true
)
&& DataObject::singleton($class)->hasStages()
);
}
);
}
}
@NightJar
Copy link
Author

NEW migration task to publish unVersioned objects

DataObjects with the Versioned extension may not have always had this
setting applied to them. What was previously "always live" by nature of
not having a "draft" version now are by default in draft, as it is the
"base table" that is used as the draft with the Versioned extension.

We need to migrate all objects that exist in the draft table and have no
recorded version at all to the live table, in order that they may continue
to function as they had before the Versioned extension was applied.

This is a new build task that does exactly that. It searches for any
DataObject in this state, and publishes it - but care is taken to not
cascade that publish to cause issues with any new owns relationship
that may have been set up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment