¿Recuperar temas basados en campo personalizado?

Usando la muy útil guía de @angus, he podido agregar campos personalizados a temas y grupos.

Una vez que agregas un campo personalizado con éxito, ¿existe una forma (eficiente) de obtener elementos basados en ese campo personalizado?

Por ejemplo, digamos que agregas el campo personalizado fun_level a los temas. Ahora existe el campo topic.fun_level (una cadena) agregado mediante mi complemento.

Ahora, quiero obtener y mostrar una lista de temas de todos los temas donde fun_level sea “super-duper-fun”.

¿Cómo lo haría?


Si queremos obtener todos los temas con, digamos, una etiqueta determinada, podemos usar ajax('/tags/tag-name.json').then(function(result))..., como se usa en este post explicativo.

Para un campo personalizado que acabamos de crear, esto no estaría disponible (creo). Esto requeriría crear un controlador para el campo personalizado (como un controlador para fun_level, con un método show que de alguna manera obtenga todos los temas donde fun_level sea igual al valor del parámetro :fun_level, o algo así).

Supongo que hay una forma más directa.

1 me gusta

Creo que he encontrado un método para recuperar temas basados en un campo personalizado: buscarlos y obtener los resultados.

Aquí tienes un ejemplo. Imagina que tienes un botón con la acción “searchForTopics()” en algún lugar y estás intentando obtener temas con el campo personalizado fun_level igual a “super-duper-fun”:

(esto iría en algún código js.es6, como un inicializador):

import Topic from 'discourse/models/topic'; //no es necesario según el código de abajo, pero probablemente relevante para otras acciones relacionadas que quieras realizar
import { ajax } from 'discourse/lib/ajax';

export default {
    actions: {
        checkTopic(){
            let custom_field_value = 'super-duper-fun'
            let searchTerm = 'fun_level:' + custom_field_value
            let args = { q: searchTerm }
            ajax("/search", { data: args }).then(results => {
                let topics = results.topics
                topics.forEach(topic => {
                   //solo para obtener una lista de los nombres de los temas
                    console.log('nombre del tema = ')
                    console.log(topic)
                })
            })
        }
    }
}

Esto funciona. ¿Pero es esta la forma más eficiente de hacerlo?

Por ejemplo, ¿es este método, que utiliza la búsqueda, tan eficiente como el método que utiliza Discourse para mostrarte los temas que coinciden con una etiqueta determinada cuando vas a la página de esa etiqueta?

1 me gusta

La forma de hacerlo es

  1. En el cliente: agrega un parámetro de consulta de tema usando api.addDiscoveryQueryParam

  2. En el servidor: filtra las consultas de tema por el parámetro usando add_custom_filter en la clase TopicQuery (consulta lib/topic_query)

La función de devolución de llamada add_custom_filter se verá más o menos así

::TopicQuery.add_custom_filter(:field_name) do |topics, query|
  if query.options[:field_name]
    topics.where("topics.id in (
      SELECT topic_id FROM topic_custom_fields
      WHERE (name = 'field_name')
      AND value = '#{query.options[:field_name]}'
    )")
  else
    topics
  end
end
3 Me gusta

EDIT: Tras investigar más a fondo api.addDiscoveryQueryParam, creo que ahora entiendo la idea general:

Quiero recuperar programáticamente todos los temas con el campo personalizado fun_level = super-duper-fun. ¿Quizás un método de controlador podría hacerlo? (todavía estoy averiguando eso).

Una alternativa es realizar una búsqueda con ajax("/search") donde busco todos los temas basándome en el campo personalizado fun_level=super-duper-fun. Sin embargo, crear el campo personalizado no es suficiente para habilitar esto. Necesito hacer que el campo personalizado fun_level sea uno de los campos contra los que se puede buscar (al igual que se puede buscar en ciertas categorías, etiquetas, etc.), y eso no se hace automáticamente.

De alguna manera, api.addDiscoveryQueryParam en un archivo JS mezclado con TopicQuery en plugin.rb es necesario para lograrlo. Pero, siendo honestos, simplemente no he logrado que funcione. He visto algunos plugins que utilizan estos métodos, pero no he podido entender cómo “lo llevan a buen término”. Creo que se requiere algo de código adicional, pero aún no lo he encontrado.

¿Cómo se pasa de estos métodos a tener realmente el campo personalizado disponible como un término de búsqueda?

Respuesta anterior

Gracias, @angus. Para aclarar, el objetivo no es que los usuarios ingresen manualmente valores de búsqueda en el cuadro de búsqueda. El objetivo es recuperar programáticamente temas basados en un campo personalizado específico. Por ejemplo, el usuario iría a la página /fun_levels/super-duper-fun y cargaría todos los temas donde el campo fun_level = ‘super-duper-fun’.

¿Es api.addDiscoveryQueryParam para ese propósito?

