take(self::FEED_LIMIT)->map(fn ($a) => $this->artworkToItem($a));
return $this->buildResponse($channelTitle, $channelDescription, url('/'), $feedUrl, $items);
}
/**
* Build an RSS 2.0 Response from a BlogPost Eloquent collection.
* Posts must have 'author' relation preloaded.
*/
public function buildFromBlogPosts(
string $channelTitle,
string $channelDescription,
string $feedUrl,
Collection $posts,
): Response {
$items = $posts->take(self::FEED_LIMIT)->map(fn ($p) => $this->blogPostToItem($p));
return $this->buildResponse($channelTitle, $channelDescription, url('/blog'), $feedUrl, $items);
}
// ── Private helpers ───────────────────────────────────────────────────────
private function buildResponse(
string $channelTitle,
string $channelDescription,
string $channelLink,
string $feedUrl,
Collection $items,
): Response {
$xml = view('rss.channel', [
'channelTitle' => trim($channelTitle) . ' — Skinbase',
'channelDescription' => $channelDescription,
'channelLink' => $channelLink,
'feedUrl' => $feedUrl,
'items' => $items,
'buildDate' => now()->toRfc2822String(),
])->render();
return response($xml, 200, [
'Content-Type' => 'application/rss+xml; charset=utf-8',
'Cache-Control' => 'public, max-age=300',
]);
}
/** Convert an Artwork model to an RSS item array. */
private function artworkToItem(object $artwork): array
{
$link = url('/art/' . $artwork->id . '/' . ($artwork->slug ?? ''));
$thumb = method_exists($artwork, 'thumbUrl') ? $artwork->thumbUrl('sm') : null;
// Primary category from eagerly loaded relation (avoid N+1)
$primaryCategory = ($artwork->relationLoaded('categories'))
? $artwork->categories->first()
: null;
// Build HTML description embedded in CDATA
$descParts = [];
if ($thumb) {
$descParts[] = 'title, ENT_XML1) . '" />';
}
if (!empty($artwork->description)) {
$descParts[] = '
' . htmlspecialchars(strip_tags((string) $artwork->description), ENT_XML1) . '
'; } return [ 'title' => (string) $artwork->title, 'link' => $link, 'guid' => $link, 'description' => implode('', $descParts), 'pubDate' => $artwork->published_at?->toRfc2822String(), 'author' => $artwork->user?->username ?? 'Unknown', 'category' => $primaryCategory?->name, 'enclosure' => $thumb ? [ 'url' => $thumb, 'length' => 0, 'type' => 'image/jpeg', ] : null, ]; } /** Convert a BlogPost model to an RSS item array. */ private function blogPostToItem(object $post): array { $link = url('/blog/' . $post->slug); $excerpt = $post->excerpt ?? strip_tags((string) ($post->body ?? '')); return [ 'title' => (string) $post->title, 'link' => $link, 'guid' => $link, 'description' => $excerpt, 'pubDate' => $post->published_at?->toRfc2822String(), 'author' => $post->author?->username ?? 'Skinbase', 'category' => null, 'enclosure' => !empty($post->featured_image) ? [ 'url' => $post->featured_image, 'length' => 0, 'type' => 'image/jpeg', ] : null, ]; } }