From 919a1e63e349eff051a8c1d2261f62ee2988ad28 Mon Sep 17 00:00:00 2001 From: marijoo Date: Thu, 6 Oct 2022 15:49:15 +0200 Subject: [PATCH] feat: Add `versionAt` method Queries a version at a specific time in the models history. --- README.md | 16 +++++---- src/Versionable.php | 20 +++++++++++ tests/VersionAtTest.php | 79 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 tests/VersionAtTest.php diff --git a/README.md b/README.md index e459468..57d9374 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ class Post extends Model * @var array */ protected $versionable = ['title', 'content']; - + // Or use blacklist //protected $dontVersionable = ['created_at', 'updated_at']; @@ -93,11 +93,15 @@ $post->update(['title' => 'version2']); $post->versions; // all versions $post->latestVersion; // latest version // or -$post->lastVersion; +$post->lastVersion; $post->versions->first(); // first version -// or +// or $post->firstVersion; + +$post->versionAt('2022-10-06 12:00:00'); // get version from a specific time +// or +$post->versionAt(\Carbon\Carbon::create(2022, 10, 6, 12)); ``` ### Reversion @@ -164,8 +168,8 @@ You can set the following different version policies through property `protected ### Show diff between two versions ```php -$diff = $post->getVersion(1)->diff($post->getVersion(2)); -``` +$diff = $post->getVersion(1)->diff($post->getVersion(2)); +``` `$diff` is a object `Overtrue\LaravelVersionable\Diff`, it based on [jfcherng/php-diff](https://github.com/jfcherng/php-diff). @@ -211,7 +215,7 @@ toSideBySideHtml(array $differOptions = [], array $renderOptions = []): array ``` > **Note** -> +> > `$differOptions` and `$renderOptions` are optional, you can set them following the README of [jfcherng/php-diff](https://github.com/jfcherng/php-diff#example). ## :heart: Sponsor me diff --git a/src/Versionable.php b/src/Versionable.php index 773692d..9f2570a 100644 --- a/src/Versionable.php +++ b/src/Versionable.php @@ -5,6 +5,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\MorphOne; +use Illuminate\Support\Carbon; trait Versionable { @@ -64,6 +65,25 @@ public function firstVersion(): MorphOne return $this->morphOne($this->getVersionModel(), 'versionable')->oldest('id'); } + /** + * Get the version for a specific time. + * + * @param string|DateTimeInterface|null $time + * @param DateTimeZone|string|null $tz + * + * @throws \Carbon\Exceptions\InvalidFormatException + * + * @return static + */ + public function versionAt($time = null, $tz = null): ?Version + { + return $this->versions() + ->where('created_at', '<=', Carbon::parse($time, $tz)) + ->orderByDesc('created_at') + ->orderByDesc($this->getKey()) + ->first(); + } + public function getVersion(int $id): ?Version { return $this->versions()->find($id); diff --git a/tests/VersionAtTest.php b/tests/VersionAtTest.php new file mode 100644 index 0000000..ac942e4 --- /dev/null +++ b/tests/VersionAtTest.php @@ -0,0 +1,79 @@ + User::class, + 'versionable.user_model' => User::class, + ]); + + $this->user = User::create(['name' => 'marijoo']); + $this->actingAs($this->user); + } + + /** + * @test + */ + public function it_can_find_version_at_specific_time() + { + $this->travelTo(Carbon::create(2022, 10, 1, 12, 0)); + + $post = Post::create(['title' => 'version1', 'content' => 'version1 content']); + + $this->travelTo(Carbon::create(2022, 10, 2, 14, 0)); + + $post->update(['title' => 'version2']); + + $this->travelTo(Carbon::create(2022, 10, 3, 10, 0)); + + $post->update(['title' => 'version3']); + + $this->assertEquals('version1', $post->versionAt('2022-10-02 10:00:00')->contents['title']); + $this->assertEquals('version1', $post->versionAt('2022-10-02 13:59:59')->contents['title']); + $this->assertEquals('version2', $post->versionAt(Carbon::create(2022, 10, 02, 14))->contents['title']); + $this->assertEquals('version2', $post->versionAt('2022-10-02 15:00:00')->contents['title']); + $this->assertEquals('version3', $post->versionAt('2022-10-03 10:00:00')->contents['title']); + } + + /** + * @test + */ + public function it_returns_null_if_given_time_is_before_first_version() + { + $this->travelTo(Carbon::create(2022, 10, 1, 12, 0)); + + $post = Post::create(['title' => 'version1', 'content' => 'version1 content']); + + $this->assertNull($post->versionAt('2022-10-01 11:59:59')); + $this->assertEquals('version1', $post->versionAt('2022-10-01 12:00:00')->contents['title']); + } + + /** + * @test + */ + public function it_returns_latest_version_if_given_time_is_in_future() + { + $this->travelTo(Carbon::create(2022, 10, 1, 12, 0)); + + $post = Post::create(['title' => 'version1', 'content' => 'version1 content']); + + $this->travelTo(Carbon::create(2022, 10, 2, 14, 0)); + + $post->update(['title' => 'version2']); + + $this->assertEquals('version2', $post->versionAt('2024-11-01 12:00:00')->contents['title']); + } +}