Building Sitemaps
Building Sitemaps¶
This bundle helps loading documents that should go in a sitemap, extracting the information from them and rendering them to a sitemap information.
You can generate sitemaps in different formats and it is possible to provide more than one set of configurations.
Setting Up Sitemap Support¶
You need to register the route for the controller that is serving sitemaps:
- YAML
1 2 3 4 5
# app/config/routing.yml # ... sitemaps: prefix: /sitemaps resource: "@CmfSeoBundle/Resources/config/routing/sitemap.xml"
- XML
1 2 3 4 5 6 7 8 9 10
<!-- app/config/routing.xml --> <?xml version="1.0" encoding="UTF-8" ?> <routes xmlns="http://symfony.com/schema/routing" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd"> <!-- ... --> <import prefix="/sitemaps" resource="@CmfSeoBundle/Resources/config/routing/sitemap.xml" /> </routes>
- PHP
1 2 3 4 5 6
// app/config/routing.php $sitemap = $loader->import("@CmfSeoBundle/Resources/config/routing/sitemap.xml") $sitemap->addPrefix('/sitemaps'); $collection->addCollection($sitemap); return $collection;
If you only have one sitemap, you can omit the prefix. The URL of the default
sitemap is /sitemap.{_format}
. If you configure several sitemaps (see
below), you might want to use the prefix to get /sitemaps/{config}.{_format}
.
The minimal configuration is to just enable sitemap support with the default settings:
- YAML
1 2 3 4
# app/config/config.yml cmf_seo: sitemap: enabled: true
- XML
1 2 3 4 5 6 7 8
<!-- app/config/config.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services"> <config xmlns="http://example.org/schema/dic/cmf_seo"> <sitemap enabled="true"/> </config> </container>
- PHP
1 2 3 4 5 6
// app/config/config.php $container->loadFromExtension('cmf_seo', [ 'sitemap' => [ 'enabled' => true, ], ]);
Rendering Sitemaps¶
Sitemaps can be rendered in different formats. While Json sitemaps are generated by serializing the information to Json, all other formats require a template. The SeoBundle comes with default templates for HTML and XML.
You can configure other templates to change the formatting or add new formats.
The template is passed a list of UrlInformation
objects in the parameter
urls
. A good starting point to build a custom template is the default
template for that format provided by the SeoBundle bundle.
The templates are specified for a specific sitemap (see below) or in the
defaults
section:
- YAML
1 2 3 4 5 6
# app/config/config.yml cmf_seo: sitemap: defaults: templates: html: sitemap/default.html.twig
- XML
1 2 3 4 5 6 7 8 9 10 11 12
<!-- app/config/config.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services"> <config xmlns="http://example.org/schema/dic/cmf_seo"> <sitemap> <defaults> <template format="html">sitemap/default.html.twig</template> </defaults> </sitemap> </config> </container>
- PHP
1 2 3 4 5 6 7 8 9 10
// app/config/config.php $container->loadFromExtension('cmf_seo', [ 'sitemap' => [ 'defaults' => [ 'templates' => [ 'html' => 'sitemap/default.html.twig', ], ], ], ]);
The formats for the templates are not limited, you can add any format you like.
UrlInformationProvider and Customizing Sitemap Information¶
The UrlInformationProvider consists of 3 steps:
# Loader: Load models from a database or similar;
# Voter: Decide whether the loaded models should be included in the sitemap;
# Guesser: Populate the UrlInformation
from the models.
Each of these steps is chaining handlers. You can add your own handlers by
implementing the corresponding interface LoaderInterface
, VoterInterface
or GuesserInterface
respectively. Then define a service and tag it with
cmf_seo.sitemap.loader
, cmf_seo.sitemap.voter
or cmf_seo.sitemap.guesser
.
A guesser to determine when a page was last modified could look like this:
// src/AppBundle/Seo/Sitemap/LastModifiedGuesser.php
namespace AppBundle\Seo\Sitemap;
use Symfony\Cmf\Bundle\SeoBundle\Sitemap\GuesserInterface;
use AppBundle\Model\LastModifiedInterface;
class LastModifiedGuesser implements GuesserInterface
{
/**
* Updates UrlInformation with new values if they are not already set.
*
* @param UrlInformation $urlInformation The value object to update.
* @param object $object The sitemap element to get values from.
* @param string $sitemap Name of the sitemap being built.
*/
public function guessValues(UrlInformation $urlInformation, $object, $sitemap)
{
if ($urlInformation->getLastModification()) {
// guessers should not overwrite existing values
return;
}
if (!$object instanceof LastModifiedInterface) {
return;
}
// we assume that the LastModifiedInterface provides a method getLastModifiedDate that returns a DateTime object
$urlInformation->setLastModification($object->getLastModifiedDate());
}
}
A guesser should never overwrite an existing value. Rather, you control which
guesser comes first with the help of the priority
attribute to the service
tag. All default guessers, voters and loaders have a priority of -1 or -2.
You can also restrict which sitemaps a guesser (or loader or voter) is used
for. By default, it applies to all sitemaps, but you can specify a comma
separated list in the sitemaps
attribute of the tag.
The service definition looks as follows:
- YAML
1 2 3 4 5 6
# app/config/services.yml services: app.seo.sitemap.guesser.last_modified: class: AppBundle\Seo\Sitemap\LastModifiedGuesser tags: - { name: cmf_seo.sitemap.guesser, priority: 10 }
- XML
1 2 3 4 5 6 7 8 9 10 11 12
<!-- app/config/services.xml --> <?xml version="1.0" encoding="utf-8"?> <container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd"> <services> <service id="app.seo.sitemap.guesser.last_modified" class="AppBundle\Seo\Sitemap\LastModifiedGuesser"> <tag name="cmf_seo.sitemap.guesser" priority="10"/> </service> </services> </container>
- PHP
1 2 3 4 5 6
// app/config/config.php use AppBundle\Seo\Sitemap\LastModifiedGuesser; $container->register('app.seo.sitemap.guesser.last_modified', LastModifiedGuesser::class) ->addTag('cmf_seo.sitemap.guesser', [priority' => 10] ;
Loaders and voters work exactly the same, with the tags cmf_seo.sitemap.loader
and cmf_seo.sitemap.voter
. Their tags also have a priority and sitemaps
attribute.
Multiple Sitemaps¶
You can create more than one sitemap with a different configuration of loaders, voters and guessers. The most common use case is to have sub-sitemaps with limited loaders or additional voters.
If you do not specify any sitemaps, a sitemap called sitemap
will be
automatically created. If you want to keep that default map but add additional
sitemaps, you need to explicitly specify sitemap
as well:
- YAML
1 2 3 4 5 6 7 8 9
# app/config/config.yml cmf_seo: sitemap: configurations: sitemap: ~ categories: default_change_frequency: hourly templates: html: sitemap/categories.html.twig
- XML
1 2 3 4 5 6 7 8 9 10 11 12 13
<!-- app/config/config.xml --> <?xml version="1.0" encoding="UTF-8" ?> <container xmlns="http://symfony.com/schema/dic/services"> <config xmlns="http://example.org/schema/dic/cmf_seo"> <sitemap> <configuration name="sitemap"/> <configuration name="categories" default-change-frequency="hourly"> <template format="html">sitemap/categories.html.twig</template> </configuration> </sitemap> </config> </container>
- PHP
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// app/config/config.php $container->loadFromExtension('cmf_seo', [ 'sitemap' => [ 'configurations' => [ 'sitemap' => null, 'categories' => [ 'default_change_frequency' => 'hourly', 'templates' => [ 'html' => 'sitemap/categories.html.twig', ], ], ], ], ]);
You will now be able to serve a categories sitemap at
<prefix>/categories.html
.
In this example, you might register an additional voter for the categories sitemap to only keep category pages in the sitemap. All default configuration is merged into each sitemap configuration.
This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.