Recuperare argomenti basati su campo personalizzato?

Usando la guida molto utile di @angus, sono riuscito ad aggiungere campi personalizzati a topic e gruppi.

Una volta aggiunto con successo un campo personalizzato, esiste un modo (efficiente) per recuperare gli elementi in base a quel campo personalizzato?

Ad esempio, supponiamo di aggiungere il campo personalizzato fun_level ai topic. Ora esiste il campo topic.fun_level (una stringa) aggiunto tramite il mio plugin.

Ora, voglio recuperare e mostrare un elenco di topic contenenti tutti i topic in cui fun_level = “super-duper-fun”.

Come potrei farlo?


Se volessimo recuperare tutti i topic con, ad esempio, un certo tag, potremmo usare ajax('/tags/tag-name.json').then(function(result))..., come illustrato in questo post esplicativo.

Per un campo personalizzato appena creato, questa opzione non sarebbe disponibile (credo). Poiché ciò richiederebbe la creazione di un controller per il campo personalizzato (come un controller per fun_level, con un metodo show che in qualche modo recupera tutti i topic in cui fun_level è uguale al valore del parametro :fun_level, o qualcosa di simile).

Immagino esista un modo più diretto?

1 Mi Piace

Penso di aver individuato un metodo per recuperare gli argomenti in base a un campo personalizzato: cercarli e ottenere i risultati.

Ecco un esempio, immaginando di avere un pulsante con l’azione “searchForTopics()” da qualche parte e di voler ottenere gli argomenti con il campo personalizzato fun_level uguale a “super-duper-fun”:

(questo andrebbe inserito in un codice js.es6, come un inizializzatore):


import Topic from 'discourse/models/topic'; // non necessario in base al codice sottostante, ma probabilmente rilevante per altre azioni correlate che vorresti eseguire
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 per ottenere un elenco dei nomi degli argomenti
                    console.log('topic name = ')
                    console.log(topic)
                })
            })
        }
    }
}

Questo funziona. Ma è il modo più efficiente per farlo?

Ad esempio, questo metodo, che utilizza la ricerca, è efficiente quanto il metodo che Discourse utilizza per mostrare gli argomenti corrispondenti a un determinato tag quando si accede alla pagina di quel tag?

1 Mi Piace

Il modo per farlo è

  1. Sul client: Aggiungi un parametro di query per l’argomento utilizzando api.addDiscoveryQueryParam

  2. Sul server: Filtra le query per argomento tramite il parametro usando add_custom_filter nella classe TopicQuery (vedi lib/topic_query)

Il callback add_custom_filter assomiglierà a questo

::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 Mi Piace

MODIFICA: Dopo aver approfondito api.addDiscoveryQueryParam, credo di aver capito l’idea generale:

Voglio recuperare programmaticamente tutti gli argomenti con il campo personalizzato fun_level = super-duper-fun. Penso che forse un metodo del controller potrebbe farlo? (sto ancora cercando di capire come).

Un’alternativa è eseguire una ricerca con ajax(“/search”) cercando tutti gli argomenti in base al campo personalizzato fun_level=super-duper-fun. Ma creare il campo personalizzato non è sufficiente per abilitare questa funzionalità. Devo rendere il campo personalizzato fun_level uno dei campi contro cui è possibile cercare (proprio come si può cercare per determinate categorie, tag, ecc.), e questo non viene fatto automaticamente.

In qualche modo, api.addDiscoveryQueryParam in un file js combinato con TopicQuery in plugin.rb è necessario per farlo. Ma, onestamente, non sono ancora riuscito a farlo funzionare. Ho visto alcuni plugin che utilizzano questi metodi, ma non sono riuscito a capire come “portarli a termine”. Penso che sia necessario del codice aggiuntivo, ma non l’ho ancora trovato.

Come si passa da questi metodi all’effettiva disponibilità del campo personalizzato come termine di ricerca?

Risposta precedente

Grazie, @angus. Per chiarire, l’obiettivo non è far inserire manualmente agli utenti i valori di ricerca nella casella di ricerca. L’obiettivo è recuperare programmaticamente gli argomenti in base a un certo campo personalizzato. Ad esempio, l’utente potrebbe andare alla pagina /fun_levels/super-duper-fun e caricare tutti gli argomenti in cui il campo fun_level = ‘super-duper-fun’.

api.addDiscoveryQueryParam serve a questo scopo?

Guardando esempi come questo, non sono sicuro di come addDiscoveryQueryParam funzioni per recuperare effettivamente gli argomenti (non credo che chiamare quel metodo restituisca risultati che posso analizzare).

