This commit is contained in:
Doar Mihai
2026-05-01 03:14:52 +03:00
parent 17097c3bb1
commit 96e8fa3859
9 changed files with 284 additions and 1 deletions
@@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller;
use App\Models\Article;
class BlogController extends Controller
{
public function index()
{
$articles = Article::published()
->orderByDesc('published_at')
->paginate(12);
return view('blog.index', compact('articles'));
}
public function show(Article $article)
{
abort_unless($article->is_published, 404);
return view('blog.show', compact('article'));
}
}
+16 -1
View File
@@ -66,11 +66,26 @@ class SitemapController extends Controller
{
$xml = $this->cacheService->remember(
'sitemap:jobs',
1800, // 30 minutes cache (jobs change more frequently)
1800,
fn() => $this->sitemapService->generateJobsSitemap()
);
return response($xml, 200)
->header('Content-Type', 'application/xml');
}
/**
* Generate and serve blog sitemap
*/
public function blogSitemap(): Response
{
$xml = $this->cacheService->remember(
'sitemap:blog',
3600,
fn() => $this->sitemapService->generateBlogSitemap()
);
return response($xml, 200)
->header('Content-Type', 'application/xml');
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Article extends Model
{
protected $fillable = [
'title',
'slug',
'excerpt',
'body',
'published_at',
];
protected function casts(): array
{
return [
'published_at' => 'datetime',
];
}
protected static function boot(): void
{
parent::boot();
static::creating(function (Article $article) {
if (empty($article->slug)) {
$article->slug = static::generateUniqueSlug($article->title);
}
});
static::updating(function (Article $article) {
if ($article->isDirty('title') && empty($article->slug)) {
$article->slug = static::generateUniqueSlug($article->title);
}
});
}
public static function generateUniqueSlug(string $title): string
{
$baseSlug = Str::limit(Str::slug($title), 200, '');
$slug = $baseSlug;
$counter = 1;
while (static::where('slug', $slug)->exists()) {
$slug = $baseSlug . '-' . $counter;
$counter++;
}
return $slug;
}
public function scopePublished(Builder $query): Builder
{
return $query->whereNotNull('published_at')->where('published_at', '<=', now());
}
public function getIsPublishedAttribute(): bool
{
return $this->published_at !== null && $this->published_at->isPast();
}
public function getRouteKeyName(): string
{
return 'slug';
}
}
+29
View File
@@ -2,6 +2,7 @@
namespace App\Services;
use App\Models\Article;
use App\Models\Job;
use App\Models\JobCategory;
use Illuminate\Support\Facades\URL;
@@ -92,6 +93,9 @@ class SitemapService
// Add jobs sitemap
$this->addSitemapToIndex($xml, route('sitemap.jobs'), now());
// Add blog sitemap
$this->addSitemapToIndex($xml, route('sitemap.blog'), now());
return $xml->asXML();
}
@@ -105,6 +109,31 @@ class SitemapService
$sitemapElement->addChild('lastmod', $lastmod->format('Y-m-d'));
}
/**
* Generate blog sitemap
*/
public function generateBlogSitemap(): string
{
$xml = new \SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"></urlset>');
$this->addUrl($xml, route('blog.index'), now(), 'daily', '0.8');
Article::published()
->orderByDesc('published_at')
->get(['slug', 'published_at', 'updated_at'])
->each(function (Article $article) use ($xml) {
$this->addUrl(
$xml,
route('blog.show', $article),
$article->updated_at,
'monthly',
'0.7'
);
});
return $xml->asXML();
}
/**
* Generate main sitemap (static pages and categories)
*/