From b2641d94c10e7d4a266882a66e014a5472d3f74f Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Fri, 23 Jun 2023 16:40:56 +0100 Subject: [PATCH 1/6] Add upgrade note to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e45607ad..40cc877b 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,10 @@ The configuration file (`statamic.eloquent-driver`) allows you to choose which r You may also specify your own models for each repository, should you wish to use something different from the one provided. +## Upgrading + +After upgrading please ensure to run `php artisan migrate` to update your database to the latest schema. + ## Importing existing file based content We have provided imports from file based content for each repository, which can be run as follows: From 3c43ef5bf8b17b026cfb696879cab7aed2bd1013 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 27 Jun 2023 07:42:08 +0100 Subject: [PATCH 2/6] Missing update script --- src/ServiceProvider.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 12ab2099..42f31581 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -44,6 +44,7 @@ class ServiceProvider extends AddonServiceProvider \Statamic\Eloquent\Updates\AddBlueprintToEntriesTable::class, \Statamic\Eloquent\Updates\ChangeDefaultBlueprint::class, \Statamic\Eloquent\Updates\DropForeignKeysOnEntriesAndForms::class, + \Statamic\Eloquent\Updates\SplitGlobalsFromVariables::class, ]; protected $listen = [ From 946b2dd1c7fdf54f987b212adfe69b487953aebe Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Tue, 27 Jun 2023 07:46:03 +0100 Subject: [PATCH 3/6] Correct file path to migration --- src/Updates/SplitGlobalsFromVariables.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Updates/SplitGlobalsFromVariables.php b/src/Updates/SplitGlobalsFromVariables.php index c16c0935..000519ce 100644 --- a/src/Updates/SplitGlobalsFromVariables.php +++ b/src/Updates/SplitGlobalsFromVariables.php @@ -19,7 +19,7 @@ public function update() $this->files->copy($source, $dest); - $source = __DIR__.'/../../database/migrations/updates/update_globals_table.stub'; + $source = __DIR__.'/../../database/migrations/updates/update_globals_table.php.stub'; $dest = database_path('migrations/'.date('Y_m_d_His').'_update_globals_table.php'); $this->files->copy($source, $dest); From 2ec86584225a82e82da26f211811f236b6585082 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 28 Jun 2023 21:53:33 +0100 Subject: [PATCH 4/6] Lets not exclude parent --- src/Entries/Entry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 680b6006..123b541a 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -101,7 +101,7 @@ public static function makeModelFromContract(EntryContract $source) 'date' => $date, 'collection' => $source->collectionHandle(), 'blueprint' => $source->blueprint ?? $source->blueprint()->handle(), - 'data' => $data->except(EntryQueryBuilder::COLUMNS)->except(['parent']), + 'data' => $data->except(EntryQueryBuilder::COLUMNS), 'published' => $source->published(), 'status' => $source->status(), 'updated_at' => $source->lastModified(), From 626c9d49ce8b0c0dca9dcef4338f3838c908a90f Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 5 Nov 2025 08:58:48 +0000 Subject: [PATCH 5/6] init --- composer.json | 2 +- src/Commands/ImportRevisions.php | 2 +- src/Revisions/Revision.php | 10 +++--- src/Revisions/RevisionQueryBuilder.php | 46 ++++++++++++++++++++++++++ src/Revisions/RevisionRepository.php | 2 ++ tests/Commands/ImportRevisionsTest.php | 5 ++- 6 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 src/Revisions/RevisionQueryBuilder.php diff --git a/composer.json b/composer.json index 753aeb5c..2cac5a97 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ }, "require": { "php": "^8.2", - "statamic/cms": "^6.0" + "statamic/cms": "dev-master" }, "require-dev": { "doctrine/dbal": "^3.8", diff --git a/src/Commands/ImportRevisions.php b/src/Commands/ImportRevisions.php index dd0a77fa..0eeaf5b1 100644 --- a/src/Commands/ImportRevisions.php +++ b/src/Commands/ImportRevisions.php @@ -45,7 +45,7 @@ public function handle(): int private function importRevisions(): void { - $this->withProgressBar(File::allFiles(config('statamic.revisions.path')), function ($file) { + $this->withProgressBar(File::allFiles(config('statamic.stache.stores.revisions.directory')), function ($file) { $yaml = YAML::file($file->getPathname())->parse(); $revision = (new Revision) diff --git a/src/Revisions/Revision.php b/src/Revisions/Revision.php index 0d0161ac..ef40c31a 100644 --- a/src/Revisions/Revision.php +++ b/src/Revisions/Revision.php @@ -33,11 +33,11 @@ public static function fromModel(Model $model) { return (new static) ->key($model->key) - ->action($model->action ?? false) + ->action($model->action ?? null) ->id($model->created_at->timestamp) ->date($model->created_at) - ->user($model->user ?? false) - ->message($model->message ?? '') + ->user($model->user ?? null) + ->message($model->message ?? null) ->attributes($model->attributes ?? []) ->model($model); } @@ -59,10 +59,10 @@ public function fromRevisionOrWorkingCopy($item) { return (new static) ->key($item->key()) - ->action($item instanceof WorkingCopy ? 'working' : $item->action()) + ->action($item->isWorkingCopy() ? 'working' : $item->action()) ->date($item->date()) ->user($item->user()?->id() ?? false) - ->message($item->message() ?? '') + ->message($item->message() ?? null) ->attributes($item->attributes() ?? []); } diff --git a/src/Revisions/RevisionQueryBuilder.php b/src/Revisions/RevisionQueryBuilder.php new file mode 100644 index 00000000..5b556967 --- /dev/null +++ b/src/Revisions/RevisionQueryBuilder.php @@ -0,0 +1,46 @@ +map(function ($model) use ($columns) { + return app(RevisionContract::class)::fromModel($model) + ->selectedQueryColumns($this->selectedQueryColumns ?? $columns); + }); + } + + protected function column($column) + { + if (! is_string($column)) { + return $column; + } + + if (! in_array($column, self::COLUMNS)) { + if (! Str::startsWith($column, 'attributes->')) { + $column = 'attributes->'.$column; + } + } + + return $column; + } + + public function with($relations, $callback = null) + { + return $this; + } +} diff --git a/src/Revisions/RevisionRepository.php b/src/Revisions/RevisionRepository.php index c5f2366e..3f9c3d08 100644 --- a/src/Revisions/RevisionRepository.php +++ b/src/Revisions/RevisionRepository.php @@ -3,6 +3,7 @@ namespace Statamic\Eloquent\Revisions; use Statamic\Contracts\Revisions\Revision as RevisionContract; +use Statamic\Contracts\Revisions\RevisionQueryBuilder as QueryBuilderContract; use Statamic\Revisions\RevisionRepository as StacheRepository; use Statamic\Revisions\WorkingCopy; @@ -70,6 +71,7 @@ public static function bindings(): array { return [ RevisionContract::class => Revision::class, + QueryBuilderContract::class => RevisionQueryBuilder::class, ]; } } diff --git a/tests/Commands/ImportRevisionsTest.php b/tests/Commands/ImportRevisionsTest.php index 08d9bf64..3e6a210f 100644 --- a/tests/Commands/ImportRevisionsTest.php +++ b/tests/Commands/ImportRevisionsTest.php @@ -22,10 +22,9 @@ protected function setUp(): void config()->set('statamic.revisions', [ 'enabled' => true, - 'path' => __DIR__.'/tmp', ]); - mkdir(__DIR__.'/tmp'); + config()->set('statamic.stache.stores.revisions.directory', __DIR__.'/../__fixtures__/dev-null/storage/statamic/revisions'); Facade::clearResolvedInstance(RevisionRepositoryContract::class); @@ -35,7 +34,7 @@ protected function setUp(): void protected function tearDown(): void { - app('files')->deleteDirectory(__DIR__.'/tmp'); + app('files')->deleteDirectory(config('statamic.stache.stores.revisions.directory')); parent::tearDown(); } From 07a1f21877f97d0a2e4a2428feeb39e1e732c124 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Wed, 5 Nov 2025 09:23:38 +0000 Subject: [PATCH 6/6] add tests and fixes --- src/Revisions/Revision.php | 25 +--- src/Revisions/RevisionRepository.php | 22 +--- src/ServiceProvider.php | 7 ++ tests/Repositories/RevisionRepositoryTest.php | 110 ++++++++++++++++++ 4 files changed, 120 insertions(+), 44 deletions(-) create mode 100644 tests/Repositories/RevisionRepositoryTest.php diff --git a/src/Revisions/Revision.php b/src/Revisions/Revision.php index ef40c31a..30e198c9 100644 --- a/src/Revisions/Revision.php +++ b/src/Revisions/Revision.php @@ -3,11 +3,7 @@ namespace Statamic\Eloquent\Revisions; use Illuminate\Database\Eloquent\Model; -use Statamic\Events\RevisionDeleted; -use Statamic\Events\RevisionSaved; -use Statamic\Events\RevisionSaving; use Statamic\Revisions\Revision as FileEntry; -use Statamic\Revisions\WorkingCopy; class Revision extends FileEntry { @@ -34,7 +30,6 @@ public static function fromModel(Model $model) return (new static) ->key($model->key) ->action($model->action ?? null) - ->id($model->created_at->timestamp) ->date($model->created_at) ->user($model->user ?? null) ->message($model->message ?? null) @@ -61,7 +56,7 @@ public function fromRevisionOrWorkingCopy($item) ->key($item->key()) ->action($item->isWorkingCopy() ? 'working' : $item->action()) ->date($item->date()) - ->user($item->user()?->id() ?? false) + ->user($item->user()?->id() ?? null) ->message($item->message() ?? null) ->attributes($item->attributes() ?? []); } @@ -76,22 +71,4 @@ public function model($model = null) return $this; } - - public function save() - { - if (RevisionSaving::dispatch($this) === false) { - return false; - } - - $this->model->save(); - - RevisionSaved::dispatch($this); - } - - public function delete() - { - $this->model->delete(); - - RevisionDeleted::dispatch($this); - } } diff --git a/src/Revisions/RevisionRepository.php b/src/Revisions/RevisionRepository.php index 3f9c3d08..7e75b443 100644 --- a/src/Revisions/RevisionRepository.php +++ b/src/Revisions/RevisionRepository.php @@ -5,27 +5,9 @@ use Statamic\Contracts\Revisions\Revision as RevisionContract; use Statamic\Contracts\Revisions\RevisionQueryBuilder as QueryBuilderContract; use Statamic\Revisions\RevisionRepository as StacheRepository; -use Statamic\Revisions\WorkingCopy; class RevisionRepository extends StacheRepository { - public function make(): RevisionContract - { - return new (app('statamic.eloquent.revisions.model')); - } - - public function whereKey($key) - { - return app('statamic.eloquent.revisions.model')::where('key', $key) - ->orderBy('created_at') - ->get() - ->map(function ($revision) use ($key) { - return $this->makeRevisionFromFile($key, $revision); - })->keyBy(function ($revision) { - return $revision->date()->timestamp; - }); - } - public function findWorkingCopyByKey($key) { $class = app('statamic.eloquent.revisions.model'); @@ -38,7 +20,7 @@ public function findWorkingCopyByKey($key) public function save(RevisionContract $copy) { - if ($copy instanceof WorkingCopy) { + if ($copy->isWorkingCopy()) { app('statamic.eloquent.revisions.model')::where([ 'key' => $copy->key(), 'action' => 'working', @@ -53,7 +35,7 @@ public function save(RevisionContract $copy) public function delete(RevisionContract $revision) { - if ($revision instanceof WorkingCopy) { + if ($revision->isWorkingCopy()) { $this->findWorkingCopyByKey($revision->key())?->delete(); return; diff --git a/src/ServiceProvider.php b/src/ServiceProvider.php index 9c5eb28e..77703184 100644 --- a/src/ServiceProvider.php +++ b/src/ServiceProvider.php @@ -33,6 +33,7 @@ use Statamic\Eloquent\Forms\SubmissionRepository; use Statamic\Eloquent\Globals\GlobalRepository; use Statamic\Eloquent\Globals\GlobalVariablesRepository; +use Statamic\Eloquent\Revisions\RevisionQueryBuilder; use Statamic\Eloquent\Revisions\RevisionRepository; use Statamic\Eloquent\Structures\CollectionTreeRepository; use Statamic\Eloquent\Structures\NavigationRepository; @@ -466,6 +467,12 @@ private function registerRevisions() return config('statamic.eloquent-driver.revisions.model'); }); + $this->app->bind(RevisionQueryBuilder::class, function ($app) { + return new RevisionQueryBuilder( + $app['statamic.eloquent.revisions.model']::query() + ); + }); + Statamic::repository(RevisionRepositoryContract::class, RevisionRepository::class); } diff --git a/tests/Repositories/RevisionRepositoryTest.php b/tests/Repositories/RevisionRepositoryTest.php new file mode 100644 index 00000000..51d14c8a --- /dev/null +++ b/tests/Repositories/RevisionRepositoryTest.php @@ -0,0 +1,110 @@ +sites(['en', 'fr']); + $this->app->instance(Stache::class, $stache); + $this->repo = new RevisionRepository($stache); + + \Statamic\Facades\Revision::make() + ->key('123') + ->action('working') + ->date(now()) + ->save(); + + \Statamic\Facades\Revision::make() + ->key('123') + ->action('other') + ->date(now()->subHour()) + ->save(); + + \Statamic\Facades\Revision::make() + ->key('123') + ->action('other') + ->date(now()->subHours(2)) + ->save(); + + \Statamic\Facades\Revision::make() + ->key('456') + ->action('working') + ->date(now()) + ->save(); + + \Statamic\Facades\Revision::make() + ->key('456') + ->action('other') + ->date(now()->subHour()) + ->save(); + } + + #[Test] + public function it_gets_revisions_and_excludes_working_copies() + { + $revisions = $this->repo->whereKey('123'); + + $this->assertInstanceOf(Collection::class, $revisions); + $this->assertCount(2, $revisions); + $this->assertContainsOnlyInstancesOf(Revision::class, $revisions); + } + + #[Test] + public function it_can_call_to_array_on_a_revision_collection() + { + User::shouldReceive('find')->andReturnNull(); + + $revisions = $this->repo->whereKey('123'); + + $this->assertIsArray($revisions->toArray()); + } + + #[Test] + public function it_returns_a_query_builder() + { + $builder = $this->repo->query(); + + $this->assertInstanceOf(RevisionQueryBuilder::class, $builder); + } + + #[Test] + public function it_gets_and_filters_items_using_query_builder() + { + $builder = $this->repo->query(); + + $revisions = $builder->get(); + $this->assertInstanceOf(Collection::class, $revisions); + $this->assertCount(5, $revisions); + $this->assertContainsOnlyInstancesOf(Revision::class, $revisions); + + $revisions = $builder->where('key', '123')->get(); + $this->assertInstanceOf(Collection::class, $revisions); + $this->assertCount(3, $revisions); + $this->assertContainsOnlyInstancesOf(Revision::class, $revisions); + + $revisions = $builder->where('key', '123')->where('action', '!=', 'working')->get(); + $this->assertInstanceOf(Collection::class, $revisions); + $this->assertCount(2, $revisions); + $this->assertContainsOnlyInstancesOf(Revision::class, $revisions); + + $revisions = $builder->where('key', '1234')->get(); + $this->assertInstanceOf(Collection::class, $revisions); + $this->assertCount(0, $revisions); + } +}