根据自定义字段检索主题?

借助 @angus非常实用的指南,我已经成功为话题(topics)和群组(groups)添加了自定义字段。

在成功添加自定义字段后,是否有(高效的)方法可以根据该自定义字段检索项目

例如,假设我为话题添加了自定义字段 fun_level。现在,通过我的插件,话题对象中已添加了字段 topic.fun_level(字符串类型)。

接下来,我想检索并展示所有 fun_level 等于“super-duper-fun”的话题列表。

我该怎么做?


如果我们想检索所有带有某个标签的话题,可以使用 ajax('/tags/tag-name.json').then(function(result))...,就像这篇说明帖中演示的那样。

但对于我们刚创建的自定义字段,这种方法似乎不可用(我认为是这样)。因为这需要为自定义字段创建一个控制器(例如为 fun_level 创建一个控制器,其中包含一个 show 方法,以某种方式检索所有 fun_level 等于 :fun_level 参数值的话题,诸如此类)。

我想应该有更直接的方法吧?

1 个赞

我想我已经找到了一种通过自定义字段检索主题的方法:搜索并获取结果。

举个例子,假设你在某个地方有一个动作为“searchForTopics()”的按钮,而你想要获取自定义字段 fun_level 等于 “super-duper-fun” 的主题:

(这段代码可以放在某个 JS ES6 文件中,例如初始化器里):

import Topic from 'discourse/models/topic'; // 根据下面的代码并非必需,但对于你可能想执行的其他相关操作可能是相关的
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 => {
                   // 只是为了获取主题名称列表
                    console.log('topic name = ')
                    console.log(topic)
                })
            })
        }
    }
}

这确实可行。但这是否是最有效的方法呢?

例如,使用搜索的这种方法和 Discourse 在访问某个标签页面时显示匹配该标签的主题所使用的方法相比,效率如何?

1 个赞

实现方法如下:

  1. 在客户端:使用 api.addDiscoveryQueryParam 添加一个 topic 查询参数。

  2. 在服务器端:在 TopicQuery 类中使用 add_custom_filter 根据该参数过滤 topic 查询(参见 lib/topic_query)。

add_custom_filter 回调函数的写法大致如下:

::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 个赞

编辑:在深入研究 api.addDiscoveryQueryParam 后,我现在大概明白了:

我想以编程方式检索所有自定义字段为 fun_level = super-duper-fun 的主题。我认为也许可以通过一个控制器方法来实现?(仍在摸索中)。

另一种方法是使用 ajax("/search") 进行搜索,即基于自定义字段 fun_level=super-duper-fun 搜索所有主题。但仅创建自定义字段还不足以启用此功能。我需要将自定义字段 fun_level 设置为可搜索的字段之一(就像可以搜索特定分类、标签等一样),而这并不是自动完成的。

在某种程度上,需要在 JS 文件中使用 api.addDiscoveryQueryParam,并结合 plugin.rb 中的 TopicQuery 才能实现这一点。但说实话,我还没能让它正常工作。我见过一些插件使用了这些方法,但我还没能弄清楚它们是如何“搞定”的。我认为还需要一些额外的代码,但我还没找到。

如何从这些方法过渡到实际上让自定义字段可作为搜索项使用?

较早的回复

谢谢,@angus。需要澄清的是,目标并非让用户在搜索框中手动输入搜索值。目标是基于某个自定义字段以编程方式检索主题。例如,用户访问页面 /fun_levels/super-duper-fun 时,应加载所有字段 fun_level = ‘super-duper-fun’ 的主题。

api.addDiscoveryQueryParam 是用于这个目的吗?

查看类似 这里 的示例,我不确定 addDiscoveryQueryParam 是如何实际检索主题的(我认为调用该方法并不会返回我可以解析的结果)。

也许它是为了允许用户在搜索框中手动按该术语搜索?这不是我想要的情况。(我可能确实漏掉了什么)。

我之前提到使用 ajax("/search..."),因为这是我目前能想到的返回主题的最佳方式,但我想知道是否有更高效的方法,甚至包括设置模型和控制器方法来自动显示主题,就像 tags/:tag-name 那样(那更复杂,所以我希望能避免,但如果这是最好的方法,我会考虑)。

最佳方案取决于您的最终目标。

您设想这如何运作?是作为 /search 页面侧边栏中的一个选项吗?

你好 @angus。目标不是要在侧边搜索栏添加查询(那样当然很好,但并非此处目标)。目标是在用户访问页面时,根据自定义字段以编程方式将主题加载到主题列表中。我想我已经搞定了模板/组件(即视图)部分,现在正在尝试弄清楚加载主题的逻辑。

之所以提到搜索,是因为我想到当用户访问页面时,针对 custom_field=value 运行 ajax("/search") 可能是一种加载主题的简洁方式。但我只是在尝试找出最佳方案。


更多细节:

在我的案例中,首要目标是让用户访问我为新路径 (/fun_levels/:fun_level) 创建的新模板页面,并加载所有 fun_level 自定义字段与 :fun_level 匹配的主题。

我已经单独搞定了如何创建模板并在该路径下加载它。现在,我想以编程方式将匹配的主题加载到页面上的 topic-list 组件中。

理想情况下,我希望避免为了保持简单和快速实施而创建一个新的 fun_level 模型(我尚未完成此操作)。但如果这不可避免地会显著提升性能(这是一个将被频繁使用的页面),我也持开放态度。


了解如何添加将 fun_level 作为搜索侧边栏选项的功能也会很有帮助——我预计也会需要这个功能。也许基于自定义字段加载主题的最佳方式是将该自定义字段添加到搜索选项中,然后调用 ajax("/search"),并将查询设置为 fun_level: super-duper-fun

