-
Notifications
You must be signed in to change notification settings - Fork 102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to generate sitemap with required parameter (locale) inside route annotations? #272
Comments
I find a solution by creating a SitemapSubscriber as below, if it can help other people who face the same problem, feel free to reuse and/or adapt the code: <?php
namespace App\EventSubscriber;
use App\EventSubscriber\Model\RouteSiteMapped;
use Presta\SitemapBundle\Event\SitemapPopulateEvent;
use Presta\SitemapBundle\Service\UrlContainerInterface;
use Presta\SitemapBundle\Sitemap\Url\UrlConcrete;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
/**
* Class SitemapSubscriber
* @package App\EventSubscriber
*
* This subscriber is used to dump/generate the sitemap.
*/
class SitemapSubscriber implements EventSubscriberInterface
{
/**
* @var array<RouteSiteMapped>
*/
private array $routesToAddToSitemap;
/**
* @var UrlGeneratorInterface
*/
private UrlGeneratorInterface $urlGenerator;
/**
* @var array<string>
*/
private array $supportedLocales;
/**
* SitemapSubscriber constructor.
* @param UrlGeneratorInterface $urlGenerator
* @param string $supportedLocales
*/
public function __construct(UrlGeneratorInterface $urlGenerator, string $supportedLocales)
{
$this->urlGenerator = $urlGenerator;
$this->supportedLocales = explode('|', $supportedLocales);
// Initialise routes will be included inside sitemap
$this->initRoutesToAddToSitemap();
}
/**
* @return array<string, string>
*/
public static function getSubscribedEvents(): array
{
return [
SitemapPopulateEvent::ON_SITEMAP_POPULATE => 'populate',
];
}
public function populate(SitemapPopulateEvent $event): void
{
$this->registerStaticPages($event->getUrlContainer());
}
/**
* Initialized routes that will be included inside sitemap.
*
* Note: Add routes in this function to add a route inside sitemap.
*/
private function initRoutesToAddToSitemap(): void
{
// Add homepage
$this->routesToAddToSitemap[] = new RouteSiteMapped('homepage.display',
null, RouteSiteMapped::CHANGE_FREQ_WEEKLY, 1);
// Add aboutus page
$this->routesToAddToSitemap[] = new RouteSiteMapped('about.display',
null, RouteSiteMapped::CHANGE_FREQ_WEEKLY, 0.5);
// Add privacy policy page
$this->routesToAddToSitemap[] = new RouteSiteMapped('privacyPolicy.display',
null, RouteSiteMapped::CHANGE_FREQ_WEEKLY, 0.5);
// Add cookie consent page
$this->routesToAddToSitemap[] = new RouteSiteMapped('cookie.display',
null, RouteSiteMapped::CHANGE_FREQ_MONTHLY, 0.3);
}
/**
* @param UrlContainerInterface $urls
*/
private function registerStaticPages(UrlContainerInterface $urls): void
{
// Loop all available locales
foreach ($this->supportedLocales as $locale)
{
// Loop all routes that we want to add inside sitemap
foreach ($this->routesToAddToSitemap as $routeSiteMapped)
{
$urls->addUrl(
new UrlConcrete(
$this->urlGenerator->generate(
$routeSiteMapped->getRouteName(),
['_locale' => $locale],
UrlGeneratorInterface::ABSOLUTE_URL
),
$routeSiteMapped->getLastmod(),
$routeSiteMapped->getChangefreq(),
$routeSiteMapped->getPriority()
),
$routeSiteMapped->getSection()
);
}
}
}
} the code above this this: # services.yaml
parameters:
app.supported_locales: 'en|fr'
router.request_context.host: 'my-website.com'
router.request_context.scheme: 'http'
services:
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind:
$supportedLocales: '%app.supported_locales%' <?php
namespace App\EventSubscriber\Model;
use DateTimeInterface;
/**
* Class RouteSiteMapped
* @package App\EventSubscriber\Model
*
* This class is a representation of route which will be includes inside generated sitemap.
*/
class RouteSiteMapped
{
/**
* Available values for changefreq used in sitemap.
*/
const CHANGE_FREQ_WEEKLY = 'weekly';
const CHANGE_FREQ_MONTHLY = 'monthly';
/**
* @var string The route name (cf. controller annotation).
*/
private string $routeName;
/**
* @var DateTimeInterface|null The data of last modification used in sitemap.
*/
private ?DateTimeInterface $lastmod;
/**
* @var string|null The change frequency value used in sitemap.
*/
private ?string $changefreq;
/**
* @var float|null The priority of the url used in sitemap.
*/
private ?float $priority;
/**
* @var string The section used in sitemap for the url.
*/
private string $section;
/**
* RouteSiteMapped constructor.
* @param string $routeName
* @param DateTimeInterface|null $lastmod
* @param string|null $changefreq
* @param float|null $priority
* @param string $section
*/
public function __construct(string $routeName,
?DateTimeInterface $lastmod,
?string $changefreq,
?float $priority,
string $section = 'default')
{
$this->routeName = $routeName;
$this->lastmod = $lastmod;
$this->changefreq = $changefreq;
$this->priority = $priority;
$this->section = $section;
}
/**
* @return string
*/
public function getRouteName(): string
{
return $this->routeName;
}
/**
* @return DateTimeInterface|null
*/
public function getLastmod(): ?DateTimeInterface
{
return $this->lastmod;
}
/**
* @return string|null
*/
public function getChangefreq(): ?string
{
return $this->changefreq;
}
/**
* @return float|null
*/
public function getPriority(): ?float
{
return $this->priority;
}
/**
* @return string
*/
public function getSection(): string
{
return $this->section;
}
} |
Hey ! Sorry I'm late... You're right, the bundle register automatically routes that has no parameters, because this is not possible to guess what are possible values. So you are right, for these kind of routes, you need to register a listener that will stick to your requirements/convention. But in your case, we may have something that got you covered. Maybe you will find some inspiration here if you want to try something else. |
Thank you @yann-eugone Interesting! Does it means I just have to declare this config and my locales value will be populated automatically? presta_sitemap:
alternate:
enabled: true
default_locale: en
locales: [en, fr]
i18n: symfony |
This system was designed to work with Symfony routing internationalisation : https://symfony.com/blog/new-in-symfony-4-1-internationalized-routing But you might try with this config and see what is happening 😉 But keep in mind that the result won't be the same : this will not register 2 routes at the root of you sitemap. |
I tested with this config but i still have the same error. :-( Your link is about Symfony 4.1 but it for my project i am using Symfony 5.2 and I followed this documentation to implement localizations: https://symfony.com/doc/current/the-fast-track/en/28-intl.html#internationalizing-urls The advantage of using "_locale" parameter in route path instead of prefix is that I can save in session the current locale used, and also determine the prefered locale for the current user using function $request->getPreferredLanguage(...) when the user try to access to my website without specifying prefix (ie. https://mywebsite.com is redirecting to https://mywebsite.com/[prefered_local] using a custom LocalSubscriber (code below). <?php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Class LocaleSubscriber
* @package App\EventSubscriber
*
* This subscriber is managing the locale in User session and detect
* the preferred language for the user during the first connection.
*/
class LocaleSubscriber implements EventSubscriberInterface
{
private string $defaultLocale;
/**
* @var array<string>
*/
private array $supportedLocales;
/**
* LocaleSubscriber constructor.
* @param string $defaultLocale
* @param string $supportedLocales
*/
public function __construct(string $supportedLocales, string $defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
$this->supportedLocales = explode('|', $supportedLocales);
}
/**
* @param RequestEvent $event
*/
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
// If not previous session found
if (!$request->hasPreviousSession()) {
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $request->attributes->get('_locale'))
{
$request->getSession()->set('_locale', $locale);
}else{
// try to see if user have preferred language
if($preferredLanguage = $request->getPreferredLanguage($this->supportedLocales))
{
$request->getSession()->set('_locale', $preferredLanguage);
// Set the local with the prefered language
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
}else{
// Here, a session has been found
// try to see if the locale has been set as a _locale routing parameter
if ($locale = $request->attributes->get('_locale'))
{
$request->getSession()->set('_locale', $locale);
} else {
// if no explicit locale has been set on this request,
// use one from the session
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
}
/**
* @inheritDoc
*/
public static function getSubscribedEvents()
{
return [
// must be registered before (i.e. with a higher priority than) the default Locale listener
KernelEvents::REQUEST => [['onKernelRequest', 20]],
];
}
} Maybe i did wrong, do not hesitate to tell me in that case. ^^' |
My link is just the news Symfony created when support of route internationalisation was introduced : it's working like this since 4.1. What you did is prefixing URLs, which is fine, but it is not an internationalized routing system. As I said, I was not sure the system would be compatible with what you did, now we know : it is not. |
Yes, thank you Yann. It is still interesting to see another solution. :) |
Hello,
Context:
To do that, below how my controller is declared:
Below the declaration of app.supported_locales parameter in services.yaml:
Problem:
When i execute the command to dump the sitemap, i have an error message:
So i tried to execute this command to try to give the required parameter but it the same:
Questions:
Could I use the concept of "dynamic route usage" as a solution?
I mean, using Subscriber instead of route annotations? In this case, how to determine the supported sitemap parameters (mastmod, changefreq and priority)?
Thanks for your help
The text was updated successfully, but these errors were encountered: