diff --git a/composer.json b/composer.json index ec810440..5a0450f1 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "require-dev": { "doctrine/dbal": "^3.3", "laravel/pint": "^1.0", - "orchestra/testbench": "^8.0 || ^9.0", + "orchestra/testbench": "^8.0 || ^9.0.2", "phpunit/phpunit": "^9.4 || ^10.0 || ^11.0" }, "scripts": { diff --git a/src/Assets/AssetContainer.php b/src/Assets/AssetContainer.php index ba98a66b..f82cf39e 100644 --- a/src/Assets/AssetContainer.php +++ b/src/Assets/AssetContainer.php @@ -4,6 +4,7 @@ use Illuminate\Database\Eloquent\Model; use Statamic\Assets\AssetContainer as FileEntry; +use Statamic\Contracts\Assets\AssetContainer as AssetContainerContract; use Statamic\Events\AssetContainerDeleted; use Statamic\Events\AssetContainerSaved; use Statamic\Support\Str; @@ -76,6 +77,34 @@ public function toModel() ]); } + public static function makeModelFromContract(AssetContainerContract $source) + { + $model = app('statamic.eloquent.assets.container_model')::firstOrNew(['handle' => $source->handle()])->fill([ + 'title' => $source->title(), + 'disk' => $source->diskHandle() ?? config('filesystems.default'), + 'settings' => [ + 'allow_uploads' => $source->allowUploads(), + 'allow_downloading' => $source->allowDownloading(), + 'allow_moving' => $source->allowMoving(), + 'allow_renaming' => $source->allowRenaming(), + 'create_folders' => $source->createFolders(), + 'search_index' => $source->searchIndex(), + 'source_preset' => $source->sourcePreset, + 'warm_presets' => $source->warmPresets, + ], + ]); + + // Set initial timestamps. + if (empty($model->created_at) && isset($meta['last_modified'])) { + $model->created_at = $source->fileLastModified(); + $model->updated_at = $source->fileLastModified(); + } + + $model->save(); + + return $model; + } + public function model($model = null) { if (func_num_args() === 0) { diff --git a/src/Commands/ExportAssets.php b/src/Commands/ExportAssets.php index 17376ba6..5e680dba 100644 --- a/src/Commands/ExportAssets.php +++ b/src/Commands/ExportAssets.php @@ -30,7 +30,7 @@ class ExportAssets extends Command * * @var string */ - protected $signature = 'statamic:eloquent:export-assets {--force : Force the operation to run, with all questions yes}'; + protected $signature = 'statamic:eloquent:export-assets {--force : Force the export to run, with all prompts answered "yes"}'; /** * The console command description. diff --git a/src/Commands/ExportCollections.php b/src/Commands/ExportCollections.php index cb7d64b5..c589c4d0 100644 --- a/src/Commands/ExportCollections.php +++ b/src/Commands/ExportCollections.php @@ -29,7 +29,7 @@ class ExportCollections extends Command * * @var string */ - protected $signature = 'statamic:eloquent:export-collections {--force : Force the operation to run, with all questions yes}'; + protected $signature = 'statamic:eloquent:export-collections {--force : Force the export to run, with all prompts answered "yes"}'; /** * The console command description. diff --git a/src/Commands/ExportNavs.php b/src/Commands/ExportNavs.php index 35aef1c3..2808654e 100644 --- a/src/Commands/ExportNavs.php +++ b/src/Commands/ExportNavs.php @@ -28,7 +28,7 @@ class ExportNavs extends Command * * @var string */ - protected $signature = 'statamic:eloquent:export-navs {--force : Force the operation to run, with all questions yes}'; + protected $signature = 'statamic:eloquent:export-navs {--force : Force the export to run, with all prompts answered "yes"}'; /** * The console command description. diff --git a/src/Commands/ExportTaxonomies.php b/src/Commands/ExportTaxonomies.php index 91aa84d7..5368c08e 100644 --- a/src/Commands/ExportTaxonomies.php +++ b/src/Commands/ExportTaxonomies.php @@ -29,7 +29,7 @@ class ExportTaxonomies extends Command * * @var string */ - protected $signature = 'statamic:eloquent:export-taxonomies {--force : Force the operation to run, with all questions yes}'; + protected $signature = 'statamic:eloquent:export-taxonomies {--force : Force the export to run, with all prompts answered "yes"}'; /** * The console command description. diff --git a/src/Commands/ImportAssets.php b/src/Commands/ImportAssets.php index 17163347..dba8ae54 100644 --- a/src/Commands/ImportAssets.php +++ b/src/Commands/ImportAssets.php @@ -7,7 +7,6 @@ use Statamic\Assets\AssetContainerContents; use Statamic\Assets\AssetRepository; use Statamic\Console\RunsInPlease; -use Statamic\Contracts\Assets\Asset; use Statamic\Contracts\Assets\AssetContainer as AssetContainerContract; use Statamic\Contracts\Assets\AssetContainerRepository as AssetContainerRepositoryContract; use Statamic\Contracts\Assets\AssetRepository as AssetRepositoryContract; @@ -27,21 +26,22 @@ class ImportAssets extends Command * * @var string */ - protected $signature = 'statamic:eloquent:import-assets {--force : Force the operation to run, with all questions yes}'; + protected $signature = 'statamic:eloquent:import-assets + {--force : Force the import to run, with all prompts answered "yes"} + {--only-asset-containers : Only import asset containers} + {--only-assets : Only import assets}'; /** * The console command description. * * @var string */ - protected $description = 'Imports file based asset containers into the database.'; + protected $description = 'Imports file-based asset containers & asset metadata into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { $this->useDefaultRepositories(); @@ -51,7 +51,7 @@ public function handle() return 0; } - private function useDefaultRepositories() + private function useDefaultRepositories(): void { Facade::clearResolvedInstance(AssetContainerRepositoryContract::class); Facade::clearResolvedInstance(AssetRepositoryContract::class); @@ -60,43 +60,46 @@ private function useDefaultRepositories() Statamic::repository(AssetRepositoryContract::class, AssetRepository::class); app()->bind(AssetContainerContract::class, AssetContainer::class); - app()->bind(AssetContract::class, Asset::class); - - app()->bind(AssetContainerContents::class, function ($app) { - return new AssetContainerContents(); - }); + app()->bind(AssetContainerContents::class, fn ($app) => new AssetContainerContents()); } - private function importAssetContainers() + private function importAssetContainers(): void { - if (! $this->option('force') && ! $this->confirm('Do you want to import asset containers?')) { + if (! $this->shouldImportAssetContainers()) { return; } - $containers = AssetContainerFacade::all(); - - $this->withProgressBar($containers, function ($container) { - $lastModified = $container->fileLastModified(); - $container->toModel()->fill(['created_at' => $lastModified, 'updated_at' => $lastModified])->save(); + $this->withProgressBar(AssetContainerFacade::all(), function ($container) { + AssetContainer::makeModelFromContract($container); }); - $this->line(''); - $this->info('Asset containers imported'); + $this->components->info('Assets containers imported sucessfully'); } - private function importAssets() + private function importAssets(): void { - if (! $this->option('force') && ! $this->confirm('Do you want to import assets?')) { + if (! $this->shouldImportAssets()) { return; } - $assets = AssetFacade::all(); - - $this->withProgressBar($assets, function ($asset) { + $this->withProgressBar(AssetFacade::all(), function ($asset) { EloquentAsset::makeModelFromContract($asset); }); - $this->newLine(); - $this->info('Assets imported'); + $this->components->info('Assets imported sucessfully'); + } + + private function shouldImportAssetContainers(): bool + { + return $this->option('only-asset-containers') + || ! $this->option('only-assets') + && ($this->option('force') || $this->confirm('Do you want to import asset containers?')); + } + + private function shouldImportAssets(): bool + { + return $this->option('only-assets') + || ! $this->option('only-asset-containers') + && ($this->option('force') || $this->confirm('Do you want to import assets?')); } } diff --git a/src/Commands/ImportBlueprints.php b/src/Commands/ImportBlueprints.php index d5776d2c..abc86a4d 100644 --- a/src/Commands/ImportBlueprints.php +++ b/src/Commands/ImportBlueprints.php @@ -29,14 +29,12 @@ class ImportBlueprints extends Command * * @var string */ - protected $description = 'Imports file based blueprints and fieldsets into the database.'; + protected $description = 'Imports file-based blueprints & fieldsets into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { $this->useDefaultRepositories(); @@ -46,7 +44,7 @@ public function handle() return 0; } - private function useDefaultRepositories() + private function useDefaultRepositories(): void { Facade::clearResolvedInstance(\Statamic\Fields\BlueprintRepository::class); Facade::clearResolvedInstance(\Statamic\Fields\FieldsetRepository::class); @@ -62,7 +60,7 @@ private function useDefaultRepositories() ); } - private function importBlueprints() + private function importBlueprints(): void { $directory = resource_path('blueprints'); @@ -104,25 +102,21 @@ private function importBlueprints() ->setHandle($handle) ->setNamespace($namespace ?? null) ->setContents($contents); + $lastModified = Carbon::createFromTimestamp(File::lastModified($path)); - $model = app('statamic.eloquent.blueprints.blueprint_model')::firstOrNew([ - 'handle' => $blueprint->handle(), + app('statamic.eloquent.blueprints.blueprint_model')::firstOrNew([ + 'handle' => $blueprint->handle(), 'namespace' => $blueprint->namespace() ?? null, - ])->fill([ - 'data' => $blueprint->contents(), - 'created_at' => $lastModified, - 'updated_at' => $lastModified, - ]); - - $model->save(); + ]) + ->fill(['data' => $blueprint->contents(), 'created_at' => $lastModified, 'updated_at' => $lastModified]) + ->save(); }); - $this->newLine(); - $this->info('Blueprints imported'); + $this->components->info('Blueprints imported successfully.'); } - private function importFieldsets() + private function importFieldsets(): void { $directory = resource_path('fieldsets'); @@ -134,26 +128,21 @@ private function importFieldsets() $handle = Str::before($basename, '.yaml'); $handle = str_replace('/', '.', $handle); - $fieldset = Fieldset::make($handle) - ->setContents(YAML::file($path)->parse()); + $fieldset = Fieldset::make($handle)->setContents(YAML::file($path)->parse()); + $lastModified = Carbon::createFromTimestamp(File::lastModified($path)); - $model = app('statamic.eloquent.blueprints.fieldset_model')::firstOrNew([ + app('statamic.eloquent.blueprints.fieldset_model')::firstOrNew([ 'handle' => $fieldset->handle(), - ])->fill([ - 'data' => $fieldset->contents(), - 'created_at' => $lastModified, - 'updated_at' => $lastModified, - ]); - - $model->save(); + ]) + ->fill(['data' => $fieldset->contents(), 'created_at' => $lastModified, 'updated_at' => $lastModified]) + ->save(); }); - $this->newLine(); - $this->info('Fieldsets imported'); + $this->components->info('Fieldsets imported successfully.'); } - private function getNamespaceAndHandle($blueprint) + private function getNamespaceAndHandle(string $blueprint): array { $blueprint = str_replace('/', '.', $blueprint); $parts = explode('.', $blueprint); diff --git a/src/Commands/ImportCollections.php b/src/Commands/ImportCollections.php index 46d185c1..b3536925 100644 --- a/src/Commands/ImportCollections.php +++ b/src/Commands/ImportCollections.php @@ -25,21 +25,22 @@ class ImportCollections extends Command * * @var string */ - protected $signature = 'statamic:eloquent:import-collections {--force : Force the operation to run, with all questions yes}'; + protected $signature = 'statamic:eloquent:import-collections + {--force : Force the import to run, with all prompts answered "yes"} + {--only-collections : Only import collections} + {--only-collection-trees : Only import collection trees}'; /** * The console command description. * * @var string */ - protected $description = 'Imports file based collections into the database.'; + protected $description = 'Imports file-based collections & collection trees into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { $this->usingDefaultRepositories(function () { $this->importCollections(); @@ -47,13 +48,12 @@ public function handle() $this->updateEntryOrder(); - $this->newLine(); - $this->info('Collections imported'); + $this->components->info('Collections imported successfully.'); return 0; } - private function usingDefaultRepositories(Closure $callback) + private function usingDefaultRepositories(Closure $callback): void { $originalRepo = get_class(app()->make(CollectionRepositoryContract::class)); $originalTreeRepo = get_class(app()->make(CollectionTreeRepositoryContract::class)); @@ -76,20 +76,10 @@ private function usingDefaultRepositories(Closure $callback) Facade::clearResolvedInstance(CollectionTreeRepositoryContract::class); } - private function importCollections() + private function importCollections(): void { - if (! $this->option('force')) { - $importCollections = $this->confirm('Do you want to import collections?'); - $importCollectionTrees = $this->confirm('Do you want to import collections trees?'); - } else { - $importCollections = true; - $importCollectionTrees = true; - } - - $collections = CollectionFacade::all(); - - $this->withProgressBar($collections, function ($collection) use ($importCollections, $importCollectionTrees) { - if ($importCollections) { + $this->withProgressBar(CollectionFacade::all(), function ($collection) { + if ($this->shouldImportCollections()) { $lastModified = $collection->fileLastModified(); EloquentCollection::makeModelFromContract($collection) @@ -97,9 +87,10 @@ private function importCollections() ->save(); } - if ($importCollectionTrees && ($structure = $collection->structure())) { + if ($this->shouldImportCollectionTrees() && $structure = $collection->structure()) { $structure->trees()->each(function ($tree) { $lastModified = $tree->fileLastModified(); + app('statamic.eloquent.collections.tree')::makeModelFromContract($tree) ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) ->save(); @@ -108,10 +99,24 @@ private function importCollections() }); } - private function updateEntryOrder() + private function updateEntryOrder(): void { $this->withProgressBar(CollectionFacade::all(), function ($collections) { $collections->updateEntryOrder(); }); } + + private function shouldImportCollections(): bool + { + return $this->option('only-collections') + || ! $this->option('only-collection-trees') + && ($this->option('force') || $this->confirm('Do you want to import collections?')); + } + + private function shouldImportCollectionTrees(): bool + { + return $this->option('only-collection-trees') + || ! $this->option('only-collections') + && ($this->option('force') || $this->confirm('Do you want to import collections trees?')); + } } diff --git a/src/Commands/ImportEntries.php b/src/Commands/ImportEntries.php index f54b933e..82bc0613 100644 --- a/src/Commands/ImportEntries.php +++ b/src/Commands/ImportEntries.php @@ -30,14 +30,12 @@ class ImportEntries extends Command * * @var string */ - protected $description = 'Imports file based entries into the database.'; + protected $description = 'Imports file-based entries into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { $this->useDefaultRepositories(); @@ -46,7 +44,7 @@ public function handle() return 0; } - private function useDefaultRepositories() + private function useDefaultRepositories(): void { Facade::clearResolvedInstance(EntryRepositoryContract::class); Facade::clearResolvedInstance(CollectionRepositoryContract::class); @@ -57,7 +55,7 @@ private function useDefaultRepositories() app()->bind(EntryContract::class, app('statamic.eloquent.entries.entry')); } - private function importEntries() + private function importEntries(): void { $entries = Entry::all(); @@ -67,29 +65,29 @@ private function importEntries() }); if ($entriesWithOrigin->count() > 0) { - $this->info('Importing origin entries'); + $this->components->info('Importing origin entries...'); } $this->withProgressBar($entriesWithoutOrigin, function ($entry) { $lastModified = $entry->fileLastModified(); + $entry = EloquentEntry::makeModelFromContract($entry) ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) ->save(); }); if ($entriesWithOrigin->count() > 0) { - $this->newLine(); - $this->info('Importing localized entries'); + $this->components->info('Importing localized entries...'); $this->withProgressBar($entriesWithOrigin, function ($entry) { $lastModified = $entry->fileLastModified(); + EloquentEntry::makeModelFromContract($entry) ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) ->save(); }); } - $this->newLine(); - $this->info('Entries imported'); + $this->components->info('Entries imported successfully.'); } } diff --git a/src/Commands/ImportForms.php b/src/Commands/ImportForms.php index 1b977ab6..daa92066 100644 --- a/src/Commands/ImportForms.php +++ b/src/Commands/ImportForms.php @@ -23,21 +23,22 @@ class ImportForms extends Command * * @var string */ - protected $signature = 'statamic:eloquent:import-forms'; + protected $signature = 'statamic:eloquent:import-forms + {--force : Force the import to run, with all prompts answered "yes"} + {--only-forms : Only import forms} + {--only-form-submissions : Only import submissions}'; /** * The console command description. * * @var string */ - protected $description = 'Imports file based forms and submissions into the database.'; + protected $description = 'Imports file-based forms & form submissions into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { $this->useDefaultRepositories(); @@ -46,38 +47,56 @@ public function handle() return 0; } - private function useDefaultRepositories() + private function useDefaultRepositories(): void { Facade::clearResolvedInstance(FormContract::class); Facade::clearResolvedInstance(SubmissionContract::class); app()->bind(FormContract::class, StacheForm::class); app()->bind(SubmissionContract::class, StacheSubmission::class); + app()->bind(\Statamic\Eloquent\Forms\SubmissionQueryBuilder::class, \Statamic\Stache\Query\SubmissionQueryBuilder::class); } - private function importForms() + private function importForms(): void { - $forms = (new FormRepository())->all(); - - $this->withProgressBar($forms, function ($form) { - $lastModified = Carbon::createFromTimestamp(File::lastModified($form->path())); - $model = Form::makeModelFromContract($form)->fill([ - 'created_at' => $lastModified, - 'updated_at' => $lastModified, - ]); - $model->save(); - - $form->submissions()->each(function ($submission) use ($model) { - $timestamp = app('statamic.eloquent.forms.submission_model')::make()->fromDateTime($submission->date()); - - $model->submissions()->firstOrNew(['created_at' => $timestamp])->fill([ - 'data' => $submission->data(), - 'updated_at' => $timestamp, - ])->save(); - }); + $this->withProgressBar((new FormRepository)->all(), function ($form) { + if ($this->shouldImportForms()) { + $lastModified = Carbon::createFromTimestamp(File::lastModified($form->path())); + + Form::makeModelFromContract($form) + ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) + ->save(); + } + + if ($this->shouldImportFormSubmissions()) { + $form->submissions()->each(function ($submission) use ($form) { + $timestamp = app('statamic.eloquent.form_submissions.model')::make()->fromDateTime($submission->date()); + + app('statamic.eloquent.form_submissions.model')::firstOrNew(['created_at' => $timestamp]) + ->fill([ + 'form' => $form->handle(), + 'data' => $submission->data(), + 'updated_at' => $timestamp, + ]) + ->save(); + }); + } }); - $this->newLine(); - $this->info('Forms imported'); + $this->components->info('Forms imported successfully.'); + } + + private function shouldImportForms(): bool + { + return $this->option('only-forms') + || ! $this->option('only-form-submissions') + && ($this->option('force') || $this->confirm('Do you want to import forms?')); + } + + private function shouldImportFormSubmissions(): bool + { + return $this->option('only-form-submissions') + || ! $this->option('only-forms') + && ($this->option('force') || $this->confirm('Do you want to import form submissions?')); } } diff --git a/src/Commands/ImportGlobals.php b/src/Commands/ImportGlobals.php index af5cbe53..4f83aed6 100644 --- a/src/Commands/ImportGlobals.php +++ b/src/Commands/ImportGlobals.php @@ -24,21 +24,22 @@ class ImportGlobals extends Command * * @var string */ - protected $signature = 'statamic:eloquent:import-globals'; + protected $signature = 'statamic:eloquent:import-globals + {--force : Force the import to run, with all prompts answered "yes"} + {--only-global-sets : Only import global sets} + {--only-global-variables : Only import global variables}'; /** * The console command description. * * @var string */ - protected $description = 'Imports file based globals into the database.'; + protected $description = 'Imports file-based global sets & variables into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { $this->useDefaultRepositories(); @@ -47,7 +48,7 @@ public function handle() return 0; } - private function useDefaultRepositories() + private function useDefaultRepositories(): void { Facade::clearResolvedInstance(GlobalRepositoryContract::class); @@ -57,22 +58,38 @@ private function useDefaultRepositories() app()->bind(GlobalSetContract::class, GlobalSet::class); } - private function importGlobals() + private function importGlobals(): void { - $sets = GlobalSetFacade::all(); - - $this->withProgressBar($sets, function ($set) { - $lastModified = $set->fileLastModified(); + $this->withProgressBar(GlobalSetFacade::all(), function ($set) { + if ($this->shouldImportGlobalSets()) { + $lastModified = $set->fileLastModified(); + + GlobalSet::makeModelFromContract($set) + ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) + ->save(); + } + + if ($this->shouldImportGlobalVariables()) { + $set->localizations()->each(function ($locale) { + Variables::makeModelFromContract($locale)->save(); + }); + } + }); - $setModel = GlobalSet::makeModelFromContract($set)->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]); - $setModel->save(); + $this->components->info('Globals imported successfully.'); + } - $set->localizations()->each(function ($locale) { - Variables::makeModelFromContract($locale)->save(); - }); - }); + private function shouldImportGlobalSets(): bool + { + return $this->option('only-global-sets') + || ! $this->option('only-global-variables') + && ($this->option('force') || $this->confirm('Do you want to import global sets?')); + } - $this->newLine(); - $this->info('Globals imported'); + private function shouldImportGlobalVariables(): bool + { + return $this->option('only-global-variables') + || ! $this->option('only-global-sets') + && ($this->option('force') || $this->confirm('Do you want to import global variables?')); } } diff --git a/src/Commands/ImportNavs.php b/src/Commands/ImportNavs.php index b7b33581..553dd45c 100644 --- a/src/Commands/ImportNavs.php +++ b/src/Commands/ImportNavs.php @@ -8,10 +8,8 @@ use Statamic\Contracts\Structures\Nav as NavContract; use Statamic\Contracts\Structures\NavigationRepository as NavigationRepositoryContract; use Statamic\Contracts\Structures\NavTreeRepository as NavTreeRepositoryContract; -use Statamic\Contracts\Structures\Tree as TreeContract; use Statamic\Eloquent\Structures\Nav as EloquentNav; use Statamic\Eloquent\Structures\NavTree as EloquentNavTree; -use Statamic\Eloquent\Structures\Tree as EloquentTree; use Statamic\Facades\Nav as NavFacade; use Statamic\Stache\Repositories\NavigationRepository; use Statamic\Stache\Repositories\NavTreeRepository; @@ -26,21 +24,22 @@ class ImportNavs extends Command * * @var string */ - protected $signature = 'statamic:eloquent:import-navs {--force : Force the operation to run, with all questions yes}'; + protected $signature = 'statamic:eloquent:import-navs + {--force : Force the import to run, with all prompts answered "yes"} + {--only-navs : Only import navigations} + {--only-nav-trees : Only import navigation trees}'; /** * The console command description. * * @var string */ - protected $description = 'Imports file based navs into the database.'; + protected $description = 'Imports file-based navigations & nav trees into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { $this->useDefaultRepositories(); @@ -49,7 +48,7 @@ public function handle() return 0; } - private function useDefaultRepositories() + private function useDefaultRepositories(): void { Facade::clearResolvedInstance(NavigationRepositoryContract::class); Facade::clearResolvedInstance(NavTreeRepositoryContract::class); @@ -58,32 +57,23 @@ private function useDefaultRepositories() Statamic::repository(NavTreeRepositoryContract::class, NavTreeRepository::class); app()->bind(NavContract::class, EloquentNav::class); - app()->bind(TreeContract::class, EloquentTree::class); } - private function importNavs() + private function importNavs(): void { - if (! $this->option('force')) { - $importNavigations = $this->confirm('Do you want to import navs?'); - $importNavigationTrees = $this->confirm('Do you want to import nav trees?'); - } else { - $importNavigations = true; - $importNavigationTrees = true; - } - - $navs = NavFacade::all(); - - $this->withProgressBar($navs, function ($nav) use ($importNavigations, $importNavigationTrees) { - if ($importNavigations) { + $this->withProgressBar(NavFacade::all(), function ($nav) { + if ($this->shouldImportNavigations()) { $lastModified = $nav->fileLastModified(); + EloquentNav::makeModelFromContract($nav) ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) ->save(); } - if ($importNavigationTrees) { + if ($this->shouldImportNavigationTrees()) { $nav->trees()->each(function ($tree) { $lastModified = $tree->fileLastModified(); + EloquentNavTree::makeModelFromContract($tree) ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) ->save(); @@ -91,7 +81,20 @@ private function importNavs() } }); - $this->newLine(); - $this->info('Navs imported'); + $this->components->info('Navs imported successfully.'); + } + + private function shouldImportNavigations(): bool + { + return $this->option('only-navs') + || ! $this->option('only-nav-trees') + && ($this->option('force') || $this->confirm('Do you want to import navs?')); + } + + private function shouldImportNavigationTrees(): bool + { + return $this->option('only-nav-trees') + || ! $this->option('only-navs') + && ($this->option('force') || $this->confirm('Do you want to import nav trees?')); } } diff --git a/src/Commands/ImportRevisions.php b/src/Commands/ImportRevisions.php index 08c27136..4770e7e3 100644 --- a/src/Commands/ImportRevisions.php +++ b/src/Commands/ImportRevisions.php @@ -25,35 +25,37 @@ class ImportRevisions extends Command * * @var string */ - protected $description = 'Imports file based revisions into the database.'; + protected $description = 'Imports file-based revisions into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { - if (config('statamic.revisions.enabled')) { - $this->importRevisions(); + if (! config('statamic.revisions.enabled')) { + $this->components->error('This import can only be run when revisions are enabled.'); + + return 1; } + $this->importRevisions(); + return 0; } - private function importRevisions() + private function importRevisions(): void { - $files = File::allFiles(config('statamic.revisions.path')); + $this->withProgressBar(File::allFiles(config('statamic.revisions.path')), function ($file) { + $yaml = YAML::file($file->getPathname())->parse(); - $this->withProgressBar($files, function ($file) { - $yml = YAML::file($file->getPathname())->parse(); $revision = (new Revision()) ->key($file->getRelativePath()) - ->action($yml['action'] ?? false) - ->date(Carbon::parse($yml['date'])) - ->user($yml['user'] ?? false) - ->message($yml['message'] ?? '') - ->attributes($yml['attributes'] ?? []); + ->action($yaml['action'] ?? false) + ->date(Carbon::parse($yaml['date'])) + ->user($yaml['user'] ?? false) + ->message($yaml['message'] ?? '') + ->attributes($yaml['attributes'] ?? []); + if ($file->getBasename('.yaml') === 'working') { $revision->action('working'); } @@ -61,7 +63,6 @@ private function importRevisions() $revision->toModel()->save(); }); - $this->newLine(); - $this->info('Revisions imported'); + $this->components->info('Revisions imported successfully.'); } } diff --git a/src/Commands/ImportTaxonomies.php b/src/Commands/ImportTaxonomies.php index 34675adc..7e5bb4a7 100644 --- a/src/Commands/ImportTaxonomies.php +++ b/src/Commands/ImportTaxonomies.php @@ -28,21 +28,22 @@ class ImportTaxonomies extends Command * * @var string */ - protected $signature = 'statamic:eloquent:import-taxonomies {--force : Force the operation to run, with all questions yes}'; + protected $signature = 'statamic:eloquent:import-taxonomies + {--force : Force the import to run, with all prompts answered "yes"} + {--only-taxonomies : Only import taxonomies} + {--only-terms : Only import terms}'; /** * The console command description. * * @var string */ - protected $description = 'Imports file based taxonomies and terms into the database.'; + protected $description = 'Imports file-based taxonomies & terms into the database.'; /** * Execute the console command. - * - * @return int */ - public function handle() + public function handle(): int { $this->useDefaultRepositories(); @@ -52,7 +53,7 @@ public function handle() return 0; } - private function useDefaultRepositories() + private function useDefaultRepositories(): void { Facade::clearResolvedInstance(TaxonomyRepositoryContract::class); Facade::clearResolvedInstance(TermRepositoryContract::class); @@ -64,45 +65,51 @@ private function useDefaultRepositories() app()->bind(TermContract::class, StacheTerm::class); } - private function importTaxonomies() + private function importTaxonomies(): void { - if (! $this->option('force') && ! $this->confirm('Do you want to import taxonomies?')) { + if (! $this->shouldImportTaxonomies()) { return; } - $taxonomies = TaxonomyFacade::all(); - - $this->withProgressBar($taxonomies, function ($taxonomy) { + $this->withProgressBar(TaxonomyFacade::all(), function ($taxonomy) { $lastModified = $taxonomy->fileLastModified(); - EloquentTaxonomy::makeModelFromContract($taxonomy)->fill([ - 'created_at' => $lastModified, - 'updated_at' => $lastModified, - ])->save(); + + EloquentTaxonomy::makeModelFromContract($taxonomy) + ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) + ->save(); }); - $this->newLine(); - $this->info('Taxonomies imported'); + $this->components->info('Taxonomies imported successfully.'); } - private function importTerms() + private function importTerms(): void { - if (! $this->option('force') && ! $this->confirm('Do you want to import terms?')) { + if (! $this->shouldImportTerms()) { return; } - $terms = TermFacade::all(); - // Grab unique parent terms. - $terms = $terms->map->term()->unique(); - - $this->withProgressBar($terms, function ($term) { + $this->withProgressBar(TermFacade::all()->map->term()->unique(), function ($term) { $lastModified = $term->fileLastModified(); - EloquentTerm::makeModelFromContract($term)->fill([ - 'created_at' => $lastModified, - 'updated_at' => $lastModified, - ])->save(); + + EloquentTerm::makeModelFromContract($term) + ->fill(['created_at' => $lastModified, 'updated_at' => $lastModified]) + ->save(); }); - $this->newLine(); - $this->info('Terms imported'); + $this->components->info('Terms imported successfully.'); + } + + private function shouldImportTaxonomies(): bool + { + return $this->option('only-taxonomies') + || ! $this->option('only-terms') + && ($this->option('force') || $this->confirm('Do you want to import taxonomies?')); + } + + private function shouldImportTerms(): bool + { + return $this->option('only-terms') + || ! $this->option('only-taxonomies') + && ($this->option('force') || $this->confirm('Do you want to import terms?')); } } diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 215da514..2ebc07bd 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -151,8 +151,6 @@ public function origin($origin = null) return $this; } - $class = app('statamic.eloquent.entries.model'); - if ($this->origin) { if (! $this->origin instanceof EntryContract) { $this->origin = EntryFacade::find($this->origin); diff --git a/tests/Commands/ImportAssetsTest.php b/tests/Commands/ImportAssetsTest.php new file mode 100644 index 00000000..953ef762 --- /dev/null +++ b/tests/Commands/ImportAssetsTest.php @@ -0,0 +1,203 @@ + [ + 'driver' => 'local', + 'root' => __DIR__.'/tmp', + ]]); + + Facade::clearResolvedInstance(AssetContainerRepositoryContract::class); + Facade::clearResolvedInstance(AssetRepositoryContract::class); + + app()->bind(AssetContainerContract::class, \Statamic\Assets\AssetContainer::class); + app()->bind(AssetContract::class, \Statamic\Assets\Asset::class); + app()->bind(AssetContainerRepositoryContract::class, \Statamic\Stache\Repositories\AssetContainerRepository::class); + app()->bind(AssetRepositoryContract::class, \Statamic\Assets\AssetRepository::class); + app()->bind(\Statamic\Eloquent\Assets\AssetQueryBuilder::class, \Statamic\Assets\QueryBuilder::class); + app()->bind(\Statamic\Assets\AssetContainerContents::class, \Statamic\Assets\AssetContainerContents::class); + } + + public function tearDown(): void + { + app('files')->deleteDirectory(__DIR__.'/tmp'); + + parent::tearDown(); + } + + /** @test */ + public function it_imports_asset_containers_and_assets() + { + $container = tap(AssetContainer::make('test')->disk('test'))->save(); + $container->makeAsset('one.txt')->upload(UploadedFile::fake()->create('one.txt')); + $container->makeAsset('two.jpg')->upload(UploadedFile::fake()->image('two.jpg')); + $container->makeAsset('subdirectory/other.txt')->upload(UploadedFile::fake()->create('other.txt')); + + $this->assertCount(0, AssetContainerModel::all()); + $this->assertCount(0, AssetModel::all()); + + $this->artisan('statamic:eloquent:import-assets') + ->expectsQuestion('Do you want to import asset containers?', true) + ->expectsOutputToContain('Assets containers imported sucessfully.') + ->expectsQuestion('Do you want to import assets?', true) + ->expectsOutputToContain('Assets imported sucessfully.') + ->assertExitCode(0); + + $this->assertCount(1, AssetContainerModel::all()); + $this->assertCount(3, AssetModel::all()); + + $this->assertDatabaseHas('asset_containers', ['handle' => 'test', 'disk' => 'test']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'one.txt']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'two.jpg']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'subdirectory/other.txt']); + } + + /** @test */ + public function it_imports_asset_containers_and_assets_with_force_argument() + { + $container = tap(AssetContainer::make('test')->disk('test'))->save(); + $container->makeAsset('one.txt')->upload(UploadedFile::fake()->create('one.txt')); + $container->makeAsset('two.jpg')->upload(UploadedFile::fake()->image('two.jpg')); + $container->makeAsset('subdirectory/other.txt')->upload(UploadedFile::fake()->create('other.txt')); + + $this->assertCount(0, AssetContainerModel::all()); + $this->assertCount(0, AssetModel::all()); + + $this->artisan('statamic:eloquent:import-assets', ['--force' => true]) + ->expectsOutputToContain('Assets containers imported sucessfully.') + ->expectsOutputToContain('Assets imported sucessfully.') + ->assertExitCode(0); + + $this->assertCount(1, AssetContainerModel::all()); + $this->assertCount(3, AssetModel::all()); + + $this->assertDatabaseHas('asset_containers', ['handle' => 'test', 'disk' => 'test']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'one.txt']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'two.jpg']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'subdirectory/other.txt']); + } + + /** @test */ + public function it_imports_asset_containers_with_only_asset_containers_argument() + { + $container = tap(AssetContainer::make('test')->disk('test'))->save(); + $container->makeAsset('one.txt')->upload(UploadedFile::fake()->create('one.txt')); + $container->makeAsset('two.jpg')->upload(UploadedFile::fake()->image('two.jpg')); + $container->makeAsset('subdirectory/other.txt')->upload(UploadedFile::fake()->create('other.txt')); + + $this->assertCount(0, AssetContainerModel::all()); + $this->assertCount(0, AssetModel::all()); + + $this->artisan('statamic:eloquent:import-assets', ['--only-asset-containers' => true]) + ->expectsOutputToContain('Assets containers imported sucessfully.') + ->doesntExpectOutputToContain('Assets imported sucessfully.') // doesntExpectOutput + ->assertExitCode(0); + + $this->assertCount(1, AssetContainerModel::all()); + $this->assertCount(0, AssetModel::all()); + + $this->assertDatabaseHas('asset_containers', ['handle' => 'test', 'disk' => 'test']); + $this->assertDatabaseMissing('assets_meta', ['container' => 'test', 'path' => 'one.txt']); + $this->assertDatabaseMissing('assets_meta', ['container' => 'test', 'path' => 'two.jpg']); + $this->assertDatabaseMissing('assets_meta', ['container' => 'test', 'path' => 'subdirectory/other.txt']); + } + + /** @test */ + public function it_imports_asset_containers_with_console_question() + { + $container = tap(AssetContainer::make('test')->disk('test'))->save(); + $container->makeAsset('one.txt')->upload(UploadedFile::fake()->create('one.txt')); + $container->makeAsset('two.jpg')->upload(UploadedFile::fake()->image('two.jpg')); + $container->makeAsset('subdirectory/other.txt')->upload(UploadedFile::fake()->create('other.txt')); + + $this->assertCount(0, AssetContainerModel::all()); + $this->assertCount(0, AssetModel::all()); + + $this->artisan('statamic:eloquent:import-assets') + ->expectsQuestion('Do you want to import asset containers?', true) + ->expectsOutputToContain('Assets containers imported sucessfully.') + ->expectsQuestion('Do you want to import assets?', false) + ->doesntExpectOutputToContain('Assets imported sucessfully.') + ->assertExitCode(0); + + $this->assertCount(1, AssetContainerModel::all()); + $this->assertCount(0, AssetModel::all()); + + $this->assertDatabaseHas('asset_containers', ['handle' => 'test', 'disk' => 'test']); + $this->assertDatabaseMissing('assets_meta', ['container' => 'test', 'path' => 'one.txt']); + $this->assertDatabaseMissing('assets_meta', ['container' => 'test', 'path' => 'two.jpg']); + $this->assertDatabaseMissing('assets_meta', ['container' => 'test', 'path' => 'subdirectory/other.txt']); + } + + /** @test */ + public function it_imports_assets_with_only_assets_argument() + { + $container = tap(AssetContainer::make('test')->disk('test'))->save(); + $container->makeAsset('one.txt')->upload(UploadedFile::fake()->create('one.txt')); + $container->makeAsset('two.jpg')->upload(UploadedFile::fake()->image('two.jpg')); + $container->makeAsset('subdirectory/other.txt')->upload(UploadedFile::fake()->create('other.txt')); + + $this->assertCount(0, AssetContainerModel::all()); + $this->assertCount(0, AssetModel::all()); + + $this->artisan('statamic:eloquent:import-assets', ['--only-assets' => true]) + ->doesntExpectOutputToContain('Assets containers imported sucessfully.') + ->expectsOutputToContain('Assets imported sucessfully.') + ->assertExitCode(0); + + $this->assertCount(0, AssetContainerModel::all()); + $this->assertCount(3, AssetModel::all()); + + $this->assertDatabaseMissing('asset_containers', ['handle' => 'test', 'disk' => 'test']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'one.txt']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'two.jpg']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'subdirectory/other.txt']); + } + + /** @test */ + public function it_imports_assets_with_console_question() + { + $container = tap(AssetContainer::make('test')->disk('test'))->save(); + $container->makeAsset('one.txt')->upload(UploadedFile::fake()->create('one.txt')); + $container->makeAsset('two.jpg')->upload(UploadedFile::fake()->image('two.jpg')); + $container->makeAsset('subdirectory/other.txt')->upload(UploadedFile::fake()->create('other.txt')); + + $this->assertCount(0, AssetContainerModel::all()); + $this->assertCount(0, AssetModel::all()); + + $this->artisan('statamic:eloquent:import-assets') + ->expectsQuestion('Do you want to import asset containers?', false) + ->doesntExpectOutputToContain('Assets containers imported sucessfully.') + ->expectsQuestion('Do you want to import assets?', true) + ->expectsOutputToContain('Assets imported sucessfully.') + ->assertExitCode(0); + + $this->assertCount(0, AssetContainerModel::all()); + $this->assertCount(3, AssetModel::all()); + + $this->assertDatabaseMissing('asset_containers', ['handle' => 'test', 'disk' => 'test']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'one.txt']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'two.jpg']); + $this->assertDatabaseHas('assets_meta', ['container' => 'test', 'path' => 'subdirectory/other.txt']); + } +} diff --git a/tests/Commands/ImportBlueprintsTest.php b/tests/Commands/ImportBlueprintsTest.php new file mode 100644 index 00000000..1815bad8 --- /dev/null +++ b/tests/Commands/ImportBlueprintsTest.php @@ -0,0 +1,70 @@ +bind(Blueprint::class, Blueprint::class); + app()->bind(Fieldset::class, Fieldset::class); + + app()->bind(BlueprintRepository::class, function () { + return (new BlueprintRepository)->setDirectory(resource_path('blueprints')); + }); + app()->bind(FieldsetRepository::class, function () { + return (new FieldsetRepository)->setDirectory(resource_path('fieldsets')); + }); + + // Statamic will automatically generate a default blueprint. For the purpose of this test, we'll delete it. + BlueprintModel::all()->each->delete(); + } + + /** @test */ + public function it_imports_blueprints_and_fieldsets() + { + BlueprintFacade::make('user')->setContents([ + 'fields' => [ + ['handle' => 'name', 'field' => ['type' => 'text']], + ['handle' => 'email', 'field' => ['type' => 'text'], 'validate' => 'required'], + ], + ])->save(); + + FieldsetFacade::make('test')->setContents([ + 'fields' => [ + ['handle' => 'foo', 'field' => ['type' => 'text']], + ['handle' => 'bar', 'field' => ['type' => 'textarea', 'validate' => 'required']], + ], + ])->save(); + + $this->assertCount(0, BlueprintModel::all()); + $this->assertCount(0, FieldsetModel::all()); + + $this->artisan('statamic:eloquent:import-blueprints') + ->expectsOutputToContain('Blueprints imported successfully.') + ->expectsOutputToContain('Fieldsets imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, BlueprintModel::all()); + $this->assertCount(1, FieldsetModel::all()); + } +} diff --git a/tests/Commands/ImportCollectionsTest.php b/tests/Commands/ImportCollectionsTest.php new file mode 100644 index 00000000..2d73dcce --- /dev/null +++ b/tests/Commands/ImportCollectionsTest.php @@ -0,0 +1,213 @@ +bind(CollectionContract::class, \Statamic\Entries\Collection::class); + app()->bind(CollectionTreeContract::class, \Statamic\Structures\CollectionTree::class); + app()->bind(CollectionRepositoryContract::class, \Statamic\Stache\Repositories\CollectionRepository::class); + app()->bind(CollectionTreeRepositoryContract::class, \Statamic\Stache\Repositories\CollectionTreeRepository::class); + + app()->bind(EntryRepositoryContract::class, \Statamic\Stache\Repositories\EntryRepository::class); + app()->bind(EntryContract::class, \Statamic\Entries\Entry::class); + } + + /** @test */ + public function it_imports_collections_and_collection_trees() + { + $collection = tap(Collection::make('pages')->title('Pages'))->save(); + $collection->structure(new CollectionStructure)->save(); + + $entryA = tap(Entry::make()->collection($collection)->slug('foo'))->save(); + $entryB = tap(Entry::make()->collection($collection)->slug('foo'))->save(); + + $collection->structure()->in('en')->tree([ + ['entry' => $entryA->id()], + ['entry' => $entryB->id()], + ])->save(); + + $this->assertCount(0, CollectionModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-collections') + ->expectsQuestion('Do you want to import collections?', true) + ->expectsQuestion('Do you want to import collections trees?', true) + ->expectsOutputToContain('Collections imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, CollectionModel::all()); + $this->assertCount(1, TreeModel::all()); + + $this->assertDatabaseHas('collections', ['handle' => 'pages', 'title' => 'Pages']); + $this->assertDatabaseHas('trees', ['handle' => 'pages', 'type' => 'collection']); + } + + /** @test */ + public function it_imports_collections_and_collection_trees_with_force_argument() + { + $collection = tap(Collection::make('pages')->title('Pages'))->save(); + $collection->structure(new CollectionStructure)->save(); + + Entry::make()->collection($collection)->id('foo')->save(); + Entry::make()->collection($collection)->id('bar')->save(); + + $collection->structure()->in('en')->tree([ + ['entry' => 'foo'], + ['entry' => 'bar'], + ])->save(); + + $this->assertCount(0, CollectionModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-collections', ['--force' => true]) + ->expectsOutputToContain('Collections imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, CollectionModel::all()); + $this->assertCount(1, TreeModel::all()); + + $this->assertDatabaseHas('collections', ['handle' => 'pages', 'title' => 'Pages']); + $this->assertDatabaseHas('trees', ['handle' => 'pages', 'type' => 'collection']); + } + + /** @test */ + public function it_imports_collections_with_console_question() + { + $collection = tap(Collection::make('pages')->title('Pages'))->save(); + $collection->structure(new CollectionStructure)->save(); + + Entry::make()->collection($collection)->id('foo')->save(); + Entry::make()->collection($collection)->id('bar')->save(); + + $collection->structure()->in('en')->tree([ + ['entry' => 'foo'], + ['entry' => 'bar'], + ])->save(); + + $this->assertCount(0, CollectionModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-collections') + ->expectsQuestion('Do you want to import collections?', true) + ->expectsQuestion('Do you want to import collections trees?', false) + ->expectsOutputToContain('Collections imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, CollectionModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->assertDatabaseHas('collections', ['handle' => 'pages', 'title' => 'Pages']); + $this->assertDatabaseMissing('trees', ['handle' => 'pages', 'type' => 'collection']); + } + + /** @test */ + public function it_imports_collections_with_only_collections_argument() + { + $collection = tap(Collection::make('pages')->title('Pages'))->save(); + $collection->structure(new CollectionStructure)->save(); + + Entry::make()->collection($collection)->id('foo')->save(); + Entry::make()->collection($collection)->id('bar')->save(); + + $collection->structure()->in('en')->tree([ + ['entry' => 'foo'], + ['entry' => 'bar'], + ])->save(); + + $this->assertCount(0, CollectionModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-collections', ['--only-collections' => true]) + ->expectsOutputToContain('Collections imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, CollectionModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->assertDatabaseHas('collections', ['handle' => 'pages', 'title' => 'Pages']); + $this->assertDatabaseMissing('trees', ['handle' => 'pages', 'type' => 'collection']); + } + + /** @test */ + public function it_imports_collection_trees_with_console_question() + { + $collection = tap(Collection::make('pages')->title('Pages'))->save(); + $collection->structure(new CollectionStructure)->save(); + + Entry::make()->collection($collection)->id('foo')->save(); + Entry::make()->collection($collection)->id('bar')->save(); + + $collection->structure()->in('en')->tree([ + ['entry' => 'foo'], + ['entry' => 'bar'], + ])->save(); + + $this->assertCount(0, CollectionModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-collections') + ->expectsQuestion('Do you want to import collections?', false) + ->expectsQuestion('Do you want to import collections trees?', true) + ->expectsOutputToContain('Collections imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, CollectionModel::all()); + $this->assertCount(1, TreeModel::all()); + + $this->assertDatabaseMissing('collections', ['handle' => 'pages', 'title' => 'Pages']); + $this->assertDatabaseHas('trees', ['handle' => 'pages', 'type' => 'collection']); + } + + /** @test */ + public function it_imports_collection_trees_with_only_collections_argument() + { + $collection = tap(Collection::make('pages')->title('Pages'))->save(); + $collection->structure(new CollectionStructure)->save(); + + Entry::make()->collection($collection)->id('foo')->save(); + Entry::make()->collection($collection)->id('bar')->save(); + + $collection->structure()->in('en')->tree([ + ['entry' => 'foo'], + ['entry' => 'bar'], + ])->save(); + + $this->assertCount(0, CollectionModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-collections', ['--only-collection-trees' => true]) + ->expectsOutputToContain('Collections imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, CollectionModel::all()); + $this->assertCount(1, TreeModel::all()); + + $this->assertDatabaseMissing('collections', ['handle' => 'pages', 'title' => 'Pages']); + $this->assertDatabaseHas('trees', ['handle' => 'pages', 'type' => 'collection']); + } +} diff --git a/tests/Commands/ImportEntriesTest.php b/tests/Commands/ImportEntriesTest.php new file mode 100644 index 00000000..9d1869e3 --- /dev/null +++ b/tests/Commands/ImportEntriesTest.php @@ -0,0 +1,84 @@ +shouldUseStringEntryIds = true; + + parent::setUp(); + + Facade::clearResolvedInstance(CollectionRepositoryContract::class); + Facade::clearResolvedInstance(CollectionTreeRepositoryContract::class); + + app()->bind(CollectionContract::class, \Statamic\Entries\Collection::class); + app()->bind(CollectionTreeContract::class, \Statamic\Structures\CollectionTree::class); + app()->bind(CollectionRepositoryContract::class, \Statamic\Stache\Repositories\CollectionRepository::class); + app()->bind(CollectionTreeRepositoryContract::class, \Statamic\Stache\Repositories\CollectionTreeRepository::class); + + app()->bind(EntryRepositoryContract::class, \Statamic\Stache\Repositories\EntryRepository::class); + app()->bind(EntryContract::class, \Statamic\Entries\Entry::class); + } + + /** @test */ + public function it_imports_entries() + { + $collection = tap(Collection::make('pages')->title('Pages'))->save(); + Entry::make()->collection($collection)->slug('foo')->data(['foo' => 'bar'])->save(); + + $this->assertCount(0, EntryModel::all()); + + $this->artisan('statamic:eloquent:import-entries') + ->expectsOutputToContain('Entries imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, EntryModel::all()); + + $this->assertDatabaseHas('entries', ['collection' => 'pages', 'slug' => 'foo', 'data' => '{"foo":"bar"}']); + } + + /** @test */ + public function it_imports_localized_entries() + { + Site::setSites([ + 'en' => ['url' => 'http://localhost/', 'locale' => 'en'], + 'fr' => ['url' => 'http://localhost/fr/', 'locale' => 'fr'], + ]); + + $collection = tap(Collection::make('pages')->title('Pages'))->save(); + + $originEntry = tap(Entry::make()->collection($collection)->slug('foo')->data(['foo' => 'bar']))->save(); + $originEntry->makeLocalization('fr')->data(['baz' => 'qux'])->save(); + + $this->assertCount(0, EntryModel::all()); + + $this->artisan('statamic:eloquent:import-entries') + ->expectsOutputToContain('Importing origin entries...') + ->expectsOutputToContain('Importing localized entries...') + ->expectsOutputToContain('Entries imported successfully.') + ->assertExitCode(0); + + $this->assertCount(2, EntryModel::all()); + + $this->assertDatabaseHas('entries', ['collection' => 'pages', 'site' => 'en', 'slug' => 'foo', 'data' => '{"foo":"bar"}']); + $this->assertDatabaseHas('entries', ['collection' => 'pages', 'site' => 'fr', 'slug' => 'foo', 'data' => '{"foo":"bar","baz":"qux","__localized_fields":[]}']); + } +} diff --git a/tests/Commands/ImportFormsTest.php b/tests/Commands/ImportFormsTest.php new file mode 100644 index 00000000..864d18f3 --- /dev/null +++ b/tests/Commands/ImportFormsTest.php @@ -0,0 +1,183 @@ +bind(FormContract::class, \Statamic\Forms\Form::class); + app()->bind(SubmissionContract::class, \Statamic\Forms\Submission::class); + app()->bind(FormRepositoryContract::class, \Statamic\Forms\FormRepository::class); + app()->bind(SubmissionRepositoryContract::class, \Statamic\Stache\Repositories\SubmissionRepository::class); + app()->bind(\Statamic\Eloquent\Forms\SubmissionQueryBuilder::class, \Statamic\Stache\Query\SubmissionQueryBuilder::class); + } + + /** @test */ + public function it_imports_forms_and_submissions() + { + $form = tap(Form::make('contact')->title('Contact')->store(true))->save(); + $form->makeSubmission()->data(['name' => 'Jack'])->save(); + $form->makeSubmission()->data(['name' => 'Jason'])->save(); + $form->makeSubmission()->data(['name' => 'Jesse'])->save(); + + $this->assertCount(0, FormModel::all()); + $this->assertCount(0, SubmissionModel::all()); + + $this->artisan('statamic:eloquent:import-forms') + ->expectsQuestion('Do you want to import forms?', true) + ->expectsQuestion('Do you want to import form submissions?', true) + ->expectsOutputToContain('Forms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, FormModel::all()); + $this->assertCount(3, SubmissionModel::all()); + + $this->assertDatabaseHas('forms', ['handle' => 'contact', 'title' => 'Contact']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jack"}']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jason"}']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jesse"}']); + } + + /** @test */ + public function it_imports_forms_and_submissions_with_force_argument() + { + $form = tap(Form::make('contact')->title('Contact')->store(true))->save(); + $form->makeSubmission()->data(['name' => 'Jack'])->save(); + $form->makeSubmission()->data(['name' => 'Jason'])->save(); + $form->makeSubmission()->data(['name' => 'Jesse'])->save(); + + $this->assertCount(0, FormModel::all()); + $this->assertCount(0, SubmissionModel::all()); + + $this->artisan('statamic:eloquent:import-forms', ['--force' => true]) + ->expectsOutputToContain('Forms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, FormModel::all()); + $this->assertCount(3, SubmissionModel::all()); + + $this->assertDatabaseHas('forms', ['handle' => 'contact', 'title' => 'Contact']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jack"}']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jason"}']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jesse"}']); + } + + /** @test */ + public function it_imports_only_forms_with_only_forms_argument() + { + $form = tap(Form::make('contact')->title('Contact')->store(true))->save(); + $form->makeSubmission()->data(['name' => 'Jack'])->save(); + $form->makeSubmission()->data(['name' => 'Jason'])->save(); + $form->makeSubmission()->data(['name' => 'Jesse'])->save(); + + $this->assertCount(0, FormModel::all()); + $this->assertCount(0, SubmissionModel::all()); + + $this->artisan('statamic:eloquent:import-forms', ['--only-forms' => true]) + ->expectsOutputToContain('Forms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, FormModel::all()); + $this->assertCount(0, SubmissionModel::all()); + + $this->assertDatabaseHas('forms', ['handle' => 'contact', 'title' => 'Contact']); + $this->assertDatabaseMissing('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jack"}']); + $this->assertDatabaseMissing('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jason"}']); + $this->assertDatabaseMissing('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jesse"}']); + } + + /** @test */ + public function it_imports_only_forms_with_console_question() + { + $form = tap(\Statamic\Facades\Form::make('contact')->title('Contact')->store(true))->save(); + $form->makeSubmission()->data(['name' => 'Jack'])->save(); + $form->makeSubmission()->data(['name' => 'Jason'])->save(); + $form->makeSubmission()->data(['name' => 'Jesse'])->save(); + + $this->assertCount(0, FormModel::all()); + $this->assertCount(0, SubmissionModel::all()); + + $this->artisan('statamic:eloquent:import-forms') + ->expectsQuestion('Do you want to import forms?', true) + ->expectsQuestion('Do you want to import form submissions?', false) + ->expectsOutputToContain('Forms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, FormModel::all()); + $this->assertCount(0, SubmissionModel::all()); + + $this->assertDatabaseHas('forms', ['handle' => 'contact', 'title' => 'Contact']); + $this->assertDatabaseMissing('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jack"}']); + $this->assertDatabaseMissing('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jason"}']); + $this->assertDatabaseMissing('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jesse"}']); + } + + /** @test */ + public function it_imports_only_submissions_with_only_form_submissions_argument() + { + $form = tap(Form::make('contact')->title('Contact')->store(true))->save(); + $form->makeSubmission()->data(['name' => 'Jack'])->save(); + $form->makeSubmission()->data(['name' => 'Jason'])->save(); + $form->makeSubmission()->data(['name' => 'Jesse'])->save(); + + $this->assertCount(0, FormModel::all()); + $this->assertCount(0, SubmissionModel::all()); + + $this->artisan('statamic:eloquent:import-forms', ['--only-form-submissions' => true]) + ->expectsOutputToContain('Forms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, FormModel::all()); + $this->assertCount(3, SubmissionModel::all()); + + $this->assertDatabaseMissing('forms', ['handle' => 'contact', 'title' => 'Contact']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jack"}']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jason"}']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jesse"}']); + } + + /** @test */ + public function it_imports_only_form_submissions_with_console_question() + { + $form = tap(\Statamic\Facades\Form::make('contact')->title('Contact')->store(true))->save(); + $form->makeSubmission()->data(['name' => 'Jack'])->save(); + $form->makeSubmission()->data(['name' => 'Jason'])->save(); + $form->makeSubmission()->data(['name' => 'Jesse'])->save(); + + $this->assertCount(0, FormModel::all()); + $this->assertCount(0, SubmissionModel::all()); + + $this->artisan('statamic:eloquent:import-forms') + ->expectsQuestion('Do you want to import forms?', false) + ->expectsQuestion('Do you want to import form submissions?', true) + ->expectsOutputToContain('Forms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, FormModel::all()); + $this->assertCount(3, SubmissionModel::all()); + + $this->assertDatabaseMissing('forms', ['handle' => 'contact', 'title' => 'Contact']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jack"}']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jason"}']); + $this->assertDatabaseHas('form_submissions', ['form' => 'contact', 'data' => '{"name":"Jesse"}']); + } +} diff --git a/tests/Commands/ImportGlobalsTest.php b/tests/Commands/ImportGlobalsTest.php new file mode 100644 index 00000000..c994b6bb --- /dev/null +++ b/tests/Commands/ImportGlobalsTest.php @@ -0,0 +1,164 @@ +bind(GlobalSetContract::class, \Statamic\Globals\GlobalSet::class); + app()->bind(VariablesContract::class, \Statamic\Globals\Variables::class); + app()->bind(GlobalRepositoryContract::class, \Statamic\Stache\Repositories\GlobalRepository::class); + app()->bind(GlobalVariablesRepositoryContract::class, \Statamic\Stache\Repositories\GlobalVariablesRepository::class); + } + + /** @test */ + public function it_imports_global_sets_and_variables() + { + $globalSet = tap(GlobalSet::make('footer')->title('Footer'))->save(); + $variables = $globalSet->makeLocalization('en')->data(['foo' => 'bar']); + $globalSet->addLocalization($variables)->save(); + + $this->assertCount(0, GlobalSetModel::all()); + $this->assertCount(0, VariablesModel::all()); + + $this->artisan('statamic:eloquent:import-globals') + ->expectsQuestion('Do you want to import global sets?', true) + ->expectsQuestion('Do you want to import global variables?', true) + ->expectsOutputToContain('Globals imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, GlobalSetModel::all()); + $this->assertCount(1, VariablesModel::all()); + + $this->assertDatabaseHas('global_sets', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseHas('global_set_variables', ['handle' => 'footer', 'locale' => 'en', 'data' => '{"foo":"bar"}']); + } + + /** @test */ + public function it_imports_global_sets_and_variables_with_force_argument() + { + $globalSet = tap(GlobalSet::make('footer')->title('Footer'))->save(); + $variables = $globalSet->makeLocalization('en')->data(['foo' => 'bar']); + $globalSet->addLocalization($variables)->save(); + + $this->assertCount(0, GlobalSetModel::all()); + $this->assertCount(0, VariablesModel::all()); + + $this->artisan('statamic:eloquent:import-globals', ['--force' => true]) + ->expectsOutputToContain('Globals imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, GlobalSetModel::all()); + $this->assertCount(1, VariablesModel::all()); + + $this->assertDatabaseHas('global_sets', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseHas('global_set_variables', ['handle' => 'footer', 'locale' => 'en', 'data' => '{"foo":"bar"}']); + } + + /** @test */ + public function it_imports_only_global_sets_with_console_question() + { + $globalSet = tap(GlobalSet::make('footer')->title('Footer'))->save(); + $variables = $globalSet->makeLocalization('en')->data(['foo' => 'bar']); + $globalSet->addLocalization($variables)->save(); + + $this->assertCount(0, GlobalSetModel::all()); + $this->assertCount(0, VariablesModel::all()); + + $this->artisan('statamic:eloquent:import-globals') + ->expectsQuestion('Do you want to import global sets?', true) + ->expectsQuestion('Do you want to import global variables?', false) + ->expectsOutputToContain('Globals imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, GlobalSetModel::all()); + $this->assertCount(0, VariablesModel::all()); + + $this->assertDatabaseHas('global_sets', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseMissing('global_set_variables', ['handle' => 'footer', 'locale' => 'en', 'data' => '{"foo":"bar"}']); + } + + /** @test */ + public function it_imports_only_global_sets_with_only_global_sets_argument() + { + $globalSet = tap(GlobalSet::make('footer')->title('Footer'))->save(); + $variables = $globalSet->makeLocalization('en')->data(['foo' => 'bar']); + $globalSet->addLocalization($variables)->save(); + + $this->assertCount(0, GlobalSetModel::all()); + $this->assertCount(0, VariablesModel::all()); + + $this->artisan('statamic:eloquent:import-globals', ['--only-global-sets' => true]) + ->expectsOutputToContain('Globals imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, GlobalSetModel::all()); + $this->assertCount(0, VariablesModel::all()); + + $this->assertDatabaseHas('global_sets', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseMissing('global_set_variables', ['handle' => 'footer', 'locale' => 'en', 'data' => '{"foo":"bar"}']); + } + + /** @test */ + public function it_imports_only_variables_with_console_question() + { + $globalSet = tap(GlobalSet::make('footer')->title('Footer'))->save(); + $variables = $globalSet->makeLocalization('en')->data(['foo' => 'bar']); + $globalSet->addLocalization($variables)->save(); + + $this->assertCount(0, GlobalSetModel::all()); + $this->assertCount(0, VariablesModel::all()); + + $this->artisan('statamic:eloquent:import-globals') + ->expectsQuestion('Do you want to import global sets?', false) + ->expectsQuestion('Do you want to import global variables?', true) + ->expectsOutputToContain('Globals imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, GlobalSetModel::all()); + $this->assertCount(1, VariablesModel::all()); + + $this->assertDatabaseMissing('global_sets', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseHas('global_set_variables', ['handle' => 'footer', 'locale' => 'en', 'data' => '{"foo":"bar"}']); + } + + /** @test */ + public function it_imports_only_variables_with_only_global_variables_argument() + { + $globalSet = tap(GlobalSet::make('footer')->title('Footer'))->save(); + $variables = $globalSet->makeLocalization('en')->data(['foo' => 'bar']); + $globalSet->addLocalization($variables)->save(); + + $this->assertCount(0, GlobalSetModel::all()); + $this->assertCount(0, VariablesModel::all()); + + $this->artisan('statamic:eloquent:import-globals', ['--only-global-variables' => true]) + ->expectsOutputToContain('Globals imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, GlobalSetModel::all()); + $this->assertCount(1, VariablesModel::all()); + + $this->assertDatabaseMissing('global_sets', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseHas('global_set_variables', ['handle' => 'footer', 'locale' => 'en', 'data' => '{"foo":"bar"}']); + } +} diff --git a/tests/Commands/ImportNavsTest.php b/tests/Commands/ImportNavsTest.php new file mode 100644 index 00000000..b8865660 --- /dev/null +++ b/tests/Commands/ImportNavsTest.php @@ -0,0 +1,176 @@ +bind(NavContract::class, \Statamic\Structures\Nav::class); + app()->bind(NavTreeContract::class, \Statamic\Structures\NavTree::class); + app()->bind(NavigationRepositoryContract::class, \Statamic\Stache\Repositories\NavigationRepository::class); + app()->bind(NavTreeRepositoryContract::class, \Statamic\Stache\Repositories\NavTreeRepository::class); + } + + /** @test */ + public function it_imports_navs_and_nav_trees() + { + $nav = tap(Nav::make('footer')->title('Footer'))->save(); + $nav->makeTree('en', [ + ['id' => 'a', 'url' => 'https://statamic.com'], + ['id' => 'a', 'url' => 'https://wilderborn.com'], + ])->save(); + + $this->assertCount(0, NavModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-navs') + ->expectsQuestion('Do you want to import navs?', true) + ->expectsQuestion('Do you want to import nav trees?', true) + ->expectsOutputToContain('Navs imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, NavModel::all()); + $this->assertCount(1, TreeModel::all()); + + $this->assertDatabaseHas('navigations', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseHas('trees', ['handle' => 'footer', 'type' => 'navigation']); + } + + /** @test */ + public function it_imports_navs_and_nav_trees_with_force_argument() + { + $nav = tap(Nav::make('footer')->title('Footer'))->save(); + $nav->makeTree('en', [ + ['id' => 'a', 'url' => 'https://statamic.com'], + ['id' => 'a', 'url' => 'https://wilderborn.com'], + ])->save(); + + $this->assertCount(0, NavModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-navs', ['--force' => true]) + ->expectsOutputToContain('Navs imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, NavModel::all()); + $this->assertCount(1, TreeModel::all()); + + $this->assertDatabaseHas('navigations', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseHas('trees', ['handle' => 'footer', 'type' => 'navigation']); + } + + /** @test */ + public function it_imports_navs_with_console_question() + { + $nav = tap(Nav::make('footer')->title('Footer'))->save(); + $nav->makeTree('en', [ + ['id' => 'a', 'url' => 'https://statamic.com'], + ['id' => 'a', 'url' => 'https://wilderborn.com'], + ])->save(); + + $this->assertCount(0, NavModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-navs') + ->expectsQuestion('Do you want to import navs?', true) + ->expectsQuestion('Do you want to import nav trees?', false) + ->expectsOutputToContain('Navs imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, NavModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->assertDatabaseHas('navigations', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseMissing('trees', ['handle' => 'footer', 'type' => 'navigation']); + } + + /** @test */ + public function it_imports_navs_with_only_navs_argument() + { + $nav = tap(Nav::make('footer')->title('Footer'))->save(); + $nav->makeTree('en', [ + ['id' => 'a', 'url' => 'https://statamic.com'], + ['id' => 'a', 'url' => 'https://wilderborn.com'], + ])->save(); + + $this->assertCount(0, NavModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-navs', ['--only-navs' => true]) + ->expectsOutputToContain('Navs imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, NavModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->assertDatabaseHas('navigations', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseMissing('trees', ['handle' => 'footer', 'type' => 'navigation']); + } + + /** @test */ + public function it_imports_nav_trees_with_console_question() + { + $nav = tap(Nav::make('footer')->title('Footer'))->save(); + $nav->makeTree('en', [ + ['id' => 'a', 'url' => 'https://statamic.com'], + ['id' => 'a', 'url' => 'https://wilderborn.com'], + ])->save(); + + $this->assertCount(0, NavModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-navs') + ->expectsQuestion('Do you want to import navs?', false) + ->expectsQuestion('Do you want to import nav trees?', true) + ->expectsOutputToContain('Navs imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, NavModel::all()); + $this->assertCount(1, TreeModel::all()); + + $this->assertDatabaseMissing('navigations', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseHas('trees', ['handle' => 'footer', 'type' => 'navigation']); + } + + /** @test */ + public function it_imports_nav_trees_with_only_nav_trees_argument() + { + $nav = tap(Nav::make('footer')->title('Footer'))->save(); + $nav->makeTree('en', [ + ['id' => 'a', 'url' => 'https://statamic.com'], + ['id' => 'a', 'url' => 'https://wilderborn.com'], + ])->save(); + + $this->assertCount(0, NavModel::all()); + $this->assertCount(0, TreeModel::all()); + + $this->artisan('statamic:eloquent:import-navs', ['--only-nav-trees' => true]) + ->expectsOutputToContain('Navs imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, NavModel::all()); + $this->assertCount(1, TreeModel::all()); + + $this->assertDatabaseMissing('navigations', ['handle' => 'footer', 'title' => 'Footer']); + $this->assertDatabaseHas('trees', ['handle' => 'footer', 'type' => 'navigation']); + } +} diff --git a/tests/Commands/ImportRevisionsTest.php b/tests/Commands/ImportRevisionsTest.php new file mode 100644 index 00000000..bac1ba42 --- /dev/null +++ b/tests/Commands/ImportRevisionsTest.php @@ -0,0 +1,77 @@ +set('statamic.revisions', [ + 'enabled' => true, + 'path' => __DIR__.'/tmp', + ]); + + mkdir(__DIR__.'/tmp'); + + Facade::clearResolvedInstance(RevisionRepositoryContract::class); + + app()->bind(RevisionRepositoryContract::class, \Statamic\Revisions\RevisionRepository::class); + app()->bind(RevisionContract::class, \Statamic\Revisions\Revision::class); + } + + public function tearDown(): void + { + app('files')->deleteDirectory(__DIR__.'/tmp'); + + parent::tearDown(); + } + + /** @test */ + public function it_cannot_import_revisions_when_feature_is_disabled() + { + config(['statamic.revisions.enabled' => false]); + + $this->artisan('statamic:eloquent:import-revisions') + ->expectsOutputToContain('This import can only be run when revisions are enabled.'); + } + + /** @test */ + public function it_imports_revisions() + { + Revision::make() + ->key('collections/pages/en/foo') + ->action('revision') + ->date(Carbon::now()) + ->message('Initial revision') + ->attributes(['foo' => 'bar']) + ->save(); + + $this->assertCount(0, RevisionModel::all()); + + $this->artisan('statamic:eloquent:import-revisions') + ->expectsOutputToContain('Revisions imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, RevisionModel::all()); + + $this->assertDatabaseHas('revisions', [ + 'key' => 'collections/pages/en/foo', + 'action' => 'revision', + 'message' => 'Initial revision', + 'attributes' => '{"foo":"bar"}', + ]); + } +} diff --git a/tests/Commands/ImportTaxonomiesTest.php b/tests/Commands/ImportTaxonomiesTest.php new file mode 100644 index 00000000..6c4d2e9d --- /dev/null +++ b/tests/Commands/ImportTaxonomiesTest.php @@ -0,0 +1,189 @@ +bind(TaxonomyContract::class, \Statamic\Taxonomies\Taxonomy::class); + app()->bind(TermContract::class, \Statamic\Taxonomies\Term::class); + app()->bind(TaxonomyRepositoryContract::class, \Statamic\Stache\Repositories\TaxonomyRepository::class); + app()->bind(TermRepositoryContract::class, \Statamic\Stache\Repositories\TermRepository::class); + } + + /** @test */ + public function it_imports_taxonomies_and_terms() + { + Taxonomy::make('tags')->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('alfa')->data(['title' => 'Alfa'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('bravo')->data(['title' => 'Bravo'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('charlie')->data(['title' => 'Charlie'])->save(); + + $this->assertCount(0, TaxonomyModel::all()); + $this->assertCount(0, TermModel::all()); + + $this->artisan('statamic:eloquent:import-taxonomies') + ->expectsQuestion('Do you want to import taxonomies?', true) + ->expectsOutputToContain('Taxonomies imported successfully.') + ->expectsQuestion('Do you want to import terms?', true) + ->expectsOutputToContain('Terms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, TaxonomyModel::all()); + $this->assertCount(3, TermModel::all()); + + $this->assertDatabaseHas('taxonomies', ['handle' => 'tags']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'alfa']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'bravo']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'charlie']); + } + + /** @test */ + public function it_imports_taxonomies_and_terms_with_force_argument() + { + Taxonomy::make('tags')->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('alfa')->data(['title' => 'Alfa'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('bravo')->data(['title' => 'Bravo'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('charlie')->data(['title' => 'Charlie'])->save(); + + $this->assertCount(0, TaxonomyModel::all()); + $this->assertCount(0, TermModel::all()); + + $this->artisan('statamic:eloquent:import-taxonomies', ['--force' => true]) + ->expectsOutputToContain('Taxonomies imported successfully.') + ->expectsOutputToContain('Terms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, TaxonomyModel::all()); + $this->assertCount(3, TermModel::all()); + + $this->assertDatabaseHas('taxonomies', ['handle' => 'tags']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'alfa']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'bravo']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'charlie']); + } + + /** @test */ + public function it_imports_taxonomies_with_console_question() + { + Taxonomy::make('tags')->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('alfa')->data(['title' => 'Alfa'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('bravo')->data(['title' => 'Bravo'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('charlie')->data(['title' => 'Charlie'])->save(); + + $this->assertCount(0, TaxonomyModel::all()); + $this->assertCount(0, TermModel::all()); + + $this->artisan('statamic:eloquent:import-taxonomies') + ->expectsQuestion('Do you want to import taxonomies?', true) + ->expectsOutputToContain('Taxonomies imported successfully.') + ->expectsQuestion('Do you want to import terms?', false) + ->doesntExpectOutputToContain('Terms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, TaxonomyModel::all()); + $this->assertCount(0, TermModel::all()); + + $this->assertDatabaseHas('taxonomies', ['handle' => 'tags']); + $this->assertDatabaseMissing('taxonomy_terms', ['slug' => 'alfa']); + $this->assertDatabaseMissing('taxonomy_terms', ['slug' => 'bravo']); + $this->assertDatabaseMissing('taxonomy_terms', ['slug' => 'charlie']); + } + + /** @test */ + public function it_imports_taxonomies_with_only_taxonomies_argument() + { + Taxonomy::make('tags')->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('alfa')->data(['title' => 'Alfa'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('bravo')->data(['title' => 'Bravo'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('charlie')->data(['title' => 'Charlie'])->save(); + + $this->assertCount(0, TaxonomyModel::all()); + $this->assertCount(0, TermModel::all()); + + $this->artisan('statamic:eloquent:import-taxonomies', ['--only-taxonomies' => true]) + ->expectsOutputToContain('Taxonomies imported successfully.') + ->doesntExpectOutputToContain('Terms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, TaxonomyModel::all()); + $this->assertCount(0, TermModel::all()); + + $this->assertDatabaseHas('taxonomies', ['handle' => 'tags']); + $this->assertDatabaseMissing('taxonomy_terms', ['slug' => 'alfa']); + $this->assertDatabaseMissing('taxonomy_terms', ['slug' => 'bravo']); + $this->assertDatabaseMissing('taxonomy_terms', ['slug' => 'charlie']); + } + + /** @test */ + public function it_imports_terms_with_console_question() + { + Taxonomy::make('tags')->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('alfa')->data(['title' => 'Alfa'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('bravo')->data(['title' => 'Bravo'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('charlie')->data(['title' => 'Charlie'])->save(); + + $this->assertCount(0, TaxonomyModel::all()); + $this->assertCount(0, TermModel::all()); + + $this->artisan('statamic:eloquent:import-taxonomies') + ->expectsQuestion('Do you want to import taxonomies?', false) + ->doesntExpectOutputToContain('Taxonomies imported successfully.') + ->expectsQuestion('Do you want to import terms?', true) + ->expectsOutputToContain('Terms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, TaxonomyModel::all()); + $this->assertCount(3, TermModel::all()); + + $this->assertDatabaseMissing('taxonomies', ['handle' => 'tags']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'alfa']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'bravo']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'charlie']); + } + + /** @test */ + public function it_imports_terms_with_only_terms_argument() + { + Taxonomy::make('tags')->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('alfa')->data(['title' => 'Alfa'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('bravo')->data(['title' => 'Bravo'])->save(); + Term::make()->taxonomy('tags')->inDefaultLocale()->slug('charlie')->data(['title' => 'Charlie'])->save(); + + $this->assertCount(0, TaxonomyModel::all()); + $this->assertCount(0, TermModel::all()); + + $this->artisan('statamic:eloquent:import-taxonomies', ['--only-terms' => true]) + ->doesntExpectOutputToContain('Taxonomies imported successfully.') + ->expectsOutputToContain('Terms imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, TaxonomyModel::all()); + $this->assertCount(3, TermModel::all()); + + $this->assertDatabaseMissing('taxonomies', ['handle' => 'tags']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'alfa']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'bravo']); + $this->assertDatabaseHas('taxonomy_terms', ['slug' => 'charlie']); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 3137ab19..d733b8ec 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -13,10 +13,6 @@ abstract class TestCase extends AddonTestCase protected string $addonServiceProvider = ServiceProvider::class; - protected $shouldFakeVersion = true; - - protected $shouldPreventNavBeingBuilt = true; - protected $shouldUseStringEntryIds = false; protected function resolveApplicationConfiguration($app) diff --git a/tests/__fixtures__/dev-null/.gitkeep b/tests/__fixtures__/dev-null/.gitkeep new file mode 100644 index 00000000..e69de29b