因此,搜索相关的问题在这里可能很重要。但当前的首要任务是在用户访问页面时,根据自定义字段加载该页面上的主题。

这个带有主题列表的页面,是旨在与 Discourse 中现有的主题列表页面感觉相似,还是存在实质性的差异?

2 个赞

实质上是不同的。但这里的重点是如何将具有自定义字段值(例如 fun_level = 'super-duper-fun')的主题加载到我将插入页面的 {{topic-list topics=selectedTopics showPosters=true}} 组件中。

当你处理主题列表时,问题在于你是扩展现有的 Discourse 发现结构,还是创建自己的结构,这会改变实现方式。这里我们略过了许多与你想要做的事情相关的问题。

因此,如果你不扩展发现结构,你就不会执行我之前建议的 第一部分。不过,你仍然应该执行第二部分,即向 TopicQuery 添加自定义过滤器。此外,你还需要一个客户端路由,通过 AJAX 调用映射到 list_controller.rb 的端点。你可以在 config/routes.rb 中通过搜索 list# 找到列表控制器的路由。

你应该使用与 Discourse 发现相同的主题列表端点,这样你就可以直接获得分页(由主题列表组件中的滚动加载处理)、权限处理以及许多其他功能。

因此,你需要:

  1. 包含自定义过滤器的 plugin.rb
  2. 客户端路由文件
  3. 客户端模板
1 个赞

谢谢提供的信息。

我很乐意采用任何最简单的方式来获取匹配自定义字段值的主题。目前我已在 /fun_levels/:fun_level 设置了一条可用的路由/路径/模板,用于加载 {{topic-list}} 组件。同样,如果这种方式是通过搜索该自定义字段值(即 ajax(/search))来实现的,那也可以。我越来越觉得这是最直接的方法,只是我还没成功实现。

另外,为明确起见,我目前的方法是:

  1. 通过 ajax 获取主题(只需弄清楚正确的端点是什么以及如何设置该端点——这是关键),
  2. 解析返回结果,
  3. 执行 component.set('showTopics', parsed-result),将主题加载到 {{topic-list topics=showTopics}} 中。

这种方式对我来说有点神秘。我看到了 list_controller 中的方法,例如 def topics_by,但我该如何选取其中一个方法,并修改它以根据自定义字段值返回主题呢?

我建议你慎重考虑,原因有很多。抱歉说得有点神秘,但要完整解释清楚,现在花的时间比我预期的要多。

最简单的方法是直接使用列表控制器(list controller)中现有的端点。它们已经配置好用于提供主题列表。你可以在 routes.rb 中找到它们,简而言之,它们就是 /latest/top 等过滤器。对于带有自定义过滤器的列表,你可以使用类似这样的查询参数:

/latest?fun_level=5

使用自定义过滤器。你可以从 list_controller.rb 追踪 TopicQuery 类,看看它是如何工作的,例如,它会将你的自定义过滤器作为受支持的参数添加到控制器中。之所以感觉神秘,是因为该控制器和该类为你处理了许多事情,比如分页和各种过滤器;如果你采用其他方式,就需要手动设置这些内容。

另一种合理的方式(注意,这里不是指使用 /search)是为该路由设置一个专用的控制器,该控制器像 list_controller.rb 一样使用 TopicQuery 类。如果你要添加一个全新的路由,无论如何都需要创建一个 Rails 控制器,所以这也是一个可行的方案,只不过你需要自己处理分页等事宜。即使采用这种方法,你也应该使用自定义过滤器。

我知道其中一些内容可能显得晦涩难懂,但这里涉及的功能确实很复杂。要完整解释清楚,我可能需要写一个十讲课程。说不定我最近就会写 :slight_smile:

2 个赞

更新:我想我(基本上)让它(大部分)运行起来了(!)。现在,这将显示与自定义字段值匹配的“最新”主题。(#latest 方法是我在 config/routes.rb 文件中找到的最合理的方法。)

重要的是,事实上,所有具有相关自定义字段 fun_level 值的主题都必须加载到页面上。我还需要做些什么来实现这一点吗?


以下是代码,供我自己记录,同时也希望对其他人有所帮助:

–我创建了自定义字段 :fun_level。然后:

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(这是一个 JavaScript 文件,在访问相关页面时会被激活。因此,这段 JavaScript 代码可以放在初始化器中,或者放在连接到插件出口的连接器中。我喜欢使用与连接器配合的代码,所以这里我将使用 setup 组件):

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

export default {
    setupComponent(args, component) {
      let parsedResultArray = []
      var endPoint = '/latest?fun_level=' + funLevel  //funLevel = 来自参数的变量值   
      ajax(endPoint).then(function (result) {
            console.log('topic list result for topics matching that fun level = ')
            console.log(result.topic_list.topics)
            //解析结果,并将其加载到 parsedResultArray 中
            component.set('showTopics', parsedResultArray        
      })
   }
}

现在,主题将被加载到我对应组件中的 {{topic-list topics=showTopics}},该组件通过 my-plugin-outlet 放入模板中。

这是一个巨大的进步。非常感谢@angus

4 个赞

在遵循上述说明(感谢 @angus@JQ331)后,我成功地通过访问 https://domain.com/latest?custom_field=custom_field_value 来获取给定自定义主题字段值的帖子。

但是,从该页面开始,如果我单击网站徽标(或顶部栏上的“最新”按钮),它会从 URL 中删除查询参数(custom_field),但帖子仍然按 custom_field 进行筛选。

刷新后,页面按预期工作(即显示所有最新帖子)。

如何修复此行为?

1 个赞