diff --git a/.gitignore b/.gitignore index bd3fea95..930180af 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ vendor .php_cs.cache .nova/* .phpunit.cache +.idea diff --git a/config/eloquent-driver.php b/config/eloquent-driver.php index a7a6baf2..a50c3c1e 100644 --- a/config/eloquent-driver.php +++ b/config/eloquent-driver.php @@ -18,8 +18,7 @@ 'blueprints' => [ 'driver' => 'file', - 'blueprint_model' => \Statamic\Eloquent\Fields\BlueprintModel::class, - 'fieldset_model' => \Statamic\Eloquent\Fields\FieldsetModel::class, + 'model' => \Statamic\Eloquent\Fields\BlueprintModel::class, 'namespaces' => 'all', ], @@ -42,6 +41,11 @@ 'entry' => \Statamic\Eloquent\Entries\Entry::class, ], + 'fieldsets' => [ + 'driver' => 'file', + 'model' => \Statamic\Eloquent\Fields\FieldsetModel::class, + ], + 'forms' => [ 'driver' => 'file', 'model' => \Statamic\Eloquent\Forms\FormModel::class, diff --git a/src/Commands/ImportBlueprints.php b/src/Commands/ImportBlueprints.php index abc86a4d..147fbca7 100644 --- a/src/Commands/ImportBlueprints.php +++ b/src/Commands/ImportBlueprints.php @@ -22,7 +22,10 @@ class ImportBlueprints extends Command * * @var string */ - protected $signature = 'statamic:eloquent:import-blueprints'; + protected $signature = 'statamic:eloquent:import-blueprints + {--force : Force the import to run, with all prompts answered "yes"} + {--only-blueprints : Only import blueprints} + {--only-fieldsets : Only import fieldsets}'; /** * The console command description. @@ -62,6 +65,10 @@ private function useDefaultRepositories(): void private function importBlueprints(): void { + if (! $this->shouldImportBlueprints()) { + return; + } + $directory = resource_path('blueprints'); $files = File::withAbsolutePaths() @@ -105,7 +112,7 @@ private function importBlueprints(): void $lastModified = Carbon::createFromTimestamp(File::lastModified($path)); - app('statamic.eloquent.blueprints.blueprint_model')::firstOrNew([ + app('statamic.eloquent.blueprints.model')::firstOrNew([ 'handle' => $blueprint->handle(), 'namespace' => $blueprint->namespace() ?? null, ]) @@ -118,6 +125,10 @@ private function importBlueprints(): void private function importFieldsets(): void { + if (! $this->shouldImportFieldsets()) { + return; + } + $directory = resource_path('fieldsets'); $files = File::withAbsolutePaths() @@ -132,7 +143,7 @@ private function importFieldsets(): void $lastModified = Carbon::createFromTimestamp(File::lastModified($path)); - app('statamic.eloquent.blueprints.fieldset_model')::firstOrNew([ + app('statamic.eloquent.fieldsets.model')::firstOrNew([ 'handle' => $fieldset->handle(), ]) ->fill(['data' => $fieldset->contents(), 'created_at' => $lastModified, 'updated_at' => $lastModified]) @@ -152,4 +163,18 @@ private function getNamespaceAndHandle(string $blueprint): array return [$namespace, $handle]; } + + private function shouldImportBlueprints(): bool + { + return $this->option('only-blueprints') + || ! $this->option('only-fieldsets') + && ($this->option('force') || $this->confirm('Do you want to import blueprints?')); + } + + private function shouldImportFieldsets(): bool + { + return $this->option('only-fieldsets') + || ! $this->option('only-blueprints') + && ($this->option('force') || $this->confirm('Do you want to import fieldsets?')); + } } diff --git a/src/Fields/BlueprintRepository.php b/src/Fields/BlueprintRepository.php index c6f45716..f937b2ad 100644 --- a/src/Fields/BlueprintRepository.php +++ b/src/Fields/BlueprintRepository.php @@ -25,14 +25,14 @@ public function find($blueprint): ?Blueprint return parent::find($blueprint); } - $blueprintModel = ($namespace ? $this->filesIn($namespace) : app('statamic.eloquent.blueprints.blueprint_model')::whereNull('namespace')) + $blueprintModel = ($namespace ? $this->filesIn($namespace) : app('statamic.eloquent.blueprints.model')::whereNull('namespace')) ->where('handle', $handle) ->first(); if (! $blueprintModel) { throw_if( $namespace === null && $handle === 'default', - Exception::class, + \Exception::class, 'Default Blueprint is required but not found. ' ); @@ -96,10 +96,14 @@ public function in(string $namespace) protected function filesIn($namespace) { + if (! $this->isEloquentDrivenNamespace($namespace)) { + return parent::filesIn($namespace); + } + return Blink::store(self::BLINK_NAMESPACE_PATHS)->once($namespace ?? 'none', function () use ($namespace) { $namespace = str_replace('/', '.', $namespace); - if (count($blueprintModels = app('statamic.eloquent.blueprints.blueprint_model')::where('namespace', $namespace)->get()) == 0) { + if (count($blueprintModels = app('statamic.eloquent.blueprints.model')::where('namespace', $namespace)->get()) == 0) { return collect(); } @@ -138,14 +142,14 @@ protected function getNamespaceAndHandle($blueprint) public function getModel($blueprint) { - return app('statamic.eloquent.blueprints.blueprint_model')::where('namespace', $blueprint->namespace() ?? null) + return app('statamic.eloquent.blueprints.model')::where('namespace', $blueprint->namespace() ?? null) ->where('handle', $blueprint->handle()) ->first(); } public function updateModel($blueprint) { - $model = app('statamic.eloquent.blueprints.blueprint_model')::firstOrNew([ + $model = app('statamic.eloquent.blueprints.model')::firstOrNew([ 'handle' => $blueprint->handle(), 'namespace' => $blueprint->namespace() ?? null, ]); diff --git a/src/Fields/FieldsetRepository.php b/src/Fields/FieldsetRepository.php index 8c054b8f..1c7c0f16 100644 --- a/src/Fields/FieldsetRepository.php +++ b/src/Fields/FieldsetRepository.php @@ -12,7 +12,7 @@ class FieldsetRepository extends StacheRepository public function all(): Collection { return Blink::once('eloquent-fieldsets', function () { - if (count($models = app('statamic.eloquent.blueprints.fieldset_model')::get() ?? collect()) === 0) { + if (count($models = app('statamic.eloquent.fieldsets.model')::get() ?? collect()) === 0) { return collect(); } @@ -47,7 +47,7 @@ public function delete(Fieldset $fieldset) public function updateModel($fieldset) { - $model = app('statamic.eloquent.blueprints.fieldset_model')::firstOrNew([ + $model = app('statamic.eloquent.fieldsets.model')::firstOrNew([ 'handle' => $fieldset->handle(), ]); @@ -59,7 +59,7 @@ public function updateModel($fieldset) public function deleteModel($fieldset) { - $model = app('statamic.eloquent.blueprints.fieldset_model')::where('handle', $fieldset->handle())->first(); + $model = app('statamic.eloquent.fieldsets.model')::where('handle', $fieldset->handle())->first(); if ($model) { $model->delete(); diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 122cab4c..c2e8a806 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -138,9 +138,12 @@ private function publishMigrations(): void $this->publishes($blueprintMigrations = [ __DIR__.'/../database/migrations/2024_03_07_100000_create_blueprints_table.php' => database_path('migrations/2024_03_07_100000_create_blueprints_table.php'), - __DIR__.'/../database/migrations/2024_03_07_100000_create_fieldsets_table.php' => database_path('migrations/2024_03_07_100000_create_fieldsets_table.php'), ], 'statamic-eloquent-blueprint-migrations'); + $this->publishes($fieldsetMigrations = [ + __DIR__.'/../database/migrations/2024_03_07_100000_create_fieldsets_table.php' => database_path('migrations/2024_03_07_100000_create_fieldsets_table.php'), + ], 'statamic-eloquent-fieldset-migrations'); + $this->publishes($formMigrations = [ __DIR__.'/../database/migrations/2024_03_07_100000_create_forms_table.php' => database_path('migrations/2024_03_07_100000_create_forms_table.php'), ], 'statamic-eloquent-form-migrations'); @@ -176,6 +179,7 @@ private function publishMigrations(): void $navigationTreeMigrations, $collectionMigrations, $blueprintMigrations, + $fieldsetMigrations, $formMigrations, $formSubmissionMigrations, $assetContainerMigrations, @@ -203,6 +207,7 @@ public function register() $this->registerCollections(); $this->registerCollectionTrees(); $this->registerEntries(); + $this->registerFieldsets(); $this->registerForms(); $this->registerFormSubmissions(); $this->registerGlobals(); @@ -267,23 +272,19 @@ private function registerBlueprints() return; } - $this->app->bind('statamic.eloquent.blueprints.blueprint_model', function () { - return config('statamic.eloquent-driver.blueprints.blueprint_model'); + $this->app->bind('statamic.eloquent.blueprints.model', function () { + return config('statamic.eloquent-driver.blueprints.model', config('statamic.eloquent-driver.blueprints.blueprint_model')); }); - $this->app->bind('statamic.eloquent.blueprints.fieldset_model', function () { - return config('statamic.eloquent-driver.blueprints.fieldset_model'); + // @deprecated + $this->app->bind('statamic.eloquent.blueprints.blueprints_model', function () { + return config('statamic.eloquent-driver.blueprints.model', config('statamic.eloquent-driver.blueprints.blueprint_model')); }); $this->app->singleton(\Statamic\Fields\BlueprintRepository::class, function () { return (new \Statamic\Eloquent\Fields\BlueprintRepository) ->setDirectory(resource_path('blueprints')); }); - - $this->app->singleton( - 'Statamic\Fields\FieldsetRepository', - 'Statamic\Eloquent\Fields\FieldsetRepository' - ); } private function registerCollections() @@ -345,6 +346,29 @@ private function registerEntries() Statamic::repository(EntryRepositoryContract::class, EntryRepository::class); } + private function registerFieldsets() + { + $usingOldConfigKeys = config()->has('statamic.eloquent-driver.blueprints.fieldset_model'); + + if (config($usingOldConfigKeys ? 'statamic.eloquent-driver.blueprints.driver' : 'statamic.eloquent-driver.fieldsets.driver', 'file') != 'eloquent') { + return; + } + + $this->app->bind('statamic.eloquent.fieldsets.model', function () { + return config('statamic.eloquent-driver.fieldsets.model', config('statamic.eloquent-driver.blueprints.fieldset_model')); + }); + + // @deprecated + $this->app->bind('statamic.eloquent.blueprints.fieldset_model', function () { + return config('statamic.eloquent-driver.fieldsets.model', config('statamic.eloquent-driver.blueprints.fieldset_model')); + }); + + $this->app->singleton( + 'Statamic\Fields\FieldsetRepository', + 'Statamic\Eloquent\Fields\FieldsetRepository' + ); + } + private function registerForms() { if (config('statamic.eloquent-driver.forms.driver', 'file') != 'eloquent') { diff --git a/tests/Commands/ImportBlueprintsTest.php b/tests/Commands/ImportBlueprintsTest.php index 1815bad8..0bcd679a 100644 --- a/tests/Commands/ImportBlueprintsTest.php +++ b/tests/Commands/ImportBlueprintsTest.php @@ -7,6 +7,7 @@ use Statamic\Eloquent\Fields\FieldsetModel; use Statamic\Facades\Blueprint as BlueprintFacade; use Statamic\Facades\Fieldset as FieldsetFacade; +use Statamic\Facades\File; use Statamic\Fields\Blueprint; use Statamic\Fields\BlueprintRepository; use Statamic\Fields\Fieldset; @@ -37,6 +38,10 @@ public function setUp(): void // Statamic will automatically generate a default blueprint. For the purpose of this test, we'll delete it. BlueprintModel::all()->each->delete(); + + // Ensure there is no stray yaml hanging out which might cause count errors. + File::withAbsolutePaths()->getFilesByTypeRecursively(resource_path('blueprints'), 'yaml')->each(fn ($file) => unlink($file)); + File::withAbsolutePaths()->getFilesByTypeRecursively(resource_path('fieldsets'), 'yaml')->each(fn ($file) => unlink($file)); } /** @test */ @@ -60,6 +65,153 @@ public function it_imports_blueprints_and_fieldsets() $this->assertCount(0, FieldsetModel::all()); $this->artisan('statamic:eloquent:import-blueprints') + ->expectsQuestion('Do you want to import blueprints?', true) + ->expectsOutputToContain('Blueprints imported successfully.') + ->expectsQuestion('Do you want to import fieldsets?', true) + ->expectsOutputToContain('Fieldsets imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, BlueprintModel::all()); + $this->assertCount(1, FieldsetModel::all()); + } + + /** @test */ + public function it_imports_blueprints_with_console_question() + { + 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') + ->expectsQuestion('Do you want to import blueprints?', true) + ->expectsOutputToContain('Blueprints imported successfully.') + ->expectsQuestion('Do you want to import fieldsets?', false) + ->assertExitCode(0); + + $this->assertCount(1, BlueprintModel::all()); + $this->assertCount(0, FieldsetModel::all()); + } + + /** @test */ + public function it_imports_fieldsets_with_console_question() + { + 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') + ->expectsQuestion('Do you want to import blueprints?', false) + ->expectsQuestion('Do you want to import fieldsets?', true) + ->expectsOutputToContain('Fieldsets imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, BlueprintModel::all()); + $this->assertCount(1, FieldsetModel::all()); + } + + /** @test */ + public function it_imports_blueprints_with_only_blueprints_argument() + { + 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', ['--only-blueprints' => true]) + ->expectsOutputToContain('Blueprints imported successfully.') + ->assertExitCode(0); + + $this->assertCount(1, BlueprintModel::all()); + $this->assertCount(0, FieldsetModel::all()); + } + + /** @test */ + public function it_imports_fieldsets_with_only_fieldsets_argument() + { + 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', ['--only-fieldsets' => true]) + ->expectsOutputToContain('Fieldsets imported successfully.') + ->assertExitCode(0); + + $this->assertCount(0, BlueprintModel::all()); + $this->assertCount(1, FieldsetModel::all()); + } + + /** @test */ + public function it_imports_blueprints_and_fieldsets_with_force_argument() + { + 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', ['--force' => true]) ->expectsOutputToContain('Blueprints imported successfully.') ->expectsOutputToContain('Fieldsets imported successfully.') ->assertExitCode(0);