Skip to content

Instantly share code, notes, and snippets.

@simonhamp
Created May 26, 2022 02:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simonhamp/83b9d16f4604ae78a25189f680511974 to your computer and use it in GitHub Desktop.
Save simonhamp/83b9d16f4604ae78a25189f680511974 to your computer and use it in GitHub Desktop.
Split a Statamic Tree into two collections
  1. Pop this file in your app/Console/Commands/
  2. Create the new collection e.g. support
  3. Run php artisan tree:split pages:d76793d4-de7c-4707-b0fb-02712fdac7f7 support (obv. replace the collection names and the branch root ID with relevant ones for your case)

It attempts to split an entire branch at the given page ID from that source collection's tree and move that branch (that page and all of its children) into a new collection.

It will probably do the job, but break a lot of stuff along the way. You may have references to those entries from other places. Those references may need updating in some way.

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Statamic\Facades\Collection;
use Statamic\Eloquent\Entries\UuidEntryModel as EntryModel;
use Statamic\Facades\Entry;
use Statamic\Structures\CollectionTree;
class TreeSplit extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'tree:split {source} {target}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$source = $this->argument('source');
$collection = Str::before($source, ':');
$root = Str::after($source, ':');
$target = $this->argument('target');
$source_tree = Collection::findByHandle($collection);
// Find equivalent $root ids for all locales
$root_ids = EntryModel::where('id', $root)
->orWhere('origin_id', $root)
->get()
->keyBy->id
->map(function ($localised) {
return $localised->site;
})
->flip();
/**
* @var string $locale
* @var CollectionTree $tree
*/
foreach ($source_tree->structure()->trees() as $locale => $tree) {
$root = $root_ids->get($locale);
/**
* @var \Statamic\Structures\Page $root_page
*/
$root_page = $tree->find($root);
if (! $root_page) {
// Set the original tree to the validated tree to remove any spurious branches
$tree->tree($tree->tree())->save();
continue;
}
$this->info("Moving {$root_page->url()} from `{$collection}` collection to `{$target}` collection");;
$new_branch = collect($tree->tree())->keyBy('entry')->get($root);
$new_root = [
[
'entry' => $root,
],
];
$new_branch = $new_branch['children'] ?? [];
$new_tree_collection = collect(array_merge($new_root, $new_branch));
// Change the collection of all the pages
foreach ($new_tree_collection->flatten() as $page_id) {
Entry::find($page_id)->collection($target)->save();
}
// Create or load the new tree
/** @var CollectionTree $new_tree */
$new_tree = Collection::findByHandle($target)->structure()->in($locale)
?? (new CollectionTree())
->handle($target)
->locale($locale)
->syncOriginal();
// Overlay the source page's structure from the current locale to this new structure
$new_tree->tree($new_tree_collection->all());
$new_tree->pages()->setParent($root_page);
try {
$new_tree->save();
} catch (\ErrorException $e) {
// meh
}
$tree->remove($root_page);
$tree->save();
}
return Command::SUCCESS;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment