草庐IT

php - 多层次评论回复 : Display and Storage

coder 2023-10-08 原文

所以我正在尝试创建一个评论系统,您可以在其中回复已经回复的评论(允许您创建理论上无限的回复线程)。我希望它们按时间顺序显示(最新的在顶部),但当然回复应该直接在原始评论的下方。如果有多个评论回复同一条评论,回复也应按时间顺序排列(仍在原始评论下方)。我还想限制评论组的数量(一组评论,其中一条评论根本不是回复),比如说,25。我应该如何设置 MySQL 表,以及我将使用什么样的查询提取我想要的东西?

这是我的数据库的简化版本:ID int(11) NOT NULL AUTO_INCREMENT,DatePosted日期时间不为空,InReplyTo int(11) 非空默认值 '0',

对不起,如果这有点令人困惑,我不知道如何用不同的方式来表达。几个月来我一直在想这个问题,每次我解决一个问题时,我都会遇到另一个问题......

最佳答案

有很多方法。这是我喜欢的一种方法(并定期使用)。

数据库

考虑以下数据库结构:

CREATE TABLE comments (
  id int(11) unsigned NOT NULL auto_increment,
  parent_id int(11) unsigned default NULL,
  parent_path varchar(255) NOT NULL,

  comment_text varchar(255) NOT NULL,
  date_posted datetime NOT NULL,  

  PRIMARY KEY  (id)
);

您的数据将如下所示:
+-----+-------------------------------------+--------------------------+---------------+
| id  | parent_id | parent_path             | comment_text             | date_posted   |
+-----+-------------------------------------+--------------------------+---------------+
|   1 | null      | /                       | I'm first                | 1288464193    | 
|   2 | 1         | /1/                     | 1st Reply to I'm First   | 1288464463    | 
|   3 | null      | /                       | Well I'm next            | 1288464331    | 
|   4 | null      | /                       | Oh yeah, well I'm 3rd    | 1288464361    | 
|   5 | 3         | /3/                     | reply to I'm next        | 1288464566    | 
|   6 | 2         | /1/2/                   | this is a 2nd level reply| 1288464193    | 

... and so on...

以可用的方式选择所有内容相当容易:
select id, parent_path, parent_id, comment_text, date_posted
from comments 
order by parent_path, date_posted;

通过 parent_path, date_posted 排序通常会按照生成页面时需要的顺序生成结果;但是您要确保在评论表上有一个索引可以正确支持这一点 - 否则查询可以工作,但它真的非常非常低效:
create index comments_hier_idx on comments (parent_path, date_posted);

对于任何给定的单个评论,很容易获得该评论的整个子评论树。只需添加一个 where 子句:
select id, parent_path, parent_id, comment_text, date_posted
from comments 
where parent_path like '/1/%'
order by parent_path, date_posted;

添加的 where 子句将使用我们已经定义的相同索引,所以我们很高兴。

请注意,我们还没有使用 parent_id。事实上,这并不是绝对必要的。但我包含它是因为它允许我们定义一个传统的外键来强制执行参照完整性,并在需要时实现级联删除和更新。外键约束和级联规则仅在 INNODB 表中可用:
ALTER TABLE comments ENGINE=InnoDB;

ALTER TABLE comments 
  ADD FOREIGN KEY ( parent_id ) REFERENCES comments 
    ON DELETE CASCADE 
    ON UPDATE CASCADE;

管理层次结构

当然,为了使用这种方法,您必须确保在插入每个注释时正确设置 parent_path。如果您四处移动评论(这无疑是一个奇怪的用例),您必须确保手动更新从属于移动评论的每个评论的每个 parent_path。 ......但这些都是相当容易跟上的事情。

如果你真的想要花哨(如果你的数据库支持它),你可以编写触发器来透明地管理 parent_path——我将把这个留给读者练习,但基本思想是插入和更新触发器会触发在提交新插入之前。他们会沿着树向上走(使用 parent_id 外键关系),并相应地重建 parent_path 的值。

甚至可以将 parent_path 分解成一个单独的表,该表完全由评论表上的触发器管理,并使用一些 View 或存储过程来实现您需要的各种查询。因此,您的中间层代码无需了解或关心存储层次结构信息的机制。

当然,无论如何都不需要任何花哨的东西——通常只需将 parent_path 放入表中,并在中间层中编写一些代码以确保它与所有其他字段一起得到正确管理你已经需要管理了。

强加限制

MySQL(和其他一些数据库)允许您使用 LIMIT 子句选择数据的“页面”:
SELECT * FROM mytable LIMIT 25 OFFSET 0;

不幸的是,在处理这样的分层数据时,单独的 LIMIT 子句不会产生预期的结果。
-- the following will NOT work as intended

select id, parent_path, parent_id, comment_text, date_posted
from comments 
order by parent_path, date_posted
LIMIT 25 OFFSET 0;

相反,我们需要在我们想要施加限制的级别上进行单独的选择,然后我们将其与我们的“子树”查询结合在一起,以给出最终所需的结果。

像这样的东西:
select 
  a.*
from 
  comments a join 
  (select id, parent_path 
    from comments 
    where parent_id is null
  order by parent_path, post_date DESC 
  limit 25 offset 0) roots
  on a.parent_path like concat(roots.parent_path,roots.id,'/%') or a.id=roots.id)
