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.
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
I used this to create a sitemap for my project. Thanks a whole lot!
Glad to help 😀
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?
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
Thanks again bruda!
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
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.
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
Thanks!
you’re welcome 🙂