Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ The command will also give you the opportunity to indicate whether you'd like ex

If you originally opt-out of importing existing content, then later change your mind, you can import existing content by running the relevant commands:

- Addon Settings: `php please eloquent:import-addon-settings`
- Assets: `php please eloquent:import-assets`
- Blueprints and Fieldsets: `php please eloquent:import-blueprints`
- Collections: `php please eloquent:import-collections`
Expand All @@ -47,6 +48,7 @@ If your assets are being driven by the Eloquent Driver and you're managing your

If you wish to move back to flat-files, you may use the following commands to export your content out of the database:

- Addon Settings: `php please eloquent:export-addon-settings`
- Assets: `php please eloquent:export-assets`
- Blueprints and Fieldsets: `php please eloquent:export-blueprints`
- Collections: `php please eloquent:export-collections`
Expand Down
5 changes: 5 additions & 0 deletions config/eloquent-driver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
'connection' => env('STATAMIC_ELOQUENT_CONNECTION', ''),
'table_prefix' => env('STATAMIC_ELOQUENT_PREFIX', ''),

'addon_settings' => [
'driver' => 'file',
'model' => \Statamic\Eloquent\AddonSettings\AddonSettingsModel::class,
],

'asset_containers' => [
'driver' => 'file',
'model' => \Statamic\Eloquent\Assets\AssetContainerModel::class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Statamic\Eloquent\Database\BaseMigration as Migration;

return new class extends Migration
{
public function up()
{
Schema::create($this->prefix('addon_settings'), function (Blueprint $table) {
$table->string('addon')->index()->primary();
$table->json('settings')->nullable();
});
}

public function down()
{
Schema::dropIfExists($this->prefix('addon_settings'));
}
};
44 changes: 44 additions & 0 deletions src/AddonSettings/AddonSettings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace Statamic\Eloquent\AddonSettings;

use Illuminate\Database\Eloquent\Model;
use Statamic\Addons\Settings as AbstractSettings;
use Statamic\Facades\Addon;

class AddonSettings extends AbstractSettings
{
protected $model;

public static function fromModel(Model $model)
{
$addon = Addon::get($model->addon);

return (new static($addon, $model->settings))->model($model);
}

public function toModel()
{
return self::makeModelFromContract($this);
}

public static function makeModelFromContract(AbstractSettings $settings)
{
$class = app('statamic.eloquent.addon_settings.model');

return $class::firstOrNew(['addon' => $settings->addon()->id()])->fill([
'settings' => array_filter($settings->raw()),
]);
}

public function model($model = null)
{
if (func_num_args() === 0) {
return $this->model;
}

$this->model = $model;

return $this;
}
}
25 changes: 25 additions & 0 deletions src/AddonSettings/AddonSettingsModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Statamic\Eloquent\AddonSettings;

use Statamic\Eloquent\Database\BaseModel;

class AddonSettingsModel extends BaseModel
{
protected $guarded = [];

protected $table = 'addon_settings';

protected $primaryKey = 'addon';

protected $keyType = 'string';

public $timestamps = false;

protected function casts(): array
{
return [
'settings' => 'array',
];
}
}
37 changes: 37 additions & 0 deletions src/AddonSettings/AddonSettingsRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Statamic\Eloquent\AddonSettings;

use Statamic\Addons\SettingsRepository as FileSettingsRepository;
use Statamic\Contracts\Addons\Settings as AddonSettingsContract;

class AddonSettingsRepository extends FileSettingsRepository
{
public function find(string $addon): ?AddonSettingsContract
{
$model = app('statamic.eloquent.addon_settings.model')::find($addon);

if (! $model) {
return null;
}

return AddonSettings::fromModel($model);
}

public function save(AddonSettingsContract $settings): bool
{
return $settings->toModel()->save();
}

public function delete(AddonSettingsContract $settings): bool
{
return $settings->toModel()->delete();
}

public static function bindings(): array
{
return [
AddonSettingsContract::class => AddonSettings::class,
];
}
}
49 changes: 49 additions & 0 deletions src/Commands/ExportAddonSettings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace Statamic\Eloquent\Commands;

use Illuminate\Console\Command;
use Statamic\Addons\FileSettingsRepository;
use Statamic\Console\RunsInPlease;
use Statamic\Contracts\Addons\SettingsRepository as SettingsRepositoryContract;
use Statamic\Eloquent\AddonSettings\AddonSettingsModel;
use Statamic\Facades\Addon;
use Statamic\Statamic;

class ExportAddonSettings extends Command
{
use RunsInPlease;

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'statamic:eloquent:export-addon-settings';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Exports eloquent addon settings to flat files.';

/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
Statamic::repository(SettingsRepositoryContract::class, FileSettingsRepository::class);

AddonSettingsModel::all()->each(function ($model) {
Addon::get($model->addon)?->settings()->set($model->settings)->save();
});

$this->newLine();
$this->info('Addon settings exported');

return 0;
}
}
50 changes: 50 additions & 0 deletions src/Commands/ImportAddonSettings.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace Statamic\Eloquent\Commands;

use Illuminate\Console\Command;
use Statamic\Addons\FileSettingsRepository;
use Statamic\Console\RunsInPlease;
use Statamic\Contracts\Addons\SettingsRepository as SettingsRepositoryContract;
use Statamic\Facades\Addon;
use Statamic\Statamic;

class ImportAddonSettings extends Command
{
use RunsInPlease;

/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'statamic:eloquent:import-addon-settings';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Imports file-based addon settings into the database.';

/**
* Execute the console command.
*/
public function handle(): int
{
Statamic::repository(SettingsRepositoryContract::class, FileSettingsRepository::class);

Addon::all()
->filter(fn ($addon) => collect($addon->settings()->raw())->filter()->isNotEmpty())
->each(function ($addon) {
app('statamic.eloquent.addon_settings.model')::updateOrCreate(
['addon' => $addon->id()],
['settings' => $addon->settings()->raw()]
);
});

$this->components->info('Addon settings imported successfully.');

return 0;
}
}
22 changes: 22 additions & 0 deletions src/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Foundation\Console\AboutCommand;
use Statamic\Assets\AssetContainerContents;
use Statamic\Contracts\Addons\SettingsRepository as AddonSettingsRepositoryContract;
use Statamic\Contracts\Assets\AssetContainerRepository as AssetContainerRepositoryContract;
use Statamic\Contracts\Assets\AssetRepository as AssetRepositoryContract;
use Statamic\Contracts\Entries\CollectionRepository as CollectionRepositoryContract;
Expand All @@ -19,6 +20,7 @@
use Statamic\Contracts\Taxonomies\TaxonomyRepository as TaxonomyRepositoryContract;
use Statamic\Contracts\Taxonomies\TermRepository as TermRepositoryContract;
use Statamic\Contracts\Tokens\TokenRepository as TokenRepositoryContract;
use Statamic\Eloquent\AddonSettings\AddonSettingsRepository;
use Statamic\Eloquent\Assets\AssetContainerContents as EloquentAssetContainerContents;
use Statamic\Eloquent\Assets\AssetContainerRepository;
use Statamic\Eloquent\Assets\AssetQueryBuilder;
Expand Down Expand Up @@ -171,6 +173,10 @@ private function publishMigrations(): void
__DIR__.'/../database/migrations/2024_07_16_100000_create_sites_table.php' => database_path('migrations/2024_07_16_100000_create_sites_table.php'),
], 'statamic-eloquent-site-migrations');

$this->publishes($addonSettingMigrations = [
__DIR__.'/../database/migrations/2025_07_07_100000_create_addon_settings_table.php' => database_path('migrations/2025_07_07_100000_create_addon_settings_table.php'),
], 'statamic-eloquent-addon-setting-migrations');

