From 662e6ebcef9b3f9c16d662ff406fadac3f3f199b Mon Sep 17 00:00:00 2001 From: jim Date: Tue, 7 Feb 2023 07:38:12 +0000 Subject: [PATCH 01/10] WIP: scheduled drafts --- composer.json | 2 + config/drafts.php | 5 ++ src/Commands/PublishScheduledDrafts.php | 37 +++++++++++++++ src/Concerns/HasDrafts.php | 46 ++++++++++++++---- src/Concerns/Publishes.php | 63 +++---------------------- src/LaravelDraftsServiceProvider.php | 6 ++- tests/DraftsTest.php | 20 ++++---- tests/SchedulingTest.php | 33 +++++++++++++ 8 files changed, 134 insertions(+), 78 deletions(-) create mode 100644 src/Commands/PublishScheduledDrafts.php create mode 100644 tests/SchedulingTest.php diff --git a/composer.json b/composer.json index 2a29119..3e57cc0 100644 --- a/composer.json +++ b/composer.json @@ -23,12 +23,14 @@ "spatie/laravel-package-tools": "^1.9.2" }, "require-dev": { + "roave/security-advisories": "dev-latest", "friendsofphp/php-cs-fixer": "^3.8", "nunomaduro/collision": "^6.0", "nunomaduro/larastan": "^2.0.1", "orchestra/testbench": "^7.0", "pestphp/pest": "^1.21", "pestphp/pest-plugin-laravel": "^1.1", + "pestphp/pest-plugin-parallel": "^1.2", "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", diff --git a/config/drafts.php b/config/drafts.php index 46aa1e3..5fe4381 100644 --- a/config/drafts.php +++ b/config/drafts.php @@ -21,6 +21,11 @@ */ 'published_at' => 'published_at', + /* + * Timestamp column that stores the date and time when the row is scheduled for publishing. + */ + 'will_publish_at' => 'will_publish_at', + /* * UUID column that stores the unique identifier of the model drafts. */ diff --git a/src/Commands/PublishScheduledDrafts.php b/src/Commands/PublishScheduledDrafts.php new file mode 100644 index 0000000..65ed423 --- /dev/null +++ b/src/Commands/PublishScheduledDrafts.php @@ -0,0 +1,37 @@ +argument('model'); + + if (! class_exists($class) || !in_array(HasDrafts::class, class_uses_recursive($class), strict: true)) { + throw new InvalidArgumentException("The model `{$class}` either doesn't exist or doesn't use the `HasDrafts` trait."); + } + + $model = new $class; + + $model::query() + ->onlyDrafts() + ->where($model->getWillPublishAtColumn(), '<', now()) + ->whereNull($model->getPublishedAtColumn()) + ->each(function (Model $record): void { + $record->setLive(); + $record->save(); + }); + + return Command::SUCCESS; + } +} diff --git a/src/Concerns/HasDrafts.php b/src/Concerns/HasDrafts.php index 8b49a67..159a6a4 100644 --- a/src/Concerns/HasDrafts.php +++ b/src/Concerns/HasDrafts.php @@ -2,7 +2,9 @@ namespace Oddvalue\LaravelDrafts\Concerns; +use Carbon\CarbonInterface; use Illuminate\Contracts\Database\Query\Builder; +use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasOne; @@ -10,7 +12,6 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Support\Arr; use Illuminate\Support\Str; -use JetBrains\PhpStorm\ArrayShape; use Oddvalue\LaravelDrafts\Facades\LaravelDrafts; trait HasDrafts @@ -27,7 +28,7 @@ trait HasDrafts |-------------------------------------------------------------------------- */ - public function initializeHasDrafts() + public function initializeHasDrafts(): void { $this->mergeCasts([ $this->getIsCurrentColumn() => 'boolean', @@ -137,6 +138,7 @@ public function setLive(): void if (! $published) { $this->{$this->getPublishedAtColumn()} ??= now(); + $this->{$this->getWillPublishAtColumn()} = null; $this->{$this->getIsPublishedColumn()} = true; $this->setCurrent(); @@ -183,11 +185,27 @@ public function setLive(): void $this->{$this->getIsPublishedColumn()} = false; $this->{$this->getPublishedAtColumn()} = null; + $this->{$this->getWillPublishAtColumn()} = null; $this->{$this->getIsCurrentColumn()} = false; $this->timestamps = false; $this->shouldCreateRevision = false; } + public function schedulePublishing(CarbonInterface $date): static + { + $this->{$this->getWillPublishAtColumn()} = $date; + $this->save(); + + return $this; + } + + public function clearScheduledPublishing(): static + { + $this->{$this->getWillPublishAtColumn()} = null; + + return $this; + } + public function getDraftableRelations(): array { return property_exists($this, 'draftableRelations') ? $this->draftableRelations : []; @@ -276,7 +294,7 @@ public function setPublisher(): static return $this; } - public function pruneRevisions() + public function pruneRevisions(): void { $this->withoutEvents(function () { $revisionsToKeep = $this->revisions() @@ -296,11 +314,11 @@ public function pruneRevisions() } /** - * Get the name of the "publisher" relation columns. - * - * @return array + * @return array{ + * id: string, + * type: string + * } */ - #[ArrayShape(['id' => "string", 'type' => "string"])] public function getPublisherColumns(): array { return [ @@ -314,9 +332,10 @@ public function getPublisherColumns(): array } /** - * Get the fully qualified "publisher" relation columns. - * - * @return array + * @return array{ + * id: string, + * type: string + * } */ public function getQualifiedPublisherColumns(): array { @@ -330,6 +349,13 @@ public function getIsCurrentColumn(): string : config('drafts.column_names.is_current', 'is_current'); } + public function getWillPublishAtColumn(): string + { + return defined(static::class.'::WILL_PUBLISH_AT') + ? static::WILL_PUBLISH_AT + : config('drafts.column_names.will_publish_at', 'will_publish_at'); + } + public function getUuidColumn(): string { return defined(static::class.'::UUID') diff --git a/src/Concerns/Publishes.php b/src/Concerns/Publishes.php index 5778149..81e3c38 100644 --- a/src/Concerns/Publishes.php +++ b/src/Concerns/Publishes.php @@ -2,30 +2,21 @@ namespace Oddvalue\LaravelDrafts\Concerns; +use Closure; use Oddvalue\LaravelDrafts\Scopes\PublishingScope; /** - * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder withPublished(bool $withPublished = true) - * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder onlyPublished() - * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder withoutPublished() + * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder withDrafts(bool $withDrafts = true) + * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder withoutDrafts() + * @method static \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder onlyDrafts() */ trait Publishes { - /** - * Boot the publishes trait for a model. - * - * @return void - */ public static function bootPublishes(): void { static::addGlobalScope(new PublishingScope()); } - /** - * Initialize the publishes trait for an instance. - * - * @return void - */ public function initializePublishes(): void { $this->mergeCasts([ @@ -34,11 +25,6 @@ public function initializePublishes(): void ]); } - /** - * Publish a model instance. - * - * @return static - */ public function publish(): static { if ($this->fireModelEvent('publishing') === false) { @@ -55,43 +41,21 @@ public function publish(): static return $this; } - /** - * Determine if the model instance has been published. - * - * @return bool - */ public function isPublished(): bool { return $this->{$this->getIsPublishedColumn()} ?? false; } - /** - * Register a "published" model event callback with the dispatcher. - * - * @param string|\Closure $callback - * @return void - */ - public static function publishing(string|\Closure $callback): void + public static function publishing(string|Closure $callback): void { static::registerModelEvent('publishing', $callback); } - /** - * Register a "softDeleted" model event callback with the dispatcher. - * - * @param string|\Closure $callback - * @return void - */ - public static function published(string|\Closure $callback): void + public static function published(string|Closure $callback): void { static::registerModelEvent('published', $callback); } - /** - * Get the name of the "published at" column. - * - * @return string - */ public function getPublishedAtColumn(): string { return defined(static::class.'::PUBLISHED_AT') @@ -99,21 +63,11 @@ public function getPublishedAtColumn(): string : config('drafts.column_names.published_at', 'published_at'); } - /** - * Get the fully qualified "published at" column. - * - * @return string - */ public function getQualifiedPublishedAtColumn(): string { return $this->qualifyColumn($this->getPublishedAtColumn()); } - /** - * Get the name of the "published at" column. - * - * @return string - */ public function getIsPublishedColumn(): string { return defined(static::class.'::IS_PUBLISHED') @@ -121,11 +75,6 @@ public function getIsPublishedColumn(): string : config('drafts.column_names.is_published', 'is_published'); } - /** - * Get the fully qualified "published at" column. - * - * @return string - */ public function getQualifiedIsPublishedColumn(): string { return $this->qualifyColumn($this->getIsPublishedColumn()); diff --git a/src/LaravelDraftsServiceProvider.php b/src/LaravelDraftsServiceProvider.php index 792c28f..a062c62 100644 --- a/src/LaravelDraftsServiceProvider.php +++ b/src/LaravelDraftsServiceProvider.php @@ -3,6 +3,7 @@ namespace Oddvalue\LaravelDrafts; use Illuminate\Database\Schema\Blueprint; +use Oddvalue\LaravelDrafts\Commands\PublishScheduledDrafts; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -19,7 +20,7 @@ public function configurePackage(Package $package): void ->name('laravel-drafts') ->hasConfigFile() ->hasViews() - ->hasMigration('create_laravel-drafts_table'); + ->hasCommand(PublishScheduledDrafts::class); } public function packageRegistered() @@ -30,6 +31,7 @@ public function packageRegistered() string $isPublished = null, string $isCurrent = null, string $publisherMorphName = null, + string $willPublishAt = null, ) { /** @var Blueprint $this */ $uuid ??= config('drafts.column_names.uuid', 'uuid'); @@ -37,9 +39,11 @@ public function packageRegistered() $isPublished ??= config('drafts.column_names.is_published', 'is_published'); $isCurrent ??= config('drafts.column_names.is_current', 'is_current'); $publisherMorphName ??= config('drafts.column_names.publisher_morph_name', 'publisher_morph_name'); + $willPublishAt ??= config('drafts.column_names.will_publish_at', 'will_publish_at'); $this->uuid($uuid)->nullable(); $this->timestamp($publishedAt)->nullable(); + $this->timestamp($willPublishAt)->nullable(); $this->boolean($isPublished)->default(false); $this->boolean($isCurrent)->default(false); $this->nullableMorphs($publisherMorphName); diff --git a/tests/DraftsTest.php b/tests/DraftsTest.php index fba8782..96c32c2 100644 --- a/tests/DraftsTest.php +++ b/tests/DraftsTest.php @@ -3,7 +3,7 @@ use Oddvalue\LaravelDrafts\Tests\Post; use function Spatie\PestPluginTestTime\testTime; -it('creates drafts', function () { +it('creates drafts', function (): void { config(['drafts.revisions.keep' => 2]); testTime()->freeze(); $post = Post::factory()->published()->create(['title' => 'Foo']); @@ -30,7 +30,7 @@ ]); }); -it('can create drafts when revisions are disabled', function () { +it('can create drafts when revisions are disabled', function (): void { config(['drafts.revisions.keep' => 0]); $post = Post::factory()->create(['title' => 'Foo']); $this->assertDatabaseCount('posts', 1); @@ -42,7 +42,7 @@ $this->assertDatabaseCount('posts', 2); }); -it('can fetch the draft of a published record', function () { +it('can fetch the draft of a published record', function (): void { $post = Post::factory()->create(); $draft = Post::factory()->make(); $post->fresh()->updateAsDraft(['title' => $draft->title]); @@ -51,7 +51,7 @@ expect($post->draft->title)->toBe($draft->title); }); -it('can publish drafts', function () { +it('can publish drafts', function (): void { $post = Post::factory()->create(['title' => 'Foo']); $draft = Post::factory()->make(['title' => 'Bar']); @@ -66,12 +66,12 @@ expect($post->fresh()->title)->toBe($draft->title); }); -it('returns false when calling update on a record that has not been persisted', function () { +it('returns false when calling update on a record that has not been persisted', function (): void { $post = Post::factory()->make(); expect($post->updateAsDraft(['title' => 'Foo']))->toBeFalse(); }); -it('gets draft record from loaded revisions relation', function () { +it('gets draft record from loaded revisions relation', function (): void { $post = Post::factory()->create(['title' => 'Foo']); $draft = Post::factory()->make(['title' => 'Bar']); $post->fresh()->updateAsDraft(['title' => $draft->title]); @@ -80,7 +80,7 @@ expect($post->draft->title)->toBe($draft->title); }); -it('gets draft record from loaded draft relation', function () { +it('gets draft record from loaded draft relation', function (): void { $post = Post::factory()->create(['title' => 'Foo']); $draft = Post::factory()->make(['title' => 'Bar']); $post->fresh()->updateAsDraft(['title' => $draft->title]); @@ -89,7 +89,7 @@ expect($post->draft->title)->toBe($draft->title); }); -it('gets draft record when no relations loaded', function () { +it('gets draft record when no relations loaded', function (): void { $post = Post::factory()->create(['title' => 'Foo']); $draft = Post::factory()->make(['title' => 'Bar']); $post->fresh()->updateAsDraft(['title' => $draft->title]); @@ -97,7 +97,7 @@ expect($post->draft->title)->toBe($draft->title); }); -it('can create draft using default save method', function () { +it('can create draft using default save method', function (): void { $post = Post::factory()->create(['title' => 'Foo']); $draft = Post::factory()->make(['title' => 'Bar']); $post->refresh(); @@ -108,7 +108,7 @@ expect($post->draft->title)->toBe($draft->title); }); -it('creates drafts without altering the original post', function () { +it('creates drafts without altering the original post', function (): void { config(['drafts.revisions.keep' => 10]); $post = Post::factory()->create(['title' => 'Foo']); $originalId = $post->id; diff --git a/tests/SchedulingTest.php b/tests/SchedulingTest.php new file mode 100644 index 0000000..5d3842d --- /dev/null +++ b/tests/SchedulingTest.php @@ -0,0 +1,33 @@ +addMonth(); + $post = Post::factory()->published()->create(); + $draft = $post->createDraft(['title' => 'Hello World']); + $draft->schedulePublishing($willPublishAt); + $this->assertDatabaseHas('posts', [ + 'title' => 'Hello World', + 'published_at' => null, + 'will_publish_at' => $willPublishAt, + ]); +}); + +it('can publish scheduled drafts', function () { + $willPublishAt = now()->addWeek(); + $post = Post::factory()->published()->create(); + $draft = $post->createDraft(['title' => 'Hello World']); + $draft->schedulePublishing($willPublishAt); + + testTime()->addMonth()->freeze(); + + \Illuminate\Support\Facades\Artisan::call('drafts:publish', ['model' => Post::class]); + + $this->assertDatabaseHas('posts', [ + 'title' => 'Hello World', + 'published_at' => now()->toDateTimeString(), + 'will_publish_at' => null, + ]); +}); From d4141de2c9fd6bad507b1bc47d036fb774624d87 Mon Sep 17 00:00:00 2001 From: oddvalue Date: Tue, 7 Feb 2023 07:40:14 +0000 Subject: [PATCH 02/10] Fix styling --- src/Commands/PublishScheduledDrafts.php | 4 ++-- tests/DraftsTest.php | 1 + tests/PublishingTest.php | 1 + tests/SchedulingTest.php | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Commands/PublishScheduledDrafts.php b/src/Commands/PublishScheduledDrafts.php index 65ed423..11e2e5e 100644 --- a/src/Commands/PublishScheduledDrafts.php +++ b/src/Commands/PublishScheduledDrafts.php @@ -17,11 +17,11 @@ public function handle(): int { $class = $this->argument('model'); - if (! class_exists($class) || !in_array(HasDrafts::class, class_uses_recursive($class), strict: true)) { + if (! class_exists($class) || ! in_array(HasDrafts::class, class_uses_recursive($class), strict: true)) { throw new InvalidArgumentException("The model `{$class}` either doesn't exist or doesn't use the `HasDrafts` trait."); } - $model = new $class; + $model = new $class(); $model::query() ->onlyDrafts() diff --git a/tests/DraftsTest.php b/tests/DraftsTest.php index 96c32c2..1352035 100644 --- a/tests/DraftsTest.php +++ b/tests/DraftsTest.php @@ -1,6 +1,7 @@ Date: Sat, 11 Feb 2023 12:22:50 +0000 Subject: [PATCH 03/10] Add `Draftable` contract --- README.md | 5 +- src/Commands/PublishScheduledDrafts.php | 3 +- src/Concerns/HasDrafts.php | 24 ++++---- src/Contacts/Draftable.php | 80 +++++++++++++++++++++++++ src/Facades/LaravelDrafts.php | 5 +- src/LaravelDrafts.php | 4 +- 6 files changed, 105 insertions(+), 16 deletions(-) create mode 100644 src/Contacts/Draftable.php diff --git a/README.md b/README.md index bfb1025..9359934 100644 --- a/README.md +++ b/README.md @@ -92,15 +92,16 @@ return [ #### Add the trait -Add the `HasDrafts` trait to your model +Add the `HasDrafts` trait and `Draftable` contact to your model ```php onlyDrafts() ->where($model->getWillPublishAtColumn(), '<', now()) ->whereNull($model->getPublishedAtColumn()) - ->each(function (Model $record): void { + ->each(function (Draftable $record): void { $record->setLive(); $record->save(); }); diff --git a/src/Concerns/HasDrafts.php b/src/Concerns/HasDrafts.php index a6fd089..f4b82ec 100644 --- a/src/Concerns/HasDrafts.php +++ b/src/Concerns/HasDrafts.php @@ -3,6 +3,7 @@ namespace Oddvalue\LaravelDrafts\Concerns; use Carbon\CarbonInterface; +use Closure; use Illuminate\Contracts\Database\Query\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; @@ -12,12 +13,13 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Support\Arr; use Illuminate\Support\Str; +use Oddvalue\LaravelDrafts\Contacts\Draftable; use Oddvalue\LaravelDrafts\Facades\LaravelDrafts; /** - * @method void Current(Builder $query) - * @method void WithoutCurrent(Builder $query) - * @method void ExcludeRevision(Builder $query, int | Model $exclude) + * @method \Illuminate\Database\Eloquent\Builder current() + * @method \Illuminate\Database\Eloquent\Builder withoutCurrent() + * @method \Illuminate\Database\Eloquent\Builder excludeRevision(int | Model $exclude) */ trait HasDrafts { @@ -43,7 +45,7 @@ public function initializeHasDrafts(): void public static function bootHasDrafts(): void { - static::creating(function ($model) { + static::creating(function (Draftable | Model $model) { $model->{$model->getIsCurrentColumn()} = true; $model->setPublisher(); $model->generateUuid(); @@ -52,26 +54,26 @@ public static function bootHasDrafts(): void } }); - static::updating(function ($model) { + static::updating(function (Draftable | Model $model) { $model->newRevision(); }); - static::publishing(function ($model) { + static::publishing(function (Draftable | Model $model) { $model->setLive(); }); - static::deleted(function ($model) { + static::deleted(function (Draftable | Model $model) { $model->revisions()->delete(); }); if (method_exists(static::class, 'restored')) { - static::restored(function ($model) { + static::restored(function (Draftable | Model $model) { $model->revisions()->restore(); }); } if (method_exists(static::class, 'forceDeleted')) { - static::forceDeleted(function ($model) { + static::forceDeleted(function (Draftable | Model $model) { $model->revisions()->forceDelete(); }); } @@ -262,12 +264,12 @@ public function save(array $options = []): bool return parent::save($options); } - public static function savingAsDraft(string|\Closure $callback): void + public static function savingAsDraft(string | Closure $callback): void { static::registerModelEvent('savingAsDraft', $callback); } - public static function savedAsDraft(string|\Closure $callback): void + public static function savedAsDraft(string | Closure $callback): void { static::registerModelEvent('drafted', $callback); } diff --git a/src/Contacts/Draftable.php b/src/Contacts/Draftable.php new file mode 100644 index 0000000..71e93b4 --- /dev/null +++ b/src/Contacts/Draftable.php @@ -0,0 +1,80 @@ +user(); } From 7e155d6b885d4b9894ef1621e8b0c493c8b8771e Mon Sep 17 00:00:00 2001 From: oddvalue Date: Sat, 11 Feb 2023 12:26:39 +0000 Subject: [PATCH 04/10] Fix styling --- src/Commands/PublishScheduledDrafts.php | 1 - src/Contacts/Draftable.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Commands/PublishScheduledDrafts.php b/src/Commands/PublishScheduledDrafts.php index bb472df..ee0595b 100644 --- a/src/Commands/PublishScheduledDrafts.php +++ b/src/Commands/PublishScheduledDrafts.php @@ -3,7 +3,6 @@ namespace Oddvalue\LaravelDrafts\Commands; use Illuminate\Console\Command; -use Illuminate\Database\Eloquent\Model; use InvalidArgumentException; use Oddvalue\LaravelDrafts\Concerns\HasDrafts; use Oddvalue\LaravelDrafts\Contacts\Draftable; diff --git a/src/Contacts/Draftable.php b/src/Contacts/Draftable.php index 71e93b4..a453e3a 100644 --- a/src/Contacts/Draftable.php +++ b/src/Contacts/Draftable.php @@ -6,7 +6,6 @@ use Closure; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\MorphTo; -use Illuminate\Contracts\Database\Query\Builder; interface Draftable { From 8f25e12f0952c56ed60a56b7036a1d60afb006f6 Mon Sep 17 00:00:00 2001 From: jim Date: Sat, 11 Feb 2023 12:27:19 +0000 Subject: [PATCH 05/10] Update test model --- tests/Post.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Post.php b/tests/Post.php index 21077b8..322d641 100644 --- a/tests/Post.php +++ b/tests/Post.php @@ -9,8 +9,9 @@ use Illuminate\Database\Eloquent\Relations\HasOne; use Illuminate\Database\Eloquent\Relations\MorphToMany; use Oddvalue\LaravelDrafts\Concerns\HasDrafts; +use Oddvalue\LaravelDrafts\Contacts\Draftable; -class Post extends Model +class Post extends Model implements Draftable { use HasDrafts; use HasFactory; From a53268a17da87bda8ba869fc2ec03d3c02285f3c Mon Sep 17 00:00:00 2001 From: jim Date: Sat, 11 Feb 2023 12:49:31 +0000 Subject: [PATCH 06/10] Ensure new changes are backwards compatible --- src/Concerns/HasDrafts.php | 2 +- src/Contacts/Draftable.php | 2 +- tests/Post.php | 2 +- tests/SchedulingPost.php | 30 ++++++++++++++++++++++++++++++ tests/SchedulingTest.php | 8 ++++---- 5 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 tests/SchedulingPost.php diff --git a/src/Concerns/HasDrafts.php b/src/Concerns/HasDrafts.php index f4b82ec..7d4d676 100644 --- a/src/Concerns/HasDrafts.php +++ b/src/Concerns/HasDrafts.php @@ -283,7 +283,7 @@ public function updateAsDraft(array $attributes = [], array $options = []): bool return $this->fill($attributes)->saveAsDraft($options); } - public static function createDraft(...$attributes): self + public static function createDraft(...$attributes): static { return tap(static::make(...$attributes), function ($instance) { $instance->{$instance->getIsPublishedColumn()} = false; diff --git a/src/Contacts/Draftable.php b/src/Contacts/Draftable.php index a453e3a..1000359 100644 --- a/src/Contacts/Draftable.php +++ b/src/Contacts/Draftable.php @@ -53,7 +53,7 @@ public static function savedAsDraft(string|Closure $callback): void; public function updateAsDraft(array $attributes = [], array $options = []): bool; - public static function createDraft(...$attributes): self; + public static function createDraft(...$attributes): static; public function setPublisher(): static; diff --git a/tests/Post.php b/tests/Post.php index 322d641..52ba439 100644 --- a/tests/Post.php +++ b/tests/Post.php @@ -11,7 +11,7 @@ use Oddvalue\LaravelDrafts\Concerns\HasDrafts; use Oddvalue\LaravelDrafts\Contacts\Draftable; -class Post extends Model implements Draftable +class Post extends Model { use HasDrafts; use HasFactory; diff --git a/tests/SchedulingPost.php b/tests/SchedulingPost.php new file mode 100644 index 0000000..4c950a6 --- /dev/null +++ b/tests/SchedulingPost.php @@ -0,0 +1,30 @@ +addMonth(); - $post = Post::factory()->published()->create(); + $post = SchedulingPost::factory()->published()->create(); $draft = $post->createDraft(['title' => 'Hello World']); $draft->schedulePublishing($willPublishAt); $this->assertDatabaseHas('posts', [ @@ -18,13 +18,13 @@ it('can publish scheduled drafts', function () { $willPublishAt = now()->addWeek(); - $post = Post::factory()->published()->create(); + $post = SchedulingPost::factory()->published()->create(); $draft = $post->createDraft(['title' => 'Hello World']); $draft->schedulePublishing($willPublishAt); testTime()->addMonth()->freeze(); - \Illuminate\Support\Facades\Artisan::call('drafts:publish', ['model' => Post::class]); + \Illuminate\Support\Facades\Artisan::call('drafts:publish', ['model' => SchedulingPost::class]); $this->assertDatabaseHas('posts', [ 'title' => 'Hello World', From 8ea1627e8acdcc2e20c487836a920c6e269be91f Mon Sep 17 00:00:00 2001 From: jim Date: Mon, 13 Feb 2023 07:19:10 +0000 Subject: [PATCH 07/10] Add some more tests for scheduling --- database/factories/PostFactory.php | 26 +++++++++---------- src/Commands/PublishScheduledDrafts.php | 4 +-- tests/ConfigTest.php | 2 ++ tests/SchedulingTest.php | 33 ++++++++++++++++++++++++- 4 files changed, 47 insertions(+), 18 deletions(-) diff --git a/database/factories/PostFactory.php b/database/factories/PostFactory.php index 9ab4937..7619735 100644 --- a/database/factories/PostFactory.php +++ b/database/factories/PostFactory.php @@ -11,7 +11,7 @@ class PostFactory extends \Illuminate\Database\Eloquent\Factories\Factory /** * @inheritDoc */ - public function definition() + public function definition(): array { return [ 'title' => $this->faker->sentence, @@ -19,23 +19,19 @@ public function definition() ]; } - public function draft() + public function draft(): PostFactory { - return $this->state(function () { - return [ - 'published_at' => null, - 'is_published' => false, - ]; - }); + return $this->state(fn (): array => [ + 'published_at' => null, + 'is_published' => false, + ]); } - public function published() + public function published(): PostFactory { - return $this->state(function () { - return [ - 'published_at' => now()->toDateTimeString(), - 'is_published' => true, - ]; - }); + return $this->state(fn (): array => [ + 'published_at' => now()->toDateTimeString(), + 'is_published' => true, + ]); } } diff --git a/src/Commands/PublishScheduledDrafts.php b/src/Commands/PublishScheduledDrafts.php index ee0595b..89ebfd3 100644 --- a/src/Commands/PublishScheduledDrafts.php +++ b/src/Commands/PublishScheduledDrafts.php @@ -17,8 +17,8 @@ public function handle(): int { $class = $this->argument('model'); - if (! class_exists($class) || ! in_array(HasDrafts::class, class_uses_recursive($class), strict: true)) { - throw new InvalidArgumentException("The model `{$class}` either doesn't exist or doesn't use the `HasDrafts` trait."); + if (! class_exists($class) || ! in_array(Draftable::class, class_implements($class), strict: true)) { + throw new InvalidArgumentException("The model `{$class}` either doesn't exist or doesn't implement `Draftable`."); } $model = new $class(); diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php index a68c571..9db60c8 100644 --- a/tests/ConfigTest.php +++ b/tests/ConfigTest.php @@ -25,6 +25,7 @@ it('can override columns via class constants', function () { $post = new class () extends Post { public const PUBLISHED_AT = 'published_at_override'; + public const WILL_PUBLISH_AT = 'will_publish_at_overridee'; public const IS_PUBLISHED = 'is_published_override'; public const IS_CURRENT = 'is_current_override'; public const UUID = 'uuid_override'; @@ -34,6 +35,7 @@ expect($post->getPublishedAtColumn())->toBe($post::PUBLISHED_AT) ->and($post->getQualifiedPublishedAtColumn())->toBe($post->qualifyColumn($post::PUBLISHED_AT)) + ->and($post->getWillPublishAtColumn())->toBe($post::WILL_PUBLISH_AT) ->and($post->getIsPublishedColumn())->toBe($post::IS_PUBLISHED) ->and($post->getIsCurrentColumn())->toBe($post::IS_CURRENT) ->and($post->getUuidColumn())->toBe($post::UUID) diff --git a/tests/SchedulingTest.php b/tests/SchedulingTest.php index 7aa9c63..3b47d7b 100644 --- a/tests/SchedulingTest.php +++ b/tests/SchedulingTest.php @@ -1,5 +1,7 @@ addMonth()->freeze(); - \Illuminate\Support\Facades\Artisan::call('drafts:publish', ['model' => SchedulingPost::class]); + Artisan::call('drafts:publish', ['model' => SchedulingPost::class]); $this->assertDatabaseHas('posts', [ 'title' => 'Hello World', @@ -32,3 +34,32 @@ 'will_publish_at' => null, ]); }); + +it('fails when the model doesnt implement the contract', function (): void { + expect(static fn() => Artisan::call('drafts:publish', ['model' => Post::class])) + ->toThrow(InvalidArgumentException::class); +}); + +it('can clear the schedule date on a revision', function (): void { + $willPublishAt = now()->addWeek(); + $post = SchedulingPost::factory() + ->published() + ->has( + SchedulingPost::factory(), + 'revisions' + ) + ->create(); + $draft = $post->createDraft(['title' => 'Hello World']); + $draft->schedulePublishing($willPublishAt); + $draft->clearScheduledPublishing()->save(); + + testTime()->addMonth()->freeze(); + + Artisan::call('drafts:publish', ['model' => SchedulingPost::class]); + + $this->assertDatabaseHas('posts', [ + 'title' => 'Hello World', + 'published_at' => null, + 'will_publish_at' => null, + ]); +}); From 8527f0e3302e642f3eb383e7bc295e206f07721a Mon Sep 17 00:00:00 2001 From: jim Date: Tue, 28 Feb 2023 07:18:02 +0000 Subject: [PATCH 08/10] Fix styling --- src/Commands/PublishScheduledDrafts.php | 1 - src/LaravelDraftsServiceProvider.php | 2 +- tests/Post.php | 1 - tests/SchedulingPost.php | 6 ------ tests/SchedulingTest.php | 2 +- 5 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Commands/PublishScheduledDrafts.php b/src/Commands/PublishScheduledDrafts.php index 89ebfd3..707823c 100644 --- a/src/Commands/PublishScheduledDrafts.php +++ b/src/Commands/PublishScheduledDrafts.php @@ -4,7 +4,6 @@ use Illuminate\Console\Command; use InvalidArgumentException; -use Oddvalue\LaravelDrafts\Concerns\HasDrafts; use Oddvalue\LaravelDrafts\Contacts\Draftable; class PublishScheduledDrafts extends Command diff --git a/src/LaravelDraftsServiceProvider.php b/src/LaravelDraftsServiceProvider.php index 9b7d556..9cca336 100644 --- a/src/LaravelDraftsServiceProvider.php +++ b/src/LaravelDraftsServiceProvider.php @@ -5,8 +5,8 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Schema; -use Oddvalue\LaravelDrafts\Http\Middleware\WithDraftsMiddleware; use Oddvalue\LaravelDrafts\Commands\PublishScheduledDrafts; +use Oddvalue\LaravelDrafts\Http\Middleware\WithDraftsMiddleware; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; diff --git a/tests/Post.php b/tests/Post.php index d69b56d..ed8c26e 100644 --- a/tests/Post.php +++ b/tests/Post.php @@ -10,7 +10,6 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany; use Oddvalue\LaravelDrafts\Concerns\HasDrafts; use Oddvalue\LaravelDrafts\Database\Factories\PostFactory; -use Oddvalue\LaravelDrafts\Contacts\Draftable; class Post extends Model { diff --git a/tests/SchedulingPost.php b/tests/SchedulingPost.php index 4c950a6..8c316cb 100644 --- a/tests/SchedulingPost.php +++ b/tests/SchedulingPost.php @@ -2,13 +2,7 @@ namespace Oddvalue\LaravelDrafts\Tests; -use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsToMany; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\HasOne; -use Illuminate\Database\Eloquent\Relations\MorphToMany; -use Oddvalue\LaravelDrafts\Concerns\HasDrafts; use Oddvalue\LaravelDrafts\Contacts\Draftable; use Oddvalue\LaravelDrafts\Database\Factories\PostFactory; diff --git a/tests/SchedulingTest.php b/tests/SchedulingTest.php index 3b47d7b..8e7dab3 100644 --- a/tests/SchedulingTest.php +++ b/tests/SchedulingTest.php @@ -36,7 +36,7 @@ }); it('fails when the model doesnt implement the contract', function (): void { - expect(static fn() => Artisan::call('drafts:publish', ['model' => Post::class])) + expect(static fn () => Artisan::call('drafts:publish', ['model' => Post::class])) ->toThrow(InvalidArgumentException::class); }); From 88f4d396f4ddbe741574fc0853077b29d26fffb3 Mon Sep 17 00:00:00 2001 From: jim Date: Tue, 28 Feb 2023 07:29:42 +0000 Subject: [PATCH 09/10] Fix HasDrafts.php --- src/Concerns/HasDrafts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Concerns/HasDrafts.php b/src/Concerns/HasDrafts.php index 22ac7d5..81fe8de 100644 --- a/src/Concerns/HasDrafts.php +++ b/src/Concerns/HasDrafts.php @@ -287,7 +287,7 @@ public function shouldDraft(): bool return $this->shouldSaveAsDraft; } - public function setPublishedAttribute(): void + public function setPublishedAttributes(): void { // Do nothing, everything should be handled by `setLive` } From 0349fdc63522dc116c17c65e3d698b779260578b Mon Sep 17 00:00:00 2001 From: jim Date: Fri, 28 Apr 2023 19:54:12 +0100 Subject: [PATCH 10/10] Add tests for revisions relation query --- tests/RevisionsTest.php | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/RevisionsTest.php b/tests/RevisionsTest.php index 5f2b169..813bd58 100644 --- a/tests/RevisionsTest.php +++ b/tests/RevisionsTest.php @@ -3,6 +3,26 @@ use Oddvalue\LaravelDrafts\Tests\Post; use Oddvalue\LaravelDrafts\Tests\SoftDeletingPost; +it('can fetch revisions', function () { + $post = Post::factory() + ->hasRevisions(3) + ->create(); + + expect($post->revisions()->pluck('id')) + ->toHaveCount(4) + ->toContain($post->id); +}); + +it('can exclude a revision from the fetched revisions', function () { + $post = Post::factory() + ->hasRevisions(3) + ->create(); + + expect($post->revisions()->excludeRevision($post->id)->pluck('id')) + ->toHaveCount(3) + ->not->toContain($post); +}); + it('keeps the correct number of revisions', function () { config(['drafts.revisions.keep' => 3]); $revsExist = function (...$titles) {