Al ver ejemplos como este, no estoy seguro de cómo funciona addDiscoveryQueryParam para recuperar realmente los temas (no creo que llamar a ese método devuelva resultados que pueda analizar).

¿Quizás es para permitir potencialmente que el usuario busque manualmente el término en el cuadro de búsqueda? Esa no es la situación a la que apunto. (Definitivamente podría estar perdiéndome algo).

Mencioné anteriormente usar ajax("/search...") ya que es lo mejor que he logrado hasta ahora para devolver temas, pero me pregunto si hay una forma más eficiente de hacerlo, incluso configurando un modelo y un método de controlador para mostrar temas automáticamente, como lo hace tags/:tag-name (eso es más complejo, así que espero evitarlo, pero si es la mejor opción, lo consideraré).

La mejor estrategia aquí depende de tu objetivo final.

¿Cómo te imaginas que funcione esto? ¿Como una opción en la barra lateral en /search?

Hola @angus. El objetivo no es agregar una consulta en la barra lateral de búsqueda (sería agradable, pero no es el objetivo aquí). El objetivo es cargar programáticamente temas basados en un campo personalizado en una lista de temas cuando el usuario visita una página. Creo que ya he resuelto la parte de la plantilla/componente (es decir, la vista). Ahora estoy tratando de averiguar la lógica que cargará los temas.

La razón por la que hablé de búsqueda fue porque pensé que ejecutar ajax("/search") con custom_field=valor al visitar la página podría ser una forma limpia de cargar los temas. Pero solo estoy tratando de encontrar la forma que mejor funcione.


Más detalles:

En mi caso, el primer objetivo es que el usuario vaya a una nueva página de plantilla que he creado en una nueva ruta (/fun_levels/:fun_level) y cargar todos los temas con el campo personalizado fun_level que coincida con :fun_level.

Por separado, ya he descubierto cómo crear la plantilla y cargarla en la ruta. Ahora quiero cargar programáticamente los temas coincidentes en el componente de lista de temas que tengo en la página.

Idealmente, evitaría tener que crear un nuevo modelo “fun_level” (lo cual aún no he hecho), solo para mantener las cosas más sencillas y rápidas de implementar. Pero estoy abierto a hacerlo si inevitablemente eso resultará ser significativamente más performante (esta es una página que se usará mucho).


También sería bueno saber cómo agregar la capacidad de que “fun_level” sea una opción en la barra lateral de búsqueda, ya que me imagino que también me gustaría tener eso. Y quizás la mejor manera de cargar temas basados en el campo personalizado sea agregar el campo personalizado a las opciones de búsqueda y luego llamar a ajax("/search") con la consulta siendo “fun_level: super-duper-fun”.

Así que lo relacionado con la búsqueda podría ser importante aquí. Pero el trabajo principal en este momento es cargar temas en una página basados en un campo personalizado cuando el usuario visita esa página.

¿Esta página con una lista de temas debe parecerse a las páginas de lista de temas existentes en Discourse, o es sustancialmente diferente?

2 Me gusta

Substancialmente diferente. Pero el enfoque aquí es simplemente cómo cargar temas que tengan un valor de campo personalizado (por ejemplo, fun_level = ‘super-duper-divertido’) en el componente {{topic-list topics=selectedTopics showPosters=true}} que estoy insertando en la página.

Cuando trabajas con listas de temas, la cuestión es si extiendes la estructura de descubrimiento existente de Discourse o si creas la tuya propia, lo cual cambia la implementación. Hay muchas preguntas relacionadas con lo que buscas que estamos omitiendo aquí.

Por lo tanto, si no estás extendiendo la estructura de descubrimiento, no llevarás a cabo la primera parte de lo que sugerí anteriormente. Sin embargo, sí deberías realizar la segunda parte: agregar el filtro personalizado a TopicQuery. También necesitarás una ruta del lado del cliente con una llamada AJAX a un punto de enlace mapeado en list_controller.rb. Puedes encontrar las rutas del controlador de listas en config/routes.rb buscando list#.

Deberías utilizar el mismo punto de enlace de lista de temas que usa el descubrimiento de Discourse, ya que así obtendrás cosas como paginación (gestionada por la carga al hacer scroll en el componente de lista de temas), gestión de permisos y muchas otras funcionalidades listas para usar.

Por lo tanto, necesitarás:

  1. plugin.rb que contenga el filtro personalizado
  2. archivo de ruta del lado del cliente
  3. plantilla del lado del cliente
1 me gusta

Gracias por la información.

Me parece bien hacerlo de la forma más sencilla para obtener los temas que coinciden con el valor del campo personalizado. Ya tengo una ruta/ruta de acceso/plantilla funcional en /fun_levels/:fun_level, que carga el componente {{topic-list}}. De nuevo, si esa forma implica buscar ese valor de campo personalizado (ajax(/search)), también funciona. Estoy cada vez más convencido de que es la forma más directa. Solo que aún no he logrado que funcione.

