Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feature/scheduled-drafts
Browse files Browse the repository at this point in the history
  • Loading branch information
oddvalue committed Feb 28, 2023
2 parents 8ea1627 + 0dbd0f7 commit c2b110a
Show file tree
Hide file tree
Showing 20 changed files with 344 additions and 47 deletions.
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# These are supported funding model platforms

github: oddvalue
13 changes: 9 additions & 4 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ jobs:
strategy:
fail-fast: true
matrix:
os: [ubuntu-latest, windows-latest]
php: [8.0,8.1]
laravel: [9.*]
stability: [prefer-lowest, prefer-stable]
os: [ubuntu-latest]
php: [8.0,8.1,8.2]
laravel: [9.*,10.*]
stability: [prefer-stable]
include:
- laravel: 9.*
testbench: 7.*
- laravel: 10.*
testbench: 8.*
exclude:
- php: 8.0
laravel: 10.*

name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}

Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,31 @@

All notable changes to `laravel-drafts` will be documented in this file.

## v1.2.0 - 2023-02-22

### What's Changed

- New feature: preview mode by @oddvalue in https://github.com/oddvalue/laravel-drafts/pull/19
- Add support for Laravel 10 by @oddvalue in https://github.com/oddvalue/laravel-drafts/pull/20

**Full Changelog**: https://github.com/oddvalue/laravel-drafts/compare/v1.1.0...v1.2.0

## v1.1.0 - 2023-02-20

### What's Changed

- Added a method to avoid creating revison by @Froxz in https://github.com/oddvalue/laravel-drafts/pull/15

### New Contributors

- @Froxz made their first contribution in https://github.com/oddvalue/laravel-drafts/pull/15

**Full Changelog**: https://github.com/oddvalue/laravel-drafts/compare/v1.0.2...v1.1.0

## v1.0.2 - 2023-02-17

**Full Changelog**: https://github.com/oddvalue/laravel-drafts/compare/v1.0.1...v1.0.2

## v0.0.3 - 2022-07-01

