Espera un momento ...
¿Te gustarÃa llevar mi curso de Laravel, gratis?
Sólo debes ingresar tus datos:
En esta ocasión vamos a ver cómo generar un sitemap para nuestros proyectos Laravel.
Pero empecemos respondiendo algunas preguntas frecuentes.
Un sitemap es un archivo XML (o bien una ruta que devuelve una respuesta en formato XML), indicando las distintas páginas que están presentes en nuestro sitio web.
No todas, pero sà las más importantes: aquellas que queremos que sean indexadas por los buscadores (como Google).
¿Por qué es importante?
Muy buena pregunta.
Si no tienes un sitemap, Google puede indexar tu sitio web de todas formas, pero, puede tardar más tiempo o bien puede no indexar todas las secciones de tu sitio web.
Los sitemaps son importantes para mejorar el SEO de tu página. SEO significa "Search Engine Optimization", y hace referencia a qué tan bien posicionado se encuentra tu sitio web en los motores de búsqueda.
De hecho, si buscas analizadores de SEO (existen varios gratuitos), verás que la gran mayorÃa (si no es que todos), consideran como un factor importante contar con un sitemap. Si no tienes uno, ya tienes unos puntos menos en tu evaluación.
Ten en cuenta que:
Un sitemap es la solución ideal ante dicho escenario.
Como debes haber notado, hoy en dÃa existen muchos paquetes para Laravel.
Sin embargo, en esta ocasión vamos a escribir nuestra propia solución. De paso que conocemos más acerca de la estructura de un sitemap.
Esto depende de las caracterÃsticas de tu proyecto.
Por ejemplo:
Lo que no debemos incluir en un sitemap es más bien:
Rutas que ejecutan acciones. Por ejemplo: si tenemos rutas que guardan preferencias, como agregar a favoritos, que activan un "modo nocturno" en nuestra página, o que no devuelven una vista, no tiene sentido considerarlas como parte de nuestro sitemap.
Si estás usando los verbos POST, PUT, PATCH y DELETE como es debido, te resultará más sencillo excluir estas rutas.
Un sitemap se define en formato XML y aquà podemos ver un ejemplo usando los datos que tÃpicamente se indican por cada URL:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://programacionymas.com/</loc>
<lastmod>2019-07-31</lastmod>
<changefreq>monthly</changefreq>
<priority>1.0</priority>
</url>
</urlset>
En el ejemplo anterior tenemos una única etiqueta url
al interior del urlset
. La idea es tener una por cada página pública de nuestro sitio.
Seguro que ya imaginas el significado de cada dato en su interior:
loc
: Es la URL de la página.lastmod
: Fecha de su última modificación.changefreq
: Frecuencia con que se actualiza.priority
: La importancia de esta página.Respecto a la prioridad, debe asignarse 1.0
a la página más importante de nuestro sitio (generalmente la página de inicio).
La prioridad para las demás páginas será menor, según su importancia, en este sentido: 0.9
, 0.8
, 0.7
, 0.6
, 0.5
(los valores válidos se expresan en décimas).
Sobre la frecuencia de cambio, tenemos varios valores disponibles. Los menciono aquà para que los tengas a tu alcance:
En proyectos medianamente grandes, existe una tendencia a organizar nuestras rutas en distintos archivos. Entonces mi sugerencia es:
¿A qué me refiero con esto último?
/planes
que lista los planes de suscripción disponibles./blog/{article}
representa a cada uno de los artÃculos de nuestro blog.Una vez hecho ello, lo siguiente es:
A veces encontramos rutas que redirigen hacia otras, porque en algún momento decidimos cambiarlas.
¿Cuál de las rutas agregamos al sitemap? ¿La antigua, la ruta nueva o ambas?
Para tomar una decisión necesitamos saber si aún recibimos visitas en las rutas antiguas.
Si dichas rutas ya no son visitadas, podemos reciclarlas.
¿Pero cómo sabemos si una página en especÃfico de nuestro sitio recibe visitas o no?
Para ello podemos apoyarnos de Google Analytics (si no lo usas, te recomiendo empezar a hacerlo, para que tengas estadÃsticas de qué secciones son las más visitadas).
Los pasos a seguir son 2:
Un subdominio resulta muy útil para separar contenido: porque la temática es distinta, o porque se trata de una variante que necesita su propio espacio.
Por ejemplo, Google usa subdominios distintos para sus productos:
news.google.com
: Google Noticiasmaps.google.com
: Google Mapsplay.google.com
: Google Play StoreEntonces: lo recomendable es tener un sitemap por cada subdominio.
Si un subdominio es dado de baja, su sitemap correspondiente desaparecerá, pero esto no afectará al sitemap de los demás subdominios (ni al sitemap del dominio principal).
A estas alturas debes tener identificadas las rutas que quieres considerar en el sitemap de tu sitio web.
A continuación te presento un ejemplo simplificado, con relación a este mismo sitio web, sobre el cual navegas.
Dominio principal
Ruta | Descripción | Tipo de ruta | Prioridad |
---|---|---|---|
/ | Página de inicio. | Única | 1.0 |
/asesoria | Información acerca de sesiones de asesorÃa. | Única | 0.8 |
/contacto | Formulario de contacto. | Única | 0.8 |
/becas | Información sobre becas y descuentos. | Única | 0.7 |
/@{username} | Páginas de perfil para cada usuario. | Múltiple | 0.7 |
/blog | Últimos artÃculos. CategorÃas y etiquetas. | Única | 0.8 |
/blog/{slug} | Representa a cada artÃculo del blog. | Múltiple | 0.9 |
/categorias | Todas las categorÃas del blog. | Única | 0.7 |
/categorias/{slug} | Lista de artÃculos para una categorÃa. | Múltiple | 0.8 |
/tags | Todas las etiquetas del blog. | Única | 0.7 |
/tags/{name} | ArtÃculos asociados con la etiqueta seleccionada. | Múltiple | 0.8 |
/portafolio | Listado de algunas aplicaciones desarrolladas. | Única | 0.6 |
/portafolio/{slug} | Información detallada sobre una aplicación. | Múltiple | 0.6 |
/{slug} | Páginas informativas, como guÃas o anuncios. | Múltiple | 0.8 |
Subdominio "series"
Ruta | Descripción | Tipo de ruta | Prioridad |
---|---|---|---|
/ | Página principal. | Única | 1.0 |
/categorias | Listado de categorÃas (o tecnologÃas). | Única | 0.8 |
/categorias/{slug} | Lista de series pertenecientes a una categorÃa. | Múltiple | 0.8 |
/lecciones/{episode} | Lección independiente. | Múltiple | 0.7 |
/{slug} | Página de serie: capÃtulos e información. | Múltiple | 0.9 |
/{slug}/{episode} | Un episodio (o capÃtulo) perteneciente a una serie. | Múltiple | 0.8 |
Bien.
Llegados a este punto lo primero que haremos es definir una ruta para mostrar allà nuestro sitemap:
Route::get('/sitemap.xml', 'SiteMapController@index');
Ten en cuenta que, aunque la ruta termina en .xml
es una ruta declarada como cualquier otra, y no un archivo.
La ruta puede llamarse de manera diferente si lo prefieres, por ejemplo /sitemap
, pero si ese es el caso, deberás indicar ello en el archivo robots.txt para que sea accesible por los motores de búsqueda (y sean conscientes de ello).
Entonces, vamos a empezar creando un controlador nuevo:
php artisan make:controller SiteMapController
Y definiendo un método index
en su interior:
class SiteMapController extends Controller
{
private $siteMap;
public function index()
{
$this->siteMap = new SiteMap();
$this->addUniqueRoutes();
$this->addArticles();
$this->addCategories();
$this->addDynamicPages();
$this->addTags();
$this->addProjects();
$this->addProfilePages();
return response($this->siteMap->build(), 200)
->header('Content-Type', 'text/xml');
}
private function addUniqueRoutes()
{
// ...
}
private function addProfilePages()
{
// ...
}
private function addArticles()
{
// ...
}
private function addCategories()
{
// ...
}
private function addTags()
{
// ...
}
private function addProjects()
{
// ...
}
private function addDynamicPages()
{
// ...
}
}
$siteMap
para que sea accesible en toda la clase.index
creamos una instancia de la clase Sitemap
He creÃdo conveniente usar 2 clases: una para representar al sitemap de nuestro sitio, y otra para representar a cada URL en su interior.
Clase Sitemap
class SiteMap
{
const START_TAG = '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
const END_TAG = '</urlset>';
// to build the XML content
private $content;
public function add(Url $siteMapUrl)
{
$this->content .= $siteMapUrl->build();
}
public function build()
{
return self::START_TAG . $this->content . self::END_TAG;
}
}
Clase URL
class Url
{
private $url;
private $lastUpdate;
private $frequency;
private $priority;
public static function create($url)
{
$newNode = new self();
$newNode->url = url($url);
return $newNode;
}
public function lastUpdate($lastUpdate)
{
$this->lastUpdate = $lastUpdate;
return $this;
}
public function frequency($frequency)
{
$this->frequency = $frequency;
return $this;
}
public function priority($priority)
{
$this->priority = $priority;
return $this;
}
public function build()
{
// $url = 'https://programacionymas.com/';
// $lastUpdate = '2019-07-31T01:06:39+00:00';
// $frequency = 'monthly';
// $priority = '1.00';
return "<url>" .
"<loc>$this->url</loc>" .
"<lastmod>$this->lastUpdate</lastmod>" .
"<changefreq>$this->frequency</changefreq>" .
"<priority>$this->priority</priority>" .
"</url>";
}
}
¿Dónde debes situar estas clases?
Eso depende de tu proyecto. Por ejemplo:
Ahora bien, ¿qué representan los métodos privados del ejemplo?
addUniqueRoutes
es un método para agregar las URLs de las rutas únicas al sitemap.addArticles
, addCategories
, addDynamicPages
y todos los demás son métodos para agregar contenido dinámico al sitemap.Empecemos definiendo las "rutas únicas", que son constantes (son fijas y no requieren consultar nuestra base de datos):
private function addUniqueRoutes()
{
$startOfMonth = Carbon::now()->startOfMonth()->format('c');
$this->siteMap->add(
Url::create('/')
->lastUpdate($startOfMonth)
->frequency('monthly')
->priority('1.00')
);
$this->siteMap->add(
Url::create('/asesoria')
->lastUpdate($startOfMonth)
->frequency('monthly')
->priority('0.8')
);
$this->siteMap->add(
Url::create('/contacto')
->lastUpdate($startOfMonth)
->frequency('yearly')
->priority('0.8')
);
$this->siteMap->add(
Url::create('/becas')
->lastUpdate($startOfMonth)
->frequency('monthly')
->priority('0.7')
);
$this->siteMap->add(
Url::create('/blog')
->lastUpdate($startOfMonth)
->frequency('monthly')
->priority('0.8')
);
$this->siteMap->add(
Url::create('/categorias')
->lastUpdate($startOfMonth)
->frequency('yearly')
->priority('0.7')
);
$this->siteMap->add(
Url::create('/tags')
->lastUpdate($startOfMonth)
->frequency('yearly')
->priority('0.7')
);
$this->siteMap->add(
Url::create('/portafolio')
->lastUpdate($startOfMonth)
->frequency('yearly')
->priority('0.6')
);
}
En el ejemplo anterior debes modificar las URLs, la frecuencia de actualización, la prioridad, y agregar o quitar URLs según corresponda.
La variable $startOfMonth
es una cadena que representa una fecha con el formato requerido por el sitemap.
La fecha representada es el inicio del mes actual. Puedes usar lo mismo o modificar la fecha si conoces cuándo se modificaron por última vez tales rutas.
Respecto a las rutas que representan múltiples URLs, éstas generalmente contienen parámetros de ruta, por lo que debemos iterar sobre nuestros datos, y agregar cada una de tales entidades al sitemap, con el formato adecuado.
La implementación de esto depende de las entidades (o modelos) que tengas definidos en tu proyecto.
De todas formas, a modo de ejemplo, te muestro a continuación cómo agrego los artÃculos de mi blog al sitemap, y asà mismo las categorÃas, y las páginas que están definidas dinámicamente (a través de un editor y almacenadas en la base de datos):
private function addArticles()
{
$articles = Article::published()->whereNotNull('slug')->get([
'slug', 'updated_at'
]);
foreach ($articles as $article) {
$this->siteMap->add(
Url::create("/blog/$article->slug")
->lastUpdate($article->updated_at->startOfMonth()->format('c'))
->frequency('monthly')
->priority('0.9')
);
}
}
private function addCategories()
{
$categories = ArticleCategory::withCount('articles')
->having('articles_count', '>', 0)
->get(['slug', 'updated_at']);
foreach ($categories as $category) {
$this->siteMap->add(
Url::create("/categorias/$category->slug")
->lastUpdate($category->updated_at->startOfMonth()->format('c'))
->frequency('monthly')
->priority('0.8')
);
}
}
private function addDynamicPages()
{
$pages = Page::where('published', true)->get(['slug', 'updated_at']);
foreach ($pages as $page) {
$this->siteMap->add(
Url::create($page->slug)
->lastUpdate($page->updated_at->startOfMonth()->format('c'))
->frequency('monthly')
->priority('0.8')
);
}
}
Si eres observador, habrás notado que en el ejemplo anterior:
updated_at
para obtener desde allà una cadena, con el formato requerido para la fecha.Si tienes múltiples entidades y quieres evitar que todas tus consultas se ejecuten continuamente, puedes hacer uso de la clase Cache
de Laravel.
Esta clase permite recordar valores temporalmente.
Como en nuestra solución no usamos ninguna vista blade y simplemente creamos una cadena con el contenido adecuado, podemos recordar este valor usando Cache
.
¿Cómo se hace ello?
Podemos actualizar nuestro método index
de esta manera:
public function index()
{
$siteMapXml = Cache::remember('sitemap', 3, function () {
$this->siteMap = new SiteMap();
$this->addUniqueRoutes();
$this->addArticles();
$this->addCategories();
$this->addDynamicPages();
$this->addTags();
$this->addProjects();
$this->addProfilePages();
return $this->siteMap->build();
});
return response($siteMapXml, 200)
->header('Content-Type', 'text/xml');
}
El método remember
recordará el contenido de nuestro sitemap bajo el nombre "sitemap" durante el tiempo que le indiquemos en el segundo parámetro.
Cuando la variable caduque y no exista (o bien se acceda por 1ra vez), la función usada como 3er argumento se ejecutará, obteniendo un nuevo valor para ser recordado.
Sólo ten cuidado con el 2do parámetro. Por ejemplo:
Si tu sitio web crece mucho y de pronto tienes varios cientos o incluso miles de URLs en un mismo sitemap, no te preocupes, puedes separar ese sitemap en varios sitemaps.
Un sitemap que apunta hacia a otros sitemaps se conoce como un Ãndice de sitemaps, y es bueno usarlos, para mantener nuestras rutas organizadas. De hecho, Google nos explica cómo separar el contenido de un extenso sitemap en varios de ellos.
La estructura es la siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://programacionymas.com/sitemaps/general.xml</loc>
</sitemap>
<sitemap>
<loc>https://programacionymas.com/sitemaps/series.xml</loc>
</sitemap>
</sitemapindex>
En esta estructura de ejemplo se definen 2 sitemaps, donde:
sitemapindex
es la etiqueta padre, correspondiente al Ãndice de sitemaps.sitemap
es una etiqueta que representa a cada sitemap listado.loc
es la ubicación de cada sitemap.Como ves, no es complicado definir un sitemap (o varios de ellos), pero sà requiere algo de tiempo, para revisar todas las rutas que tenemos en nuestro proyecto.
Lo bueno de esto es que, podemos aprovechar la ocasión para identificar:
Y bien:
Espero que estos ejemplos te hayan sido de ayuda y puedas configurar adecuadamente un sitemap para cada uno de tus proyectos Laravel ?
Comparte este post si te fue de ayuda 🙂.
RegÃstrate
Accede a todos los cursos, y resuelve todas tus dudas.
Cursos Recomendados
Aprende Laravel desde cero y desarrolla aplicaciones web reales, en tiempo récord, de la mano de Laravel.
Iniciar cursoActualiza tus proyectos desde cualquier versión hasta la última versión estable de Laravel.
Iniciar cursoDesarrollemos un Messenger! Aprende sobre Channels, Queues, Vuex, JWT, Sesiones, BootstrapVue y mucho más.
Iniciar cursoEspera un momento ...
¿Te gustarÃa llevar mi curso de Laravel, gratis?
Sólo debes ingresar tus datos: