Ordenamiento condicional por fecha: Un enfoque práctico para ordenar con campos de fecha mixtos
En proyectos desarrollados con Drupal, es habitual que los resultados de un View se muestren como listas ordenadas por fecha, normalmente desde el contenido más reciente hasta el más antiguo. Este enfoque funciona bien cuando todos los tipos de contenido comparten el mismo campo de fecha, pero puede volverse más complejo cuando cada tipo maneja sus fechas de forma distinta.
En este artículo veremos un caso práctico en el que creamos un campo personalizado en Search API que almacena una fecha definida según el tipo de contenido, este enfoque nos permite ordenar de forma consistente resultados provenientes de distintos tipos de contenido, sin complicar la configuración de Views.
El problema: Un View con distintos criterios de fecha según el tipo de contenido.
El conflicto aparece cuando un mismo View debe mostrar distintos tipos de contenido que utilizan criterios de fecha diferentes. Mientras algunos dependen de campos personalizados de fecha de publicación, otros se basan únicamente en la fecha de creación del nodo.
Imaginemos un sitio web con los siguientes tipos de contenido:
- Artículos: Tiene un campo llamado field_published_date, que representa la fecha de publicación y no necesariamente coincide con la fecha de creación del nodo.
- Impact Story: Utiliza la fecha de creación del contenido(la que viene por defecto con Drupal)
Desde el punto de vista del usuario, el requerimiento es sencillo: ordenar todos los resultados por fecha, del más reciente al más antiguo.
Eso es bastante sencillo a nivel funcional; sin embargo, la complejidad aparece en la implementación, una solución rápida sería ordenar los artículos por field_published_date y los impact stories por created, pero ni Views ni Search API permiten aplicar lógica condicional en los criterios de ordenamiento (por ejemplo, “usar este campo para un content type y otro campo para otro content type”).
Esto hace que el problema no pueda resolverse de forma sencilla desde la configuración del View.
La idea clave: Un campo de fecha combinado.
Antes de entrar en la solución, vale la pena aclarar qué papel juega Search API en este escenario. Search API permite extender el proceso de indexación mediante processors, que son componentes encargados de transformar, calcular o enriquecer los datos antes de que queden almacenados en el índice. Esto significa que la lógica se ejecuta una sola vez durante la indexación y no cada vez que se renderiza un View.
En este caso, el objetivo es resolver un problema de ordenamiento que no puede abordarse directamente desde Views ni desde la configuración estándar de Search API. Existen alternativas, como duplicar la lógica en el View, crear múltiples displays o forzar un único campo de fecha en todos los tipos de contenido, pero todas ellas aumentan la complejidad, reducen la flexibilidad o impactan el mantenimiento a largo plazo.
La solución propuesta consiste en centralizar la lógica de fechas durante la indexación, creando un campo calculado que represente la fecha correcta según el tipo de contenido. Este enfoque permite:
- Agregar un único campo al índice de Search API.
- Asignar dinámicamente el valor de la fecha según las reglas de cada tipo de contenido.
- Utilizar ese campo como único criterio de ordenamiento en el View.
De esta forma, el nuevo campo actúa como un punto de normalización entre distintos tipos de contenido, definiendo y almacenando la fecha adecuada para cada uno en el momento de indexar. El resultado es un View más simple, un ordenamiento consistente y una solución que escala bien a medida que se agregan nuevos tipos de contenido.
Este patrón no se limita únicamente a fechas. La misma estrategia puede aplicarse a otros escenarios donde distintos tipos de contenido requieren reglas específicas, como valores de prioridad, estados personalizados, campos numéricos calculados o incluso textos normalizados para búsquedas y ordenamientos avanzados.
A continuación veremos la implementación paso a paso. Para este ejemplo, comenzamos definiendo un nuevo campo en el índice de Search API llamado search_api_combined_sort_date, el cual servirá como contenedor de la fecha unificada que utilizaremos para el ordenamiento.
/**
* {@inheritdoc}
*/
public function getPropertyDefinitions(DatasourceInterface $datasource = NULL) {
$properties = [];
// Only define the property at the index level (not per datasource).
// This ensures the field is available globally in the Search API index.
if (!$datasource) {
// Define the Search API processor property.
// This field will store a unified date value used for sorting,
// based on conditional logic (e.g. published date or created date).
$definition = [
'label' => $this->t('Combined sort date'),
'description' => $this->t('Conditional date (field_published_date or created)'),
'type' => 'date',
'processor_id' => $this->getPluginId(),
];
// Register the processor property in the index.
// This field can later be used in Views for sorting results.
$properties['search_api_combined_sort_date'] = new ProcessorProperty($definition);
}
// Return all defined properties.
return $properties;
}Luego definiremos la fecha a usar según el tipo de contenido y le asignaremos el valor al nuevo campo search_api_combined_sort_date.
/**
* {@inheritdoc}
*/
public function addFieldValues(ItemInterface $item) {
// Get the original entity being indexed.
$entity = $item->getOriginalObject()->getValue();
// Ensure the indexed item is a node entity.
if ($entity instanceof NodeInterface) {
$bundle = $entity->bundle();
// Initialize the date variable.
// This value will be determined conditionally based on the content type.
$date = NULL;
// For Article content type:
// Use the custom published date field when it is available.
if ($bundle === 'article' && !$entity->get('field_published_date')->isEmpty()) {
$date = $entity->get('field_published_date')->value;
}
// For Impact Story content type:
// Use node created date.
elseif ($bundle === 'impact_story') {
$date = $entity->getCreatedTime();
}
// If a valid date value was resolved, assign it to the Search API field.
if ($date) {
// Retrieve all Search API fields that match the processor property path.
// This allows the processor to support multiple datasources if needed.
$fields = $this->getFieldsHelper()
->filterForPropertyPath($item->getFields(), NULL, 'search_api_combined_sort_date');
// Populate the combined sort date field with the calculated value.
// This value will be used later for sorting results in Views.
foreach ($fields as $field) {
$field->addValue($date);
}
}
}
}Beneficios de este enfoque
1. Evita duplicar campos en los nodos, ya que la lógica de fechas se centraliza en el índice y no requiere modificar la estructura de los tipos de contenido.
2. Es escalable, si aparece un nuevo tipo de contenido, solo se agrega una condición adicional en el processor, sin tocar el view.
3. Mejora el rendimiento, porque la lógica se ejecuta durante la indexación y no cada vez que un usuario accede al View.
4. El view queda limpio y fácil de mantener.
Conclusión
Por lo general, al utilizar Views se define un criterio de ordenación basado en el título o en la fecha de creación, y se configura si el orden será ascendente o descendente. Sin embargo, existen escenarios en los que cada tipo de contenido requiere utilizar un campo distinto para ordenarse. En estos casos, un Search API processor ofrece una solución elegante, ya que permite unificar los distintos criterios en un solo campo sin afectar el rendimiento de la página. Esto es posible porque la lógica se ejecuta durante la indexación de los datos y no en el momento en que se renderiza el view.
Recursos recomendados
_______
Ivan Mendoza is a senior Drupal developer at Rootstack.