From b81f0b00dd65c7d1e50cfd6bbd0de508cfe1526f Mon Sep 17 00:00:00 2001 From: Alfonso Bribiesca Date: Mon, 30 Oct 2023 04:29:15 -0600 Subject: [PATCH] refactor: ignore markdown on metadata (#287) --- app/Models/Article.php | 11 ++- app/Observers/ArticleObserver.php | 18 +++++ tests/App/Models/ArticleTest.php | 20 ++++++ tests/App/Observers/ArticleObserverTest.php | 75 +++++++++++++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) diff --git a/app/Models/Article.php b/app/Models/Article.php index e331f6aa6..18795ca93 100644 --- a/app/Models/Article.php +++ b/app/Models/Article.php @@ -13,6 +13,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; use Spatie\Image\Manipulations; @@ -154,7 +155,15 @@ public function scopeWithFeaturedCollections(Builder $query): Builder public function metaDescription(): string { - return $this->meta_description ?? Str::limit(strip_tags($this->content), 157); + return Cache::rememberForever("article:{$this->id}:meta_description", function () { + if (boolval($this->meta_description)) { + return $this->meta_description; + } + + $rawContent = $this->renderedMarkdown(); + + return Str::limit(trim(preg_replace("/\r?\n/", ' ', html_entity_decode(strip_tags($rawContent)))), 157); + }); } public function isNotPublished(): bool diff --git a/app/Observers/ArticleObserver.php b/app/Observers/ArticleObserver.php index 3d1ebc67f..51fb62e73 100644 --- a/app/Observers/ArticleObserver.php +++ b/app/Observers/ArticleObserver.php @@ -6,17 +6,22 @@ use App\Jobs\ConvertArticleToSpeech; use App\Models\Article; +use Illuminate\Support\Facades\Cache; class ArticleObserver { public function created(Article $article): void { $this->prepareArticleSpeech($article); + + $this->forgetMetaDescriptionCache($article); } public function updated(Article $article): void { $this->prepareArticleSpeech($article); + + $this->forgetMetaDescriptionCache($article); } private function prepareArticleSpeech(Article $article): void @@ -39,4 +44,17 @@ private function prepareArticleSpeech(Article $article): void ConvertArticleToSpeech::dispatch($article); } + + private function forgetMetaDescriptionCache(Article $article): void + { + if ($article->isNotPublished()) { + return; + } + + if (! ($article->wasRecentlyCreated || $article->wasChanged(['meta_description', 'content', 'is_published']))) { + return; + } + + Cache::forget("article:{$article->id}:meta_description"); + } } diff --git a/tests/App/Models/ArticleTest.php b/tests/App/Models/ArticleTest.php index 2821db2f6..6c21c3c51 100644 --- a/tests/App/Models/ArticleTest.php +++ b/tests/App/Models/ArticleTest.php @@ -4,6 +4,7 @@ use App\Models\Article; use App\Models\Collection; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\DB; it('should create an article', function () { @@ -49,6 +50,25 @@ expect($article->metaDescription())->toBe('This is the content'); }); +it('removes markdown in meta description', function () { + $article = Article::factory()->create([ + 'meta_description' => null, + 'content' => "### This is the content\n\nwith some *markdown*\n", + ]); + + expect($article->metaDescription())->toBe('This is the content with some markdown'); +}); + +it('gets meta description from cache', function () { + $article = Article::factory()->create(); + + Cache::shouldReceive('rememberForever') + ->with('article:'.$article->id.':meta_description', Closure::class) + ->andReturn('This is the content'); + + expect($article->metaDescription())->toBe('This is the content'); +}); + it('truncates the content if used as meta description', function () { $article = Article::factory()->create([ 'meta_description' => null, diff --git a/tests/App/Observers/ArticleObserverTest.php b/tests/App/Observers/ArticleObserverTest.php index 451311d09..24b483e9d 100644 --- a/tests/App/Observers/ArticleObserverTest.php +++ b/tests/App/Observers/ArticleObserverTest.php @@ -5,9 +5,12 @@ use App\Jobs\ConvertArticleToSpeech; use App\Models\Article; use Illuminate\Support\Facades\Bus; +use Illuminate\Support\Facades\Cache; beforeEach(function () { config(['dashbrd.text_to_speech.enabled' => true]); + + Article::truncate(); }); it('should not start audio conversion if text-to-speech is not enabled', function () { @@ -115,3 +118,75 @@ ConvertArticleToSpeech::enable(); }); + +it('should not clear the meta description cache if article is not published', function () { + Cache::shouldReceive('forget')->never(); + + Article::factory()->create([ + 'content' => 'Hello World', + 'meta_description' => 'Hello World', + 'published_at' => null, + ]); +}); + +it('should not clear the meta description cache if article content or meta description did not change', function () { + $article = Article::factory()->create([ + 'content' => 'Hello World', + 'meta_description' => 'Hello World', + 'published_at' => now()->subMinutes(2), + ])->fresh(); + + Cache::shouldReceive('forget')->never(); + + $article->update([ + 'title' => 'Updated title', + ]); +}); + +it('should clear the meta description cache when an article created', function () { + Cache::shouldReceive('forget')->with('article:1:meta_description')->once(); + + Article::factory()->create([ + 'content' => 'Hello World', + 'published_at' => now()->subMinutes(2), + ]); +}); + +it('should clear the meta description cache when an article content is updated', function () { + $article = Article::factory()->create([ + 'content' => 'Hello World', + 'published_at' => now()->subMinutes(2), + ]); + + Cache::shouldReceive('forget')->with('article:1:meta_description')->once(); + + $article->update([ + 'content' => 'Updated content', + ]); +}); + +it('should clear the meta description cache when an article meta_description is updated', function () { + $article = Article::factory()->create([ + 'content' => 'Hello World', + 'published_at' => now()->subMinutes(2), + ]); + + Cache::shouldReceive('forget')->with('article:1:meta_description')->once(); + + $article->update([ + 'meta_description' => 'Updated content', + ]); +}); + +it('should clear the meta description cache when an article is published', function () { + $article = Article::factory()->create([ + 'content' => 'Hello World', + 'published_at' => null, + ]); + + Cache::shouldReceive('forget')->with('article:1:meta_description')->once(); + + $article->update([ + 'published_at' => now()->subMinutes(2), + ]); +});