I created a website for a construction company where they can sell their properties, I built it using Elixir and the Phoenix framework.
So I was searching on Google how to implement a sitemap.xml but all I can find was this lib called sitemap
, so I tried it but...
On my website, I have a single page for each property that is created, and in this lib, I have to run a command to generate a new XML file every time I create a property. Also, I can't change the <lastmod>
because it always uses the date where I generated the sitemap.
So I thought to create my own dynamic sitemap.xml and I will share with you how I did it, it's very simple :)
First of all, I created my new route on the file router.ex
:
get "/sitemap.xml", SitemapController, :index
And then I created a new file lib/myapp_web/controllers/sitemap_controller.ex
with this code:
defmodule MyAppWeb.SitemapController do
use MyAppWeb, :controller
plug :put_layout, false
alias MyApp.Properties
def index(conn, _params) do
properties = Properties.list_properties()
conn
|> put_resp_content_type("text/xml")
|> render("index.xml", properties: properties)
end
end
Cool :) Now we have to create the file lib/myapp_web/templates/sitemap/index.xml.eex
that will be our sitemap:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc><%= Routes.home_url(@conn, :index) %></loc>
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc><%= Routes.about_url(@conn, :index) %></loc>
<changefreq>never</changefreq>
<priority>0.3</priority>
</url>
<url>
<loc><%= Routes.apartment_url(@conn, :index) %></loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc><%= Routes.house_url(@conn, :index) %></loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc><%= Routes.lot_url(@conn, :index) %></loc>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc><%= Routes.contact_url(@conn, :index) %></loc>
<changefreq>never</changefreq>
<priority>0.5</priority>
</url>
<%= for property <- @properties do %>
<url>
<loc><%= Routes.property_url(@conn, :show, property.slug) %></loc>
<lastmod><%= format_date(property.updated_at) %></lastmod>
<changefreq>weekly</changefreq>
<priority>1</priority>
</url>
<% end %>
</urlset>
You can see that I added manually all the routes I have on my website: home, about, apartments, houses, lots and contact. And then I added all the dynamic pages for each property I have created.
And I have the <lastmod>
with the correct date, using the updated_at
. But we still have to create this function called format_date so we create the file lib/myapp_web/views/sitemap_view.ex
:
defmodule MyAppWeb.SitemapView do
use MyAppWeb, :view
def format_date(date) do
date
|> DateTime.from_naive!("Etc/UTC")
|> DateTime.to_date()
|> to_string()
end
end
And that's it! Now you can now add this URL: yoursite.com/sitemap.xml
on the Google Console and it'll just work fine :)
But don't forget to add some tests for this! So we can do this on the file test/myapp_web/controllers/sitemap_controller_test.exs
:
defmodule MyAppWeb.SitemapControllerTest do
use MyAppWeb.ConnCase
describe "GET /sitemap.xml" do
test "accesses the sitemap in format xml", %{conn: conn} do
property = property_fixture()
conn = get(conn, "/sitemap.xml")
assert response_content_type(conn, :xml)
assert response(conn, 200) =~ ~r/<loc>.*#{property.slug}<\/loc>/
end
end
end
In this function property_fixture()
you have to create a new property.
And then a test to our view in test/myapp_web/views/sitemap_view_test.exs
:
defmodule MyAppWeb.SitemapViewTest do
use MyAppWeb.ConnCase, async: true
alias MyAppWeb.SitemapView
test "format_date/1" do
assert SitemapView.format_date(~N[2019-07-08 13:15:00]) == "2019-07-08"
end
end
And that's all! :)
Top comments (2)
Thank you for sharing this. Super useful! Just adding it to my site now.
👍