Adds a public "درباره ما" page at /درباره-ما reusing the landing theme
(same webpack entry, header, footer, registration modal). Section ids
match the header anchors so the nav works on this page too.
The explicit route takes precedence over LandingController's catch-all
/{slug}, which is registered with priority -10.
Also links it from the header nav and footer quick links, and lists it
in sitemap.xml.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
70 lines
2.5 KiB
PHP
70 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Shared\Controller;
|
|
|
|
use App\Shared\Landing\LandingRegistry;
|
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
use Symfony\Component\Routing\Attribute\Route;
|
|
|
|
class SeoController extends AbstractController
|
|
{
|
|
public function __construct(private readonly LandingRegistry $landings) {}
|
|
|
|
#[Route('/robots.txt', name: 'seo_robots', methods: ['GET'])]
|
|
public function robots(Request $request): Response
|
|
{
|
|
if ($this->isStaging($request)) {
|
|
$body = "User-agent: *\nDisallow: /\n";
|
|
} else {
|
|
$base = $request->getSchemeAndHttpHost();
|
|
$body = "User-agent: *\n"
|
|
. "Disallow: /admin\n"
|
|
. "Disallow: /api\n"
|
|
. "Allow: /\n\n"
|
|
. "Sitemap: {$base}/sitemap.xml\n";
|
|
}
|
|
|
|
return new Response($body, Response::HTTP_OK, ['Content-Type' => 'text/plain; charset=UTF-8']);
|
|
}
|
|
|
|
#[Route('/sitemap.xml', name: 'seo_sitemap', methods: ['GET'])]
|
|
public function sitemap(Request $request): Response
|
|
{
|
|
$base = $request->getSchemeAndHttpHost();
|
|
|
|
$xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n"
|
|
. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n"
|
|
. $this->urlNode("{$base}/", 'weekly', '1.0')
|
|
. $this->urlNode($base . '/' . rawurlencode(AboutController::SLUG), 'yearly', '0.5');
|
|
|
|
// اسلاگها فارسیاند و باید percent-encode شوند، وگرنه XML نامعتبر است.
|
|
foreach ($this->landings->all() as $page) {
|
|
$xml .= $this->urlNode($base . '/' . rawurlencode($page->slug), 'monthly', '0.8');
|
|
}
|
|
|
|
$xml .= '</urlset>' . "\n";
|
|
|
|
return new Response($xml, Response::HTTP_OK, ['Content-Type' => 'application/xml; charset=UTF-8']);
|
|
}
|
|
|
|
private function urlNode(string $loc, string $changefreq, string $priority): string
|
|
{
|
|
return ' <url>' . "\n"
|
|
. ' <loc>' . htmlspecialchars($loc, ENT_XML1) . '</loc>' . "\n"
|
|
. " <changefreq>{$changefreq}</changefreq>\n"
|
|
. " <priority>{$priority}</priority>\n"
|
|
. ' </url>' . "\n";
|
|
}
|
|
|
|
private function isStaging(Request $request): bool
|
|
{
|
|
$host = $request->getHost();
|
|
|
|
return str_ends_with($host, '.ddev.site')
|
|
|| str_contains($host, 'localhost')
|
|
|| str_starts_with($host, '127.0.0.1');
|
|
}
|
|
}
|