Y para aclarar, mi método actual es:

  1. obtener los temas mediante ajax (solo necesito averiguar cuál es el endpoint correcto y cómo configurarlo; eso es lo clave),
  2. analizar el resultado, y
  3. ejecutar component.set('showTopics', parsed-result) para cargar los temas en {{topic-list topics=showTopics}}.

Esta forma me parece un poco misteriosa. Veo los métodos en el list_controller, como def topics_by, pero ¿cómo podría tomar uno de esos métodos y modificarlo para que devuelva temas basados en un valor de campo personalizado?

Te desaconsejo esto por varias razones. Perdona que sea misterioso, pero explicarlo en detalle me llevaría más tiempo del que tengo ahora mismo.

La forma más sencilla es utilizar uno de los endpoints existentes en el controlador de listas. Ya están configurados para servir listas de temas. Puedes encontrarlos en routes.rb, pero en resumen, son los filtros /latest, /top, etc. Para una lista con un filtro personalizado, querrás usar un parámetro de consulta como este:

\n/latest?fun_level=5\n

Usando el filtro personalizado. Puedes seguir la clase TopicQuery desde list_controller.rb para ver cómo funciona; por ejemplo, agrega tu filtro personalizado como un parámetro compatible para el controlador. La razón por la que parece misterioso es porque ese controlador y esa clase se encargan de muchas cosas por ti, como la paginación y diferentes filtros, los cuales tendrías que configurar manualmente si lo haces de otra manera.

La “otra forma” que tendría sentido (sin usar /search, por supuesto), sería configurar tu propio controlador dedicado para la ruta, que utilice la clase TopicQuery tal como lo hace list_controller.rb. Necesitarás crear un controlador de Rails si vas a agregar una ruta completamente nueva de todos modos, así que esta es otra aproximación plausible, aunque tendrás que manejar cosas como la paginación tú mismo. Aún así, deberías usar un filtro personalizado si empleas este enfoque.

Entiendo que parte de esto pueda parecer opaco; sin embargo, estás lidiando con funcionalidad complicada. Para explicarlo en detalle necesitaría escribir un curso de 10 partes. De hecho, es posible que lo haga pronto :slight_smile:

2 Me gusta

ACTUALIZACIÓN: Creo que ya lo tengo (casi) funcionando (!). Ahora, esto mostrará los temas más recientes que coincidan con el valor del campo personalizado. (El método #latest fue el más cercano que pude encontrar que tuviera sentido en el archivo config/routes.rb).

Es importante que, de hecho, se carguen en la página todos los temas que tengan el valor relevante del campo personalizado fun_level. ¿Hay algo más que deba hacer para lograrlo?


Aquí está el código para mis propias notas y por si resulta útil a otros:

–He creado el campo personalizado :fun_level. Luego:

plugin.rb

TopicQuery.add_custom_filter(:fun_level) do |topics, query|
  if query.options[:fun_level]
    topics.where("topics.id in (
      SELECT topic_id FROM topic_custom_fields
      WHERE (name = 'fun_level')
      AND value = '#{query.options[:fun_level]}'
    )")
  else
    topics
  end
end

/connectors/my-plugin-outlet/fun-level.js.es6 (un archivo de JavaScript que se activa al acceder a la página relevante. Por lo tanto, este JavaScript podría estar en un inicializador o en un conector que se vincule a un plugin outlet. Me gusta usar código que vaya junto con un conector, así que usaré un componente de configuración aquí):

const ajax = require('discourse/lib/ajax')

export default {
    setupComponent(args, component) {
      let parsedResultArray = []
      var endPoint = '/latest?fun_level=' + funLevel  //funLevel = variable con el valor de los parámetros   
      ajax(endPoint).then(function (result) {
            console.log('resultado de la lista de temas para temas que coinciden con ese nivel de diversión = ')
            console.log(result.topic_list.topics)
            //analizar resultados y cargarlos en parsedResultArray
            component.set('showTopics', parsedResultArray        
      })
   }
}

Ahora, los temas se cargarán en {{topic-list topics=showTopics}} que tengo en el componente correspondiente, el cual se inserta en la plantilla a través de my-plugin-outlet.

Este es un gran avance. Muchas gracias, @angus.

4 Me gusta

Con las instrucciones dadas anteriormente (gracias a @angus y @JQ331), pude obtener con éxito los temas dado un valor para el campo de tema personalizado visitando https://domain.com/latest?custom_field=custom_field_value

Sin embargo, desde esa página, si hago clic en el logotipo del sitio (o en el botón Más reciente en la barra superior), se elimina el parámetro de consulta (campo personalizado) de la URL, pero los temas permanecen filtrados en el campo personalizado.

Al actualizar, la página funciona como se esperaba (es decir, muestra todos los temas más recientes).

¿Cómo soluciono este comportamiento?

1 me gusta