**Full Changelog**: https://github.com/oddvalue/laravel-drafts/compare/v0.0.2...v0.0.3
Expand Down
42 changes: 40 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
+ [Interacting with records](#interacting-with-records)
- [Published revision](#published-revision)
- [Current Revision](#current-revision)
- [Revisions](#revisions)
- [Preview mode](#preview-mode)
* [Testing](#testing)
* [Changelog](#changelog)
* [Contributing](#contributing)
Expand Down Expand Up @@ -248,8 +250,6 @@ To fetch the current revision you can call the `current` scope.
$posts = Post::current()->get();
```

You can implement a preview mode for your frontend by calling the `current` scope when fetching records.

#### Revisions

Every time a record is updated a new row/revision will be inserted. The default number of revisions kept is 10, this can be updated in the published config file.
Expand All @@ -263,6 +263,44 @@ $revisions = $post->revisions();

Deleting a record will also delete all of its revisions. Soft deleting records will soft delete the revisions and restoring records will restore the revisions.

If you need to update a record without creating revision

```php
$post->withoutRevision()->update($options);
```

#### Preview Mode

Enabling preview mode will disable the global scope that fetches only published records and will instead fetch the current revision regardless of published state.

```php
# Enable preview mode
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::previewMode();
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::previewMode(true);

# Disable preview mode
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::disablePreviewMode();
\Oddvalue\LaravelDrafts\Facades\LaravelDrafts::previewMode(false);
```

### Middleware

#### WithDraftsMiddleware

If you require a specific route to be able to access drafts then you can use the `WithDraftsMiddleware` middleware.

```php
Route::get('/posts/publish/{post}', [PostController::class, 'publish'])->middleware(\Oddvalue\LaravelDrafts\Http\Middleware\WithDraftsMiddleware::class);
```

There is also a helper method on the router that allows you to create a group with that middleware applied.

```php
Route::withDrafts(function (): void {
Route::get('/posts/publish/{post}', [PostController::class, 'publish']);
});
```

## Testing

```bash
Expand Down
12 changes: 5 additions & 7 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,22 @@
"require": {
"php": "^8.0",
"doctrine/dbal": "^3.3",
"illuminate/contracts": "^9.0",
"illuminate/contracts": "^9.0|^10.0",
"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",
"orchestra/testbench": "^7.0|^8.0",
"pestphp/pest": "^1.21",
"pestphp/pest-plugin-laravel": "^1.1",
"pestphp/pest-plugin-parallel": "^1.2",
"pestphp/pest-plugin-laravel": "^1.1|^2.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"phpunit/phpunit": "^9.5",
"spatie/laravel-ray": "^1.26",
"spatie/pest-plugin-test-time": "^1.1"
"spatie/pest-plugin-test-time": "^1.1",
"roave/security-advisories": "dev-latest"
},
"autoload": {
"psr-4": {
Expand Down
80 changes: 61 additions & 19 deletions src/Concerns/HasDrafts.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,20 @@ public function initializeHasDrafts(): void
{
$this->mergeCasts([
$this->getIsCurrentColumn() => 'boolean',
$this->getIsPublishedColumn() => 'boolean',
$this->getPublishedAtColumn() => 'datetime',
]);
}

public static function bootHasDrafts(): void
{
static::creating(function (Draftable | Model $model) {
static::addGlobalScope('onlyCurrentInPreviewMode', static function (Builder $builder): void {
if (LaravelDrafts::isPreviewModeEnabled()) {
$builder->current();
}
});

static::creating(function (Draftable | Model $model): void {
$model->{$model->getIsCurrentColumn()} = true;
$model->setPublisher();
$model->generateUuid();
Expand All @@ -54,26 +61,26 @@ public static function bootHasDrafts(): void
}
});

static::updating(function (Draftable | Model $model) {
static::updating(function (Draftable | Model $model): void {
$model->newRevision();
});

static::publishing(function (Draftable | Model $model) {
static::publishing(function (Draftable | Model $model): void {
$model->setLive();
});

static::deleted(function (Draftable | Model $model) {
static::deleted(function (Draftable | Model $model): void {
$model->revisions()->delete();
});

if (method_exists(static::class, 'restored')) {
static::restored(function (Draftable | Model $model) {
static::restored(function (Draftable | Model $model): void {
$model->revisions()->restore();
});
}

if (method_exists(static::class, 'forceDeleted')) {
static::forceDeleted(function (Draftable | Model $model) {
static::forceDeleted(function (Draftable | Model $model): void {
$model->revisions()->forceDelete();
});
}
Expand All @@ -96,7 +103,11 @@ protected function newRevision(): void

$revision = $this->fresh()?->replicate();

static::saved(function () use ($revision) {
static::saved(function (Model $model) use ($revision): void {
if ($model->isNot($this)) {
return;
}

$revision->created_at = $this->created_at;
$revision->updated_at = $this->updated_at;
$revision->is_current = false;
Expand All @@ -111,6 +122,13 @@ protected function newRevision(): void
});
}

public function withoutRevision(): static
{
$this->shouldCreateRevision = false;

return $this;
}

public function shouldCreateRevision(): bool
{
return $this->shouldCreateRevision;
Expand All @@ -124,26 +142,33 @@ public function generateUuid(): void
$this->{$this->getUuidColumn()} = Str::uuid();
}

public function getDraftableAttributes(): array
{
return $this->getAttributes();
}

public function setCurrent(): void
{
$oldCurrent = $this->revisions()->withDrafts()->current()->excludeRevision($this)->first();

static::saved(function () use ($oldCurrent) {
if ($oldCurrent) {
$oldCurrent->{$this->getIsCurrentColumn()} = false;
$oldCurrent->timestamps = false;
$oldCurrent->saveQuietly();
static::saved(function (Model $model) use ($oldCurrent): void {
if ($model->isNot($this) || ! $oldCurrent) {
return;
}

$oldCurrent->{$this->getIsCurrentColumn()} = false;
$oldCurrent->timestamps = false;
$oldCurrent->saveQuietly();
});

$this->{$this->getIsCurrentColumn()} = true;
}

public function setLive(): void
{
$published = $this->revisions()->excludeRevision($this)->published()->first();
$published = $this->revisions()->published()->first();

if (! $published) {
if (! $published || $this->is($published)) {
$this->{$this->getPublishedAtColumn()} ??= now();
$this->{$this->getWillPublishAtColumn()} = null;
$this->{$this->getIsPublishedColumn()} = true;
Expand All @@ -152,15 +177,19 @@ public function setLive(): void
return;
}

$oldAttributes = $published?->getAttributes() ?? [];
$newAttributes = $this->getAttributes();
$oldAttributes = $published?->getDraftableAttributes() ?? [];
$newAttributes = $this->getDraftableAttributes();
Arr::forget($oldAttributes, $this->getKeyName());
Arr::forget($newAttributes, $this->getKeyName());

$published->forceFill($newAttributes);
$this->forceFill($oldAttributes);

static::saved(function () use ($published) {
static::saved(function (Model $model) use ($published): void {
if ($model->isNot($this)) {
return;
}

$published->{$this->getIsPublishedColumn()} = true;
$published->{$this->getPublishedAtColumn()} ??= now();
$published->setCurrent();
Expand All @@ -171,13 +200,21 @@ public function setLive(): void
switch (true) {
case $relation instanceof HasOne:
if ($related = $this->{$relationName}) {
$published->{$relationName}()->create($related->replicate()->getAttributes());
$replicated = $related->replicate();

$method = method_exists($replicated, 'getDraftableAttributes') ? 'getDraftableAttributes' : 'getAttributes';

$published->{$relationName}()->create($replicated->$method());
}

break;
case $relation instanceof HasMany:
$this->{$relationName}()->get()->each(function ($model) use ($published, $relationName) {
$published->{$relationName}()->create($model->replicate()->getAttributes());
$replicated = $model->replicate();

$method = method_exists($replicated, 'getDraftableAttributes') ? 'getDraftableAttributes' : 'getAttributes';

$published->{$relationName}()->create($replicated->$method());
});

break;
Expand Down Expand Up @@ -250,6 +287,11 @@ public function shouldDraft(): bool
return $this->shouldSaveAsDraft;
}

public function setPublishedAttribute(): void
{
// Do nothing, everything should be handled by `setLive`
}

public function save(array $options = []): bool
{
if (
Expand Down
16 changes: 13 additions & 3 deletions src/Concerns/Publishes.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Oddvalue\LaravelDrafts\Concerns;

use Closure;
use Illuminate\Database\Eloquent\Model;
use Oddvalue\LaravelDrafts\Scopes\PublishingScope;

/**
Expand Down Expand Up @@ -31,16 +32,25 @@ public function publish(): static
return $this;
}

$this->{$this->getPublishedAtColumn()} ??= now();
$this->{$this->getIsPublishedColumn()} = true;
$this->setPublishedAttributes();

static::saved(function (Model $model): void {
if ($model->isNot($this)) {
return;
}

static::saved(function () {
$this->fireModelEvent('published');
});

return $this;
}

protected function setPublishedAttributes(): void
{
$this->{$this->getPublishedAtColumn()} ??= now();
$this->{$this->getIsPublishedColumn()} = true;
}

public function isPublished(): bool
{
return $this->{$this->getIsPublishedColumn()} ?? false;
Expand Down
9 changes: 8 additions & 1 deletion src/Facades/LaravelDrafts.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@
use Illuminate\Support\Facades\Facade;

/**
* @method static \Illuminate\Contracts\Auth\Authenticatable getCurrentUser()
* @method static void previewMode(bool $previewMode = true)
* @method static void disablePreviewMode()
* @method static bool isPreviewModeEnabled()
* @method static void withDrafts(bool $withDrafts = true)
* @method static bool isWithDraftsEnabled()
*
* @see \Oddvalue\LaravelDrafts\LaravelDrafts
* @method Model | Authenticatable getCurrentUser();
*/
class LaravelDrafts extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'laravel-drafts';
return \Oddvalue\LaravelDrafts\LaravelDrafts::class;
}
}
Loading

0 comments on commit c2b110a

Please sign in to comment.