From ce27dbdd1eb206a9b7e7ac9b5a1c77778ed75e03 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 30 Oct 2026 12:25:24 +0800 Subject: [PATCH] Return same-site illustration URLs in serialized articles --- app/Models/Articles.php | 10 ++++++++++ app/Services/IllustrationPool.php | 5 +++-- tests/Feature/LocalIllustrationsTest.php | 8 +++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/Models/Articles.php b/app/Models/Articles.php index 9c32d4b4..1c940349 100755 --- a/app/Models/Articles.php +++ b/app/Models/Articles.php @@ -20,6 +20,16 @@ public function getSThumbAttribute($value) return \App\Services\IllustrationPool::thumbnail($this->attributes, $value); } + public function attributesToArray(): array + { + $attributes = parent::attributesToArray(); + // Laravel's snake-case mutator discovery misses the legacy camelCase column. + if (array_key_exists('sThumb', $attributes)) { + $attributes['sThumb'] = $this->getAttribute('sThumb'); + } + return $attributes; + } + protected $appends = ['s_created_at_diff']; use Searchable; diff --git a/app/Services/IllustrationPool.php b/app/Services/IllustrationPool.php index 233c991c..a13b5763 100644 --- a/app/Services/IllustrationPool.php +++ b/app/Services/IllustrationPool.php @@ -82,7 +82,7 @@ public static function assign(string $pool, array $keys): array $selections = self::assignSource($pool, $keys); if (LocalIllustrations::enabled()) { foreach ($selections as $key => &$image) { - $image['url'] = app(LocalIllustrations::class)->storeSelection($pool, $key, $image); + $image['url'] = url(app(LocalIllustrations::class)->storeSelection($pool, $key, $image)); } unset($image); } @@ -112,7 +112,8 @@ public static function thumbnail(array $article, ?string $original): ?string $pool = config('illustrations.categories.'.($article['iCategoryId'] ?? '')); if (!$pool || empty($article['sSlug'])) return $original; if (LocalIllustrations::enabled()) { - return app(LocalIllustrations::class)->thumbnail($pool, $article['sSlug']); + $url = app(LocalIllustrations::class)->thumbnail($pool, $article['sSlug']); + return $url ? url($url) : null; } if (!self::enabled()) return $original; try { diff --git a/tests/Feature/LocalIllustrationsTest.php b/tests/Feature/LocalIllustrationsTest.php index 48aa5d8a..455c8a27 100644 --- a/tests/Feature/LocalIllustrationsTest.php +++ b/tests/Feature/LocalIllustrationsTest.php @@ -71,7 +71,9 @@ public function test_content_hash_deduplicates_assets_and_page_reads_make_no_htt config(['illustrations.enabled' => false]); // Frontend survives Resource being disabled. for ($i = 0; $i < 5; $i++) { $a = new Articles(['iCategoryId' => 1, 'sSlug' => 'fn1', 'sThumb' => 'https://old.invalid/image']); - $this->assertSame($url, $a->sThumb); + $this->assertSame(url($url), $a->sThumb); + $this->assertSame($a->sThumb, $a->toArray()['sThumb']); + $this->assertSame($a->sThumb, json_decode($a->toJson(), true)['sThumb']); } Http::assertNothingSent(); } @@ -103,9 +105,9 @@ public function test_new_article_assignment_downloads_before_returning_local_url $this->fakeImages(); Http::fake(['https://resource-v2.gemcook.net/api/v2/pools/freenode/selections' => Http::response(['data' => ['selections' => ['fn1' => $this->image()]]])]); $first = IllustrationPool::forArticle('freenode', 'fn1'); - $this->assertSame('/images/illustrations/'.$this->sha.'.png', $first); + $this->assertSame(url('/images/illustrations/'.$this->sha.'.png'), $first); $this->assertSame($first, IllustrationPool::forArticle('freenode', 'fn1')); - $this->assertSame($first, app(LocalIllustrations::class)->thumbnail('freenode', 'fn1')); + $this->assertSame($first, url(app(LocalIllustrations::class)->thumbnail('freenode', 'fn1'))); Http::assertSentCount(4); // Two assignment requests; content downloaded once. }