Forse serve a permettere potenzialmente all’utente di cercare manualmente il termine nella casella di ricerca? Non è la situazione a cui sto mirando. (Potrei assolutamente aver perso qualcosa).

Ho menzionato l’uso di ajax(“/search…”) in precedenza perché è la soluzione migliore che ho trovato finora per restituire gli argomenti, ma mi chiedo se esista un modo più efficiente per farlo, anche includendo la configurazione di un modello e di un metodo del controller per mostrare gli argomenti automaticamente, come fa tags/:tag-name (è più complesso, quindi spero di evitarlo, ma se è il modo migliore lo prenderò in considerazione).

L’approccio migliore qui dipende dal tuo obiettivo finale.

Come immagini che funzioni? Come un’opzione nella barra laterale in /search?

Ciao @angus. L’obiettivo non è aggiungere una query nella barra laterale di ricerca (sarebbe bello, ma non è lo scopo qui). L’obiettivo è caricare programmaticamente i topic basati su un campo personalizzato all’interno di una topic-list quando l’utente visita una pagina. Penso di aver capito la parte relativa al template/componente (cioè la view). Ora sto cercando di capire la logica che caricherà i topic.

Il motivo per cui ho parlato di ricerca è che pensavo che eseguire ajax("/search") con custom_field=valore quando si visita la pagina potrebbe essere un modo pulito per caricare i topic. Ma sto solo cercando di capire qual è il metodo migliore.


Maggiori dettagli:

Nel mio caso, il primo obiettivo è far sì che l’utente vada a una nuova pagina template che ho creato in un nuovo percorso (/fun_levels/:fun_level) e carichi tutti i topic con il campo personalizzato fun_level che corrisponde a :fun_level.

Ho già capito separatamente come creare il template e caricarlo nel percorso. Ora voglio caricare programmaticamente i topic corrispondenti nel componente topic-list che ho sulla pagina.

Idealmente, eviterei di dover creare un nuovo modello “fun_level” (che non ho ancora fatto), solo per mantenere le cose più semplici e più veloci da implementare. Ma sono aperto a questa opzione se inevitabilmente risulterà significativamente più performante (questa è una pagina che verrà utilizzata molto).


Sarebbe utile sapere anche come aggiungere la possibilità di avere “fun_level” come opzione nella barra laterale di ricerca, dato che mi aspetto di volerlo avere anch’io. E forse il modo migliore per caricare i topic in base al campo personalizzato è aggiungere il campo personalizzato alle opzioni di ricerca e poi chiamare ajax("/search") con la query “fun_level: super-duper-fun”.

Quindi la parte relativa alla ricerca potrebbe essere importante qui. Ma il compito principale al momento è caricare i topic su una pagina in base a un campo personalizzato quando l’utente visita quella pagina.

Questa pagina con l’elenco dei topic è pensata per essere simile alle pagine esistenti con l’elenco dei topic in Discourse, o è sostanzialmente diversa?

2 Mi Piace

Sostanzialmente diverso. Ma l’obiettivo qui è semplicemente capire come caricare i topic che hanno un valore personalizzato (ad esempio, fun_level = ‘super-duper-fun’) nel componente {{topic-list topics=selectedTopics showPosters=true}} che sto inserendo nella pagina.

Quando si lavora con le liste di topic, la domanda è se estendere la struttura di scoperta esistente di Discourse o crearne una propria, il che cambia l’implementazione. Ci sono molte domande correlate a ciò che stai cercando di fare che stiamo tralasciando qui.

Quindi, se non stai estendendo la struttura di scoperta, non dovrai eseguire la prima parte di quanto suggerito sopra. Dovresti comunque eseguire la seconda parte, ovvero aggiungere il filtro personalizzato a TopicQuery. Avrai anche bisogno di una rotta lato client con una chiamata AJAX a un endpoint mappato su list_controller.rb. Puoi trovare le rotte del controller delle liste in config/routes.rb cercando list#.

Dovresti utilizzare lo stesso endpoint della lista di topic utilizzato dalla scoperta di Discourse, poiché otterrai così funzionalità come la paginazione (gestita dal caricamento allo scorrimento nel componente della lista di topic), la gestione dei permessi e molte altre cose già pronte.

Quindi avrai bisogno di:

  1. plugin.rb contenente il filtro personalizzato
  2. file della rotta lato client
  3. template lato client
1 Mi Piace

Grazie per le informazioni.

Sono felice di procedere nel modo più semplice per ottenere i topic che corrispondono al valore del campo personalizzato. Ho già una rotta/percorso/template funzionante a /fun_levels/:fun_level, che carica il componente {{topic-list}}. Di nuovo, se questo metodo prevede una ricerca per quel valore del campo personalizzato (ajax(/search)), allora va bene così. Sto sempre più pensando che sia il modo più diretto. Semplicemente, non sono ancora riuscito a farlo funzionare.

