5. Sitemap

The SonataSeoBundle provides a support for static sitemap generation. You can either create services or provide raw SQL queries.

5.1. Service Definition

The service must implement the SourceIteratorInterface from the sonata-project/exporter library. The iterator will be used to generate the file. The iterator must not store all data as property to avoid memory issue. Data must be fetch one by one using a buffered connection (from mysql and not php which is not default behavior):

class MyCustomSitemapIterator implements SourceIteratorInterface
{
    protected $key;

    protected $stop;

    protected $current;

    public function __construct($stop = 1000)
    {
        $this->stop = $stop;
    }

    public function current()
    {
        return $this->current;
    }

    public function next()
    {
        $this->key++;
        $this->current = [
            'url'  => '/the/path/to/target',
            'lastmod'    => '01.01.2020',
            'changefreq' => 'weekly',
            'priority'   => 0.5
        ];
    }

    public function key()
    {
        return $this->key;
    }

    public function valid()
    {
        return $this->key < $this->stop;
    }

    public function rewind()
    {
        $this->key = 0;
    }
}

5.2. Raw Performance with Query

You will need to configure:
  • a Doctrine connection

  • a route

  • default parameters used by the route

  • the query which must contains all information required by a sitemap: lastmod, changefreq and priority

The url is generated by using route and parameters settings.

For performance reasons and memory usage there is no model hydrated while generating the sitemap. So if the sitemap requires specific rules, they must be expressed in the WHERE condition. The query must select fields to be used in the route.

The following code is an extract of the query required to generate a valid sitemap for the SonataNewsBundle

SELECT
    CONCAT_WS('/', YEAR(created_at), MONTH(created_at), DAY(created_at), slug) as url ,
    DATETIME(updated_at) as lastmod,
    'weekly' as changefreq,
    '0.5' as priority
FROM news__post
WHERE
        enabled = 1
    AND (publication_date_start IS NULL OR publication_date_start <= NOW())

Please note: the changefreq and the priority fields are optional.

5.3. Configuration example

Sitemap configuration obviously depends on the bundle, page types & custom routes you choose to expose.

service:
    app.my_custom_sitemap_service:
        class: MyCustomSitemapIterator

sonata_seo:
    # ...
    sitemap:
        services:
            - app.my_custom_sitemap_service

        doctrine_orm:
            # media
            - types: [image]
              connection: doctrine.dbal.default_connection
              route: sonata_media_view
              parameters: {id: null}
              query: |
                  SELECT
                    id,
                    updated_at as lastmod,
                    'weekly' as changefreq,
                    '0.5' as priority
                  FROM media__media
                  WHERE enabled = true
            # blog post
            - group: "news"
              connection: doctrine.dbal.default_connection
              route: sonata_news_view
              parameters: {url: null}
              query: |
                  SELECT
                    CONCAT_WS('/', YEAR(created_at), MONTH(created_at), DAY(created_at), slug) as url ,
                    updated_at as lastmod,
                    'weekly' as changefreq,
                    '0.5' as priority
                  FROM news__post
                  WHERE enabled = true
                    AND (publication_date_start IS NULL OR publication_date_start <= NOW())
            # page - works only for one site, please adapt the code if required
            - connection: doctrine.dbal.default_connection
              route: page_slug
              parameters: {path: null}
              query: |
                  SELECT
                    url as path,
                    updated_at as lastmod,
                    'weekly' as changefreq,
                    '0.5' as priority
                  FROM page__snapshot
                  WHERE route_name = 'page_slug'
                    AND enabled = true
                    AND (publication_date_start IS NULL OR publication_date_start <= NOW())
                    AND (publication_date_end IS NULL OR publication_date_end >= NOW())
            # product categories
            - connection: doctrine.dbal.default_connection
              route: sonata_catalog_category
              parameters: {category_id: null, category_slug: null}
              query: |
                  SELECT
                    id as category_id,
                    slug as category_slug,
                    updated_at as lastmod,
                    'weekly' as changefreq,
                    '0.5' as priority
                  FROM classification__category
                  WHERE enabled = true
            # products
            - connection: doctrine.dbal.default_connection
              route: sonata_product_view
              parameters: {productId: null, slug: null}
              query: |
                  SELECT
                    id as productId,
                    slug,
                    updated_at as lastmod,
                    'weekly' as changefreq,
                    '0.5' as priority
                  FROM product__product
                  WHERE enabled = true

5.4. Usage

  • Generate the sitemap:

    bin/console sonata:seo:sitemap web sonata-project.org
    

Note

The command will generate all files in a temporary directory to avoid issue will files are indexed. Once the files are generated then the files will be copied to the web directory. The sonata-project.org argument will be used to prefix url with the provided domain.