$this->publishes(
array_merge(
$taxonomyMigrations,
Expand All @@ -189,6 +195,7 @@ private function publishMigrations(): void
$revisionMigrations,
$tokenMigrations,
$siteMigrations,
$addonSettingMigrations
),
'migrations'
);
Expand All @@ -204,6 +211,7 @@ private function publishMigrations(): void

public function register()
{
$this->registerAddonSettings();
$this->registerAssetContainers();
$this->registerAssets();
$this->registerBlueprints();
Expand All @@ -224,6 +232,19 @@ public function register()
$this->registerSites();
}

private function registerAddonSettings()
{
if (config('statamic.eloquent-driver.addon_settings.driver', 'file') != 'eloquent') {
return;
}

$this->app->bind('statamic.eloquent.addon_settings.model', function () {
return config('statamic.eloquent-driver.addon_settings.model');
});

Statamic::repository(AddonSettingsRepositoryContract::class, AddonSettingsRepository::class);
}

private function registerAssetContainers()
{
// if we have this config key then we started on 2.1.0 or earlier when
Expand Down Expand Up @@ -549,6 +570,7 @@ protected function addAboutCommandInfo()
}

AboutCommand::add('Statamic Eloquent Driver', collect([
'Addon Settings' => config('statamic.eloquent-driver.addon_settings.driver', 'file'),
'Asset Containers' => config('statamic.eloquent-driver.asset_containers.driver', 'file'),
'Assets' => config('statamic.eloquent-driver.assets.driver', 'file'),
'Blueprints' => config('statamic.eloquent-driver.blueprints.driver', 'file'),
Expand Down
67 changes: 67 additions & 0 deletions tests/Commands/ExportAddonSettingsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Tests\Commands;

use Foo\Bar\TestAddonServiceProvider;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\File;
use PHPUnit\Framework\Attributes\Test;
use Statamic\Addons\Addon;
use Statamic\Eloquent\AddonSettings\AddonSettingsModel;
use Statamic\Facades;
use Tests\TestCase;

class ExportAddonSettingsTest extends TestCase
{
use RefreshDatabase;

#[Test]
public function it_exports_addon_settings()
{
$seoPro = $this->makeFromPackage(['id' => 'statamic/seo-pro', 'slug' => 'seo-pro']);
$importer = $this->makeFromPackage(['id' => 'statamic/importer', 'slug' => 'importer']);

Facades\Addon::shouldReceive('all')->andReturn(collect([$seoPro, $importer]));
Facades\Addon::shouldReceive('get')->with('statamic/seo-pro')->andReturn($seoPro);
Facades\Addon::shouldReceive('get')->with('statamic/importer')->andReturn($importer);

AddonSettingsModel::create(['addon' => 'statamic/seo-pro', 'settings' => ['title' => 'SEO Title', 'description' => 'SEO Description']]);
AddonSettingsModel::create(['addon' => 'statamic/importer', 'settings' => ['chunk_size' => 100]]);

$this->artisan('statamic:eloquent:export-addon-settings')
->expectsOutputToContain('Addon settings exported')
->assertExitCode(0);

$this->assertFileExists(resource_path('addons/seo-pro.yaml'));
$this->assertEquals(<<<'YAML'
title: 'SEO Title'
description: 'SEO Description'

YAML
, File::get(resource_path('addons/seo-pro.yaml')));

$this->assertFileExists(resource_path('addons/importer.yaml'));
$this->assertEquals(<<<'YAML'
chunk_size: 100

YAML
, File::get(resource_path('addons/importer.yaml')));
}

private function makeFromPackage($attributes = [])
{
return Addon::makeFromPackage(array_merge([
'id' => 'vendor/test-addon',
'name' => 'Test Addon',
'description' => 'Test description',
'namespace' => 'Vendor\\TestAddon',
'provider' => TestAddonServiceProvider::class,
'autoload' => '',
'url' => 'http://test-url.com',
'developer' => 'Test Developer LLC',
'developerUrl' => 'http://test-developer.com',
'version' => '1.0',
'editions' => ['foo', 'bar'],
], $attributes));
}
}
Loading