Per chiarire, il mio metodo attuale è:

  1. ottenere i topic via ajax (devo solo capire qual è l’endpoint corretto e come impostarlo—questo è il punto cruciale),
  2. analizzare il risultato, e
  3. eseguire component.set('showTopics', parsed-result), per caricare i topic in {{topic-list topics=showTopics}}.

Questo approccio mi sembra un po’ misterioso. Vedo i metodi in list_controller, come def topics_by, ma come potrei prendere uno di quei metodi e modificarlo per restituire i topic in base al valore di un campo personalizzato?

Sconsiglio vivamente questo approccio per diverse ragioni. Mi dispiace essere misterioso, ma ci vorrebbe un po’ più di tempo di quello che ho a disposizione al momento per spiegare tutto nel dettaglio.

Il modo più semplice è utilizzare uno degli endpoint esistenti nel controller delle liste. Sono già configurati per servire elenchi di argomenti. Puoi trovarli in routes.rb, ma in breve, sono i filtri /latest, /top ecc. Per un elenco con un filtro personalizzato, dovrai utilizzare un parametro di query come questo:

/latest?fun_level=5

Utilizzando il filtro personalizzato. Puoi seguire la classe TopicQuery partendo da list_controller.rb per vedere come funziona; ad esempio, aggiunge il tuo filtro personalizzato come parametro supportato al controller. Il motivo per cui sembra misterioso è che quel controller e quella classe gestiscono per te diverse funzionalità come la paginazione e i vari filtri, che dovresti configurare manualmente se scegli un altro approccio.

L’“altro modo” che avrebbe senso (non utilizzando /search, si badi bene), sarebbe impostare un controller dedicato per la rotta, che utilizza la classe TopicQuery proprio come fa list_controller.rb. Dovrai creare un controller Rails se stai aggiungendo una rotta completamente nuova in ogni caso, quindi questa è un’altra opzione plausibile, anche se dovrai gestire tu stesso cose come la paginazione. Dovresti comunque utilizzare un filtro personalizzato se scegli questo approccio.

Capisco che parte di questo possa sembrare oscuro, ma stai affrontando funzionalità complesse. Per spiegarlo completamente dovrei scrivere un corso in 10 parti. Forse lo farò presto, in realtà :slight_smile:

2 Mi Piace

AGGIORNAMENTO: Penso di averlo (quasi) funzionante (!). Ora, questo mostrerà gli argomenti “più recenti” che corrispondono al valore del campo personalizzato. (Il metodo #latest era quello più vicino che ho trovato che avesse senso nel file config/routes.rb).

È importante che, in realtà, tutti gli argomenti che hanno il valore rilevante del campo personalizzato fun_level vengano caricati nella pagina. C’è qualcos’altro che devo fare per far sì che ciò accada?


Ecco il codice per le mie note personali e nel caso possa essere utile ad altri:

–Ho creato il campo personalizzato :fun_level. Poi:

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 file JavaScript che viene attivato quando si accede alla pagina rilevante. Quindi questo JavaScript potrebbe trovarsi in un inizializzatore o in un connettore collegato a un plugin outlet. Mi piace usare codice associato a un connettore, quindi userò qui il setup del componente):

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

export default {
    setupComponent(args, component) {
      let parsedResultArray = []
      var endPoint = '/latest?fun_level=' + funLevel  //funLevel = variabile con valore dai parametri   
      ajax(endPoint).then(function (result) {
            console.log('risultato lista argomenti per argomenti con quel livello di divertimento = ')
            console.log(result.topic_list.topics)
            //analizza i risultati e caricali in parsedResultArray
            component.set('showTopics', parsedResultArray        
      })
   }
}

Ora, gli argomenti verranno caricati in {{topic-list topics=showTopics}} che ho nel componente corrispondente, inserito nel template tramite my-plugin-outlet.

Questo è un grande passo avanti. Grazie mille, @angus.

4 Mi Piace

Con le istruzioni fornite sopra (grazie a @angus e @JQ331), sono riuscito a recuperare gli argomenti dato un valore per il campo argomento personalizzato visitando https://domain.com/latest?custom_field=custom_field_value

Tuttavia, da quella pagina, se faccio clic sul logo del sito (o sul pulsante Latest nella barra superiore), il parametro di query (custom_field) viene rimosso dall’URL, ma gli argomenti rimangono filtrati sul custom_field.

Al refresh, la pagina funziona come previsto (nel senso che visualizza tutti gli argomenti più recenti).

Come posso correggere questo comportamento?

1 Mi Piace