Generate Sitemap in Symfony in 2 simple steps

Reading Time 2 min

What is a Sitemap?

A Sitemap is a XML file that contains a list of URLs on your website that help the Search Engines discover and crawl your website more effectively. Generating a Sitemap in Symfony is one of the trivial tasks one might encounter. There are a few bundles that might help you, although the process & configuration could be tricky, especially for beginners.
Sitemap in symfony
This tutorial aims at generating a sitemap in symfony using nothing but a simple controller action and moreover it is compatible with all major Symfony Versions (3.x, 4.x, 5.x+)

XML SiteMap Format:

<urlset> Container element/tag that defines the namespace and that encompasses all urls .
<url> URL container element that encompasses each individual <loc> tag.
<loc> tag is the only mandatory and contains the actual URL.
<lastmod>tag is optional. Includes the date when the content at the given URL was last modified.
<changefreq> Optional. Includes the frequency at which the content at the given URL changes.
– <priority> tag is optional. It is a range between 0.1 – 1.0 indicating the priority of the given URL.
(*Ref)

1. Sitemap in Symfony – Create the Controller:

Create a controller class, for instance SitemapController.php that contains a function showAction(). It will be responsible for generating the sitemap. We will only generate sitemap in XML format since it is the most widely used format, however, you can have it generated in JSON/HTML by changing a few parameters.

// AppBundle/SitemapController.php

namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class SitemapController extends Controller
{
    /**
     * @Route("/sitemap/sitemap.xml", name="sitemap", defaults={"_format"="xml"})
     */
    public function showAction(Request $request) {
        $em = $this->getDoctrine()->getManager();
        $urls = array();
        $hostname = $request->getSchemeAndHttpHost();

        // add static urls
        $urls[] = array('loc' => $this->generateUrl('home'));
        $urls[] = array('loc' => $this->generateUrl('contact_us'));
        $urls[] = array('loc' => $this->generateUrl('privacy_policy'));
        
        // add static urls with optional tags
        $urls[] = array('loc' => $this->generateUrl('fos_user_security_login'), 'changefreq' => 'monthly', 'priority' => '1.0');
        $urls[] = array('loc' => $this->generateUrl('cookie_policy'), 'lastmod' => '2018-01-01');
        
        // add dynamic urls, like blog posts from your DB
        foreach ($em->getRepository('BlogBundle:post')->findAll() as $post) {
            $urls[] = array(
                'loc' => $this->generateUrl('blog_single_post', array('post_slug' => $post->getPostSlug()))
            );
        }

        // add image urls
        $products = $em->getRepository('AppBundle:products')->findAll();
        foreach ($products as $item) {
            $images = array(
                'loc' => $item->getImagePath(), // URL to image
                'title' => $item->getTitle()    // Optional, text describing the image
            );

            $urls[] = array(
                'loc' => $this->generateUrl('single_product', array('slug' => $item->getProductSlug())),
                'image' => $images              // set the images for this product url
            );
        }
      

        // return response in XML format
        $response = new Response(
            $this->renderView('sitemap/sitemap.html.twig', array( 'urls' => $urls,
                'hostname' => $hostname)),
            200
        );
        $response->headers->set('Content-Type', 'text/xml');

        return $response;

    }

}

2. Sitemap in Symfony – Create the View

We create a twig template to handle the View. We set the correct XML namespaces and the correct XML structure required to conform to the rules of sitemaps. Then, we loop over each url in the urls[] Array and print the necessary tags completing the XML tree.