order by a.parent_path , post_date DESC;

注意 limit 25 offset 0 语句,它隐藏在内部选择的中间。该语句将检索最近的 25 个“根级”注释。

[编辑:你可能会发现你必须玩一些东西才能完全按照你喜欢的方式订购和/或限制东西。这可能包括在以 parent_path 编码的层次结构中添加信息。例如:代替 /{id}/{id2}/{id3}/ ,您可能决定将 post_date 作为 parent_path 的一部分: /{id}:{post_date}/{id2}:{post_date2}/{id3}:{post_date3}/ 。这将使获得所需的顺序和层次结构变得非常容易,代价是必须预先填充该字段,并在数据更改时对其进行管理]

希望这可以帮助。
祝你好运!

关于php - 多层次评论回复 : Display and Storage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4057947/

有关php - 多层次评论回复 : Display and Storage的更多相关文章

  1. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  2. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

    给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

  3. ruby - .gemrc 评论? - 2

    这是一个基本问题.gemrc文件中是否允许注释?如果是,你会怎么做?我这里查了没用docs.rubygems.org/read/chapter/11 最佳答案 文档说:Theconfigfileitselfisin’’’YAML’’’format.这意味着您可以拥有commentsstartingwith#,例如:#Ilikedocsrdoc:--inline-source--line-numbers 关于ruby-.gemrc评论?,我们在StackOverflow上找到一个类似的问题

  4. ruby-on-rails - 这个 C 和 PHP 程序员如何学习 Ruby 和 Rails? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它

  5. ruby - 用一个 #!在 Ubuntu 中运行的 ruby​​ 中评论 - 2

    我是编程新手,正在尝试遵循使用#!用ruby评论。我一直收到消息:bash:matz.rb:找不到命令我正在使用这个评论:#!/usr/bin/envruby我试过有和没有后面的空格!以及有和没有环境。当我使用$哪个rubyruby在:/usr/bin/ruby我还进入了操作系统,将所有用户对文件matz.rb的权限更改为rwx,但没有任何效果。是我做错了什么还是我的系统设置不正确? 最佳答案 /usr/bin/env部分没问题。运行时需要为bash提供matz.rb的路径。如果您在matz.rb所在的目录中,请键入“./matz.

  6. ruby - 评论限制 - 2

    在ruby​​1.9中,放宽了行结束位置的条件,因此我们现在可以用句号开始一行来显示方法调用。当我们混淆了链式和非链式方法,并希望显示下一个非链式方法的开始位置时,这很方便。如果没有这个新功能,我们能做的最好的可能就是使用缩进:method1(args1).method2(args2).method3(args3)method4(args4).method5(args5).method6(args6)或插入一个空行。但这很不方便,因为我们必须注意缩进,同时,不要忘记在每个方法调用之后加上链中最后一个方法调用之后的句点。正因为如此,我制造了很多错误,要么有一个额外的周期,要么有一个缺失的

  7. ruby-on-rails - 如何在 Rails 中正确呈现嵌套评论? - 2

    我试图让嵌套评论在Rails5应用程序中正常工作,但遇到了很多困难。我正在构建一个问答网站,我有一个Acomment模型,其中有属于答案的评论,还有属于其他评论的评论:classAcomment我想显示所有评论,然后显示所有对评论的回复,以及对回复的回复等。但是我不知道如何使嵌套正常工作。在我看来,在每个循环中,我正在渲染_comment.html.erb部分:"comment",:object=>comment%>然后,在我的_comment.html.erb局部中,我显示评论、回复链接,然后呈现评论回复的局部:comment,:answer_id=>comment.answer)i

  8. Ruby:评论是代币吗? - 2

    我刚刚在这里(http://ruby.runpaint.org/programs#lexical)读到评论是标记。我从未将评论视为标记,因为它们要么是注释,要么是用于后处理器。评论真的是标记还是这个来源有误? 最佳答案 是的,它们应该是标记,但稍后会被解析器忽略。如果您使用如下所示的文件执行ruby--dumpparsetreefoo.rb#thisisacomment1+1#anothercomment这是你会得到的:#@NODE_SCOPE(line:3)#+-nd_tbl:(empty)#+-nd_args:#|(nullno

  9. ruby - 使用 YARD for Ruby 文档的内部评论? - 2

    我正在将我维护的ruby​​gem从RDoc切换到YARD文档。但是,代码中有一些关键注释只需要保留在代码中,不应出现在文档中。例如:###SomeClassdocumentationhere.#--#CRITICALcommentthatshouldbeinthecodebutnotinthedocumentation,#andmustbeatthisparticularspotinthecode.#++#moredocumentationthatfollowsthecriticalcommentblock,butthispart#shouldbeinthegenerateddocu

  10. ruby-on-rails - 在处理电子邮件回复时,我怎样才能忽略任何电子邮件客户端细节和历史记录? - 2

    我有一个通过IMAP处理传入电子邮件的Rails应用程序。当前使用一种方法来搜索TMail对象的各个部分以查找给定的content_type:defself.search_parts_for_content_type(parts,content_type='text/html')parts.eachdo|part|ifpart.content_type==content_typereturnpart.bodyelseifpart.multipart?ifbody=self.search_parts_for_content_type(part.parts,content_type)ret

随机推荐