5

我在 Rails 中的一个 Web 应用程序中工作,它的行为很像 CMS:有文章,它们有一个 text 属性,其中包含指向其他文章(或其他对象类)的链接。我目前将属性存储为 HTML。

有没有什么好的方法可以以相对容易更改的方式对这些链接进行建模,并包含对对象 ID 的引用,而不是绝对 url?

我在想的解决方案之一是使用某种特殊标记,例如:

[link_to "Text for the link", Article:12]

其中 12 是它链接到的文章的 ID。呈现文本时将解析此标记。

这样做的缺点是我必须侵入 TinyMCE(我想用来编辑 HTML 的编辑器),以便它可以通过访问数据库并自动分配对象类型和 ID 来插入指向其他对象的链接(编辑文本的人不知道 ID)。

有什么简单的解决方案吗?

或者我应该坚持使用绝对网址(除了维护问题之外,这在开发中很烦人,因为它们总是指向生产,这让我感到困惑)?

此外,是否有人在其他语言(php、Wordpress、其他 CMS 等)中有类似的示例可以很好地解决这个问题?我在想,这在 CMS 中非常重要,如果一个好的系统可以处理所有这些链接,可以减少很多工时。

编辑:

我正在考虑的另一种可能的解决方案是让人们直接在代码中复制文章的链接,但它应该在提交时生成正确的关联 id 并使其在 url 结构发生变化时,链接是始终保持最新。如果您尝试过,我想听听您对这种方法的意见和经验。

这种方法的挑战是用 Rails 解析链接并发现它指向一个文章,并且该文章具有 id ##。然后我必须插入一个短代码,在解析和呈现时,它总是会转换为该文章的实际链接。

我找到了一种可以使之可行的方法:

Rails.application.routes.recognize_path

但可能有一些我现在看不到的警告......

编辑号 2

我还想说明我选择CKEditor作为内容编辑器,但是如果有更明显的优势我会考虑其他的。

4

4 回答 4

2

一篇文章可以有许多related_articles,同时这篇文章可以与许多其他文章相关,因此最好将其建模为多对多关系。

在 Rails 中定义这种类型的关系的一种方法是has_many :through.

要使用has_many :through你必须创建一个连接模型,也许称之为ArticleRelation。该模型将有两个字段,一个代表当前文章的article_id和一个代表相关文章的related_article_id

class Article < AR::Base
  has_many :article_relations
  has_many :related_articles, :through => :article_relations
end

class ArticleRelation < AR::Base
  belongs_to :article
  belongs_to :article_relation, :class_name => 'Article'
end

在创建自我参照关系时,重要的是要记住您只是在创建关系的一侧。尽管article_1可能将article_2列为相关,但article_2 无法将 article_1列为相关。您需要两个ArticleRelation记录来创建相互关系。

很难想出合适的名称来定义关系的另一端,因此您可以在两者前面加上单词“inverse”来给出inverse_article_relationsinverse_related_articles。您还需要指定一些附加选项以使关系有效。对于inverse_article_relations,您必须指定另一个模型的名称,因为它无法从关系名称中推断出来,并且您还必须将外键定义为related_article_id。对于inverse_related_articles关系,您需要将源指定为article,因为它不能从关系的名称中推断出来。

has_many :inverse_article_relations, :class_name => "ArticleRelation", :foreign_key => "related_article_id"
has_many :inverse_related_articles, :through => :inverse_article_relations, :source => :article

测试一下,这应该适合您当前的要求。

于 2013-01-02T19:31:59.270 回答
2

我使用简码系统构建了类似的东西,它允许我调用模型上的特定方法并替换文本中的简码:

帮手

def parse_shortcode(model)
  text = model.text      
  text.gsub(/(\[#!\s?\w+\])/i) do |match|
    result = model.try(match)
    result.nil? '' : link_to(result[:text], result[:url])
  end
end

模型

def contact_link
  { :text => self.name, :url => self.url }
end

看法

<%= parse_shortcode(@article) %>

我没有测试过上面的代码,它显然有点简化,但它解释了我背后的思考过程。

编辑:只是为了澄清我上面的例子使用了发明的短代码语法[#! method]

于 2013-01-08T16:40:44.840 回答
1

我在许多其他 CMS 中看到的解决方案是 TinyMCE 中的自定义文件浏览器和页面重写的组合(类似于freakyDaz 的答案)。

TinyMCE 有实现自定义浏览器的文档和示例代码。当然,您必须提供后端部分。

CKEditor 也有类似功能的文档。

让你的后端实现返回一些易于解析的 URL(例如 urlfor:Article:12),然后让你的渲染代码用实际的 URL 替换那些。

于 2013-01-08T16:54:45.707 回答
0

我只是想到了用例的另一种可能的解决方案:

  1. 管理员用户在编辑文本之前指定关系(使用Chosen.js,这可以以用户友好的方式完成)。
  2. 然后该人要么提交表单以保存模型,要么可以异步完成。
  3. 保存关系后,将为每个关系生成并显示一个短代码,并且可以轻松地将该短代码粘贴到文本中。
  4. 在前端显示文本时,文本将被解析为简码,与@freakyDaz建议的方式类似。

通过这种方式,我不必在编辑器中破解或创建自定义操作。我认为这是一种非常务实的方法,但我想听听您的意见。当然,应该教育编写文本的管理员按顺序遵循流程,但在我的情况下,很少有人可以成为管理员(1 或 2),所以它是易于管理的。

于 2013-01-09T19:29:00.017 回答