<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">

    {% for url in urls %}
        <url>{# check if hostname is not alreay in url#}
            <loc>{%if url.loc|replace({hostname:''}) == url.loc%}{{hostname}}{{url.loc}}{%else%}{{url.loc}}{%endif%}</loc>
            {% if url.lastmod is defined %}
                <lastmod>{{url.lastmod}}</lastmod>
            {% endif %}
            {% if url.changefreq is defined %}
                <changefreq>{{url.changefreq}}</changefreq>
            {% endif %}
            {% if url.priority is defined %}
                <priority>{{url.priority}}</priority>
            {% endif %}
            {% if url.image is defined and url.image is not empty %}
                <image:image>
                    <image:loc>{%if url.image.loc|replace({hostname:''}) == url.image.loc%}{{hostname}}{{url.image.loc}}{%else%}{{url.image.loc}}{%endif%}</image:loc>
                    <image:title>{{ url.image.title }}</image:title>
                </image:image>
            {% endif %}
        </url>
    {% endfor %}

</urlset>

If you followed everything correctly, the sitemap in symfony would now be accessible at http://yourhost/sitemap/sitemap.xml.

Finishing Up

You can Validate your generated sitemap at Validate XML Sitemap.
A live example of a sitemap generated by following this tutorial is found at GospelMusic. Lastly, do not forget to update your robots.txt file with the path to your sitemap.

# robots.txt 
# www.robotstxt.org/

User-agent: *
Sitemap: https://yourhost/sitemap/sitemap.xml

Your robots.txt file tells crawlers and search engines about the location of your Sitemap.
Concluding, you can have your sitemap generated automatically each time in a simple and efficient manner. Nonetheless, if you insist on using Bundles to achieve the same, here are 2 bundles PrestaSitemapBundle or Dpn’s Sitemap Bundle that could get you going.
Hope this helps 🙂


References

10 comments

      1. Since you’re monitoring this/ I’m trying to create the robot.txt file. Should this be rendered with a twig template also? Have a tutorial for this?

        1. No. This is a simple txt file and does not need to be updated dynamically. So twig is not required. You could create a simple robots.txt file at the root of your website. Then, that file should point to your sitemap as a minimum(example: https://gospelmusic.io/robots.txt ). This file can include other instructions too, but that would be specific to your site

  1. hello
    i have Symfony 3.4.44 and use this code And error


    // AppBundle/SitemapController.php namespace AppBundleController; use SensioBundleFrameworkExtraBundleConfigurationRoute; use SymfonyBundleFrameworkBundleControllerController; use SymfonyComponentHttpFoundationRequest; use SymfonyComponentHttpFoundationResponse; class SitemapController extends Controller { /** * @Route("/sitemap/sitemap.xml", name="sitemap", defaults={"_format"="xml"}) */ public function showAction(Request $request) { $em = $this->getDoctrine()->getManager(); $urls = array(); $hostname = $request->getSchemeAndHttpHost(); // add static urls $urls[] = array('loc' => $this->generateUrl('home')); $urls[] = array('loc' => $this->generateUrl('contact_us')); $urls[] = array('loc' => $this->generateUrl('privacy_policy')); // add static urls with optional tags $urls[] = array('loc' => $this->generateUrl('fos_user_security_login'), 'changefreq' => 'monthly', 'priority' => '1.0'); $urls[] = array('loc' => $this->generateUrl('cookie_policy'), 'lastmod' => '2018-01-01'); // add dynamic urls, like blog posts from your DB foreach ($em->getRepository('BlogBundle:post')->findAll() as $post) { $urls[] = array( 'loc' => $this->generateUrl('blog_single_post', array('post_slug' => $post->getPostSlug())) ); } // add image urls $products = $em->getRepository('AppBundle:products')->findAll(); foreach ($products as $item) { $images = array( 'loc' => $item->getImagePath(), // URL to image 'title' => $item->getTitle() // Optional, text describing the image ); $urls[] = array( 'loc' => $this->generateUrl('single_product', array('slug' => $item->getProductSlug())), 'image' => $images // set the images for this product url ); } // return response in XML format $response = new Response( $this->renderView('sitemap/sitemap.html.twig', array( 'urls' => $urls, 'hostname' => $hostname)), 200 ); $response->headers->set('Content-Type', 'text/xml'); return $response; } }

    you help me fix this problem

    tnx

    1. Hello, I’m sorry, i’m unable to comprehend what exact error you are facing. Also note that the code that you have posted is an exact copy-paste of my code. It will certainly not work like that because it is adapted to my database structure/routes etc. you need to replace the same according to your own database, repository, routes, etc.

      1. you help me fix this problem?
        if help me give me email and i send you ftp account and you see and check and help me
        tnx for answer

Leave a Reply