【问题标题】:How to save modifications to apply later?如何保存修改以供以后应用?
【发布时间】:2013-12-17 05:11:12
【问题描述】:

我的 webapp 的版主可以修改数据库中的一些数据。所有用户都可以看到这些修改。但出于某种原因,我不希望它们立即应用,而是仅在特定操作之后,例如,当我明确接受时。时间线是:

  1. 表中的字段具有值PreviousValue
  2. 版主请求将此值设为NewValue
    所有用户继续看到PreviousValue 值。
  3. 我接受更改。
    现在所有用户都可以看到值NewValue

问题是版主可以修改大量表格中的字段。处理这个临时值的最佳方法是什么?我可以想象这些解决方案,但没有一个听起来不错:

  • 复制所有表(MyTableMyTable_ToApply 一起)。
  • 添加一个特殊表 (ToApplyTable),其中包含四个字段:要修改的表、要修改的字段、要修改的条目 ID 和要应用的新值。

你有更好的想法吗?

【问题讨论】:

  • 第二种解决方案听起来好多了,但是主键(id)包含多个字段的情况呢?第一个解决方案非常简单,如果记住每个 table_ToApply 只有一个条目,它不会对你产生太大影响。
  • @alkis 实际上,两种解决方案都非常肮脏,因为您强调了其中一个问题。因此我的问题在这里。对于第一个解决方案,它意味着复制所有表,并且我使用的是 ORM,因此我还必须复制所有实体。这真的不能接受。
  • 为什么不能有一个保存当前值的字段和一个保存新值的字段,并且每次要接受时,新值替换旧值,并且字段保持新值变为空?
  • @alkis 字段太多(大部分时间无用)!
  • 赞成。我真的很想在这个中看到一个好的答案。请在您决定要做什么时发布您的决定。

标签: php database


【解决方案1】:

这就是 wordpress 处理这些东西的方式。您可以在同一个表中包含已发布的帖子,然后作为新行包含由不同作者在不同时刻创建的未发布修订的集合,当然这些修订与主帖子相关联。

更新

例如,如果您使用学说,您只需要相同的存储库和一个要考虑的字段,例如:$rep->findByPublished(1) 所有其余代码保持不变,因此您的控制器不知道业务级逻辑喜欢哪些是适度的,而不是处理它。然后在您的管理员控制器中编写逻辑以将修订设置为主要帖子并在接受修订时取消发布旧的。简单、干净、可扩展、可维护和有效的模型。

CREATE TABLE `wp_posts` (
  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,

  `post_author` bigint(20) unsigned NOT NULL DEFAULT '0',

   `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', #date of creation of this post o revision




  `post_status` varchar(20) NOT NULL DEFAULT 'publish', #publish, unpublish, 

  `post_name` varchar(200) NOT NULL DEFAULT '', #internal name if you want to better identify revisions

  `post_modified` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', #date of modification of this current post or revision

  `post_parent` bigint(20) unsigned NOT NULL DEFAULT '0', #if this is a revision, here you have the id of the main post this could replace if it it approved

  `post_type` varchar(20) NOT NULL DEFAULT 'post', //basicaly in your case, post or revision, but you can remove this one, since you can check if post_parent is 0 (or NULL if you want)


  ##Other columns with all the post information 



  PRIMARY KEY (`ID`),

) 

我不喜欢 wordpress 的制作方式,但我不得不说这是一个很棒的方法。而且,肯定有四个:

  • 甚至不要考虑添加额外的列,因为您将能够保存的元数据太少,并且如果您认为其他一些列可以编辑,则还必须修改数据库。
  • 在同一字段中没有多个修订版本。这违反了所有关系数据库原则。您可以为某些元数据序列化信息,这在某些情况下可能没问题,但对于这是主要信息,在许多情况下都无法处理和解析。
  • 您可以有一个辅助表,该模型将是有效的,但搜索、排序会更难,实际上您将有两个完全相同的表,所以这不是最好的主意。

【讨论】:

  • 他们真的有一个用于布尔变量发布/取消发布的 varchar(20) 吗?
  • 不,有很多可能性。我只是用它来解释它与实际提示的关系。可以肯定的是,需要调整一些列,不仅是对 sata 类型的关注,以获得适合当前模型的优化表。无论如何...wordpress 有太多没有意义的东西,我可以告诉你很多。但是我在这里试图说明的是如何处理不同的修订,并且方法很好,尽管在类型方面做得不好
【解决方案2】:

已经给出了很多答案,我觉得大部分都很有道理。我相信有一些很好的方法可以做到这一点。

我假设处理能力/速度比 HDD 和 RAM 中的存储空间更重要/更昂贵。 我也同意 Robbes 先生(在我上方某处)的观点,即每行/单元格应该有一个修订版,并且添加额外的列不是正确的解决方案(详见下文)

让我先从我认为正确的选项开始:

  • JSON/noSQL。您的数据显然是相关的。 (我的)SQL 是 走。
  • 存储为数组或其他形式的应用程序内存。这 这造成的开销,包括每个玩家的负担 值得。另外,您希望能够存储它,并查看这些 及时修订,并将其作为应用程序堆栈的一部分 随着时间的推移。这根本不值得。
  • 添加一个或多个添加到每个表的列。 创建的更改可以存储在那里。正如您所提到的,这会创建一个 表中有相当大的开销,这是一种不好的做法。但是,当仅需要编辑帖子或任何其他单个实体并且不需要保存历史记录时,这是一个合适的解决方案。但是,在这个问题中并非如此。

查看问题中给出的事实:

  1. 有一个或多个表包含可以管理的项目,
  2. 有多个版主可以创建多个修订版
  3. 这些修订并不总是按时间顺序应用
  4. 您可能希望记录所有建议和应用的记录

我认为您提供的第二种解决方案最适合您的数据。

  • 为修订创建单独的表格。这是执行此操作的最佳规范化方式。根据审核的规模,您可以保留正在编辑的整行的副本,或者引用正在编辑的特定单元格。
  • 对于额外的专家级别点,您可以使用视图或临时表之类的东西来创建一个包含所有确定行的表,并且不显示任何未决或拒绝的修订。这可以提高速度,但确实依赖于严肃的数据库工程。不是你想搞砸的事情

可以通过多种方式将修订推送给您的用户。使用上述临时表/视图,您可以使用存储过程来更改表中的内容(或将单元格设置为“活动”并更新临时表/视图) 您可以简单地将数据库中的一行替换为新行(尽管更新成本很高),或者使用连接构造。

如果 CPU 比存储便宜,那么使用散列或类似的方式查看类似 git 的系统可能会起作用。

【讨论】:

    【解决方案3】:

    我认为,如果您认为没有任何 GOOD 选择,您可以轻松地向后工作并找出 WORST 选择,以了解最不坏的选择是什么。每个选择都需要牺牲。但是有问题的牺牲是什么?

    选项 #1:复制所有表(同时包含 MyTable 和 MyTable_ToApply)。

    IMO 这将是一个巨大的头痛。说的领域太多了!您还需要创建额外的编码才能将数据从一个表移动到另一个表。我认为这里可能出错的地方太多了。

    选项 #2:添加一个特殊的表 (ToApplyTable),其中包含四个字段:要修改的表、要修改的字段、要修改的条目 ID 和要应用的新值.

    我认为这也是你提到的一个肮脏的选择,因为同样的事情 - 将值从一个表移动到另一个表。

    选项 #3:将 dateismoderated 值添加到表中。

    这已经被很多人提到了。您的论点是它太多(大部分无用)列。我认为它比已经提到的其他任何东西都好。我将谦虚地提出一种最终方法,它是选项#1 和选项#3 的一点组合。

    选项#4:在每个可以有审核字段的表下创建一个自定义ismoderated 表。

    我在我的数据库中做这样的事情并使用命名标准[TABLENAME]__ismoderated。在此表中,如果您只需要一列,您实际上并不需要多于一列。您可以简单地在需要审核的项目表中拥有外键,然后在最终审核后删除该键。或者,如果您愿意,您可以在此处添加主持人的用户 ID,并将时间添加为附加字段。

    我个人认为我也会使用日期时间字段,这样您就可以跟踪更改并知道哪个是最新的。

    无论如何,这使您能够INNER JOIN 两个表。如果您只想查看要审核的值。

    【讨论】:

      【解决方案4】:

      但是版主会使用表格更改数据..

      当他们更改数据时,为什么不将数据保存在 csv 文件中...

      在修改更改时,您可以将其插入到实际表中。

      这只是一个想法..

      【讨论】:

        【解决方案5】:

        其实,答案并没有那么难。这是我的想法:-

        第 1 步: 只需在您的表Posts(或包含用户的全部帖子的表)中添加一个名为Moderated Post 的列。任何版主的任何审核或编辑都会进入此列。

        第 2 步: 将另一列添加到名为 isModerated 的表 Posts。这包含一个值10,就像布尔值truefalse

        第 3 步:现在,您几乎完成了。你有一张这样的桌子:-

                              TABLE `POSTS`
        
        | Post id | Initial Post | Moderated Post | isModerated |
        _________________________________________________________
                  | The first    |                |             |
        |    1    | post goes    |                |      0      |
        |         | here         |                |             |
        _________________________________________________________
        |         | The second   | The moderated  |             |
        |    2    | post goes    | post goes here |      0      |
        |         | here         |(Post invisible)|             |
        _________________________________________________________
        |         | The third    | The moderated  |             |
        |    3    | post goes    | post goes here |      1      |
        |         | here         | (Post visible) |             |
        _________________________________________________________
        

        现在在您的 php 代码中,从表中检索所有值后,您可以像这样显示您的帖子(只是一个粗略的草图):-

        <?php 
        if($isModerated == 1) { 
           echo $moderated_post; 
        }else {
           echo $initial_post;
        }
        ?>
        

        大量表的变化

        正如您所说,审核会更改许多表中的值,为此您必须添加列 (Moderated_Value1) x n 其中 n 表示可以更改值的列数。示例:-

        | Value 1 | Value 2 | Moderated_Value1 | Moderated_Value2 |
        |   0     |    1    |      1000        |     2000         |
        

        现在,您的代码变为:-

        <?php 
        if($isModerated == 1) { 
           echo $moderated_post; 
           echo $moderated_value1;
           echo $moderated_value2;
        }else {
           echo $initial_post;
           echo value1;
           echo value2;
        }
        ?>
        

        使用这种方法,还有一个好处是可以通过将表中的isModerated值从1改为0,轻松回滚调节

        【讨论】:

        • 这意味着在所有表格中复制主持人可以更改的所有字段。这是一个过于繁重的解决方案。
        • 不重复@Blackhole!如果帖子尚未经过审核,则包括主帖子表在内的所有表中的Moderated_Value 将为空。 Moderated_Value 列的值只有在 dat post 有一些节制的情况下才会是 null 以外的值。当帖子被审核后,您会在isModerated 为 0 的列中获得审核值,即status pending。只有在您允许访问该审核后,isModerated 的值才会变为 1,即LIVE。你只需要在你的表中插入 n 列,然后用代码修改它们。
        • 哦 @Blackhole ,然后我将我的答案框定为“是的!显然!如果您希望您的适度性很强,那么您的代码中必须有一些“重”。你不能指望它只需要几行额外的代码并获得类似的节制。”
        • Blackhole 是对的,这是有史以来可扩展性和可维护性较差的解决方案。您将拥有双宽表,这不是一个好的解决方案,而且,许多列都为空。否则,每当您改变对可以审核的内容的想法时,您都必须更改表格。一个表必须匹配一个有效的实体关系模型,而这个表不匹配。而且每个帖子只能有一个草稿,因此,此外,它也有局限性。这是一个非常糟糕的主意,应该避免。我敢打赌,你找不到任何使用这种周模型的严肃系统。
        • @CarlosRobles,这种安排没有问题,因为当进行 SQL 查询时,您只需要多查询 1 列,即 isModerated 列主要POSTS 表。如果为 0,则甚至无需浏览可能几乎不会影响整个处理时间的 Moderated_Value 列。所以,在我看来,这一定是处理节制的最方便的方式。在您的回答中,您必须制作一个单独的表格,这是一种非常糟糕的方式,因为它可能会突飞猛进地增加处理时间。
        【解决方案6】:

        当更新发生时,创建一个新行并设置一个需要版主查看的标志。当版主接受时,将接受的记录标记为活动,将之前的记录标记为非活动。在最高级别执行此操作,这样如果您有其他表加入,它们将继承活动/非活动状态和 is_moderated 标志。

        id | text      | is_active | is_moderated | is_accepted
        -------------------------------------------------------
        1  | hello     | 1         | 1            | 1             <-- This is the active row
        

        添加了新记录,将你好改为再见

        id | text      | is_active | is_moderated | is_accepted
        -------------------------------------------------------
        1  | hello     | 1         | 1            | 1             <-- This is still the active row
        2  | goodbye   | 0         | 0            | 0             <-- Moderator needs to accept or deny this
        

        接受结果:

        id | text      | is_active | is_moderated | is_accepted
        -------------------------------------------------------
        1  | hello     | 0         | 1            | 1             <-- This is NOT the active row
        2  | goodbye   | 1         | 1            | 1             <-- Accepted, new active row
        

        拒绝状态:

        id | text      | is_active | is_moderated | is_accepted
        -------------------------------------------------------
        1  | hello     | 1         | 1            | 1             <-- This is still the active row
        2  | goodbye   | 0         | 1            | 0             <-- Moderator denied
        

        您选择的查询变为:

        获取活动行:

        SELECT * FROM TABLENAME WHERE is_active = 1
        

        获取需要审核的行

        SELECT * FROM TABLENAME WHERE is_moderated = 0
        

        【讨论】:

          【解决方案7】:

          您可以为您的表添加修订版并拥有一个复合主键

          假设你有这张桌子

          id -> Integer PK
          comment -> text
          user -> integer
          

          如果您添加此字段

          revision -> integer PK with id
          published -> boolean -- this will tell you wich one is active
          

          您可以添加更多审核字段,例如一些日期以检查新修订的创建时间或修订的创建者

          而且登录获取至极版本很容易,您可以通过其修订号选择任何修订,或者您可以获取活动版本...

          当然,这会给您带来一些并发问题(2 个人同时修改同一行...但您可以使用信号量或类似的东西),您不能继续使用生成器/自动增量插入,(好的,您只能将生成器用于新记录)。

          另一种方法是,如果您不想存储所有更改历史,而只需要最后一个版本,则添加一个文本字段,并将编辑后的值保存为序列化数组,如果您同意,只需运行更新字段。

          有很多方法,你可以使用这个序列化的方法并使用一个辅助表来保存历史,并混合这两个方法,在辅助表中放入原始ID、修订版和序列化数组,然后你两者都有......所有修订的序列化版本......

          选择最能描述您的情况的一个....

          【讨论】:

            【解决方案8】:

            一个想法:为所有表格添加一个修订号。当有人进行更改时,此更改的唯一修订号将存储在所有受影响的行中。而不是更新,而是插入具有更高修订号和相同 id 的行。 id 列当然不能设置为唯一的。当前活动的修订号存储在仅包含此编号的表中。然后生成看起来像以前的表的数据库视图,但如果 2 行具有相同的 id,它只会获取具有最高修订号且等于或低于活动修订版的条目。在您的应用程序中,您使用视图而不是原始表格。 然后,您可以查看原始表格中的更改,删除您不接受的更改,并提高当前活动的修订以接受您尚未删除的所有更改。

            【讨论】:

            • 非常好的解决方案,但很难用像 Doctrine 这样的 ORM 来做,遗憾的是......
            【解决方案9】:

            只要问题相当广泛(并且可以有很多好的答案) - 这不是一个答案,而是另一种可能的解决方案:

            你可以有这样的表:

            id - just autogenerated number
            datetime - date of change
            changed_by - string with name of actor or id of some user
            oldvalue - just old value
            newvalue - just new value
            update_insert_sql - here you put actual SQL which should be executed (you already have this sql in your current code)
            state - 0 - it is not applied to db, 1 - already executed, 2 - you're rejected this change
            datetime_of_apply - just date of action
            datetime_of_reject - just date of action
            

            注意:您可以存储 SQL 来获取当前值,而不是存储旧值,这将在不同主持人对同一行/列进行多次更改时为您提供帮助

            因此,在您的界面中,您将能够看到对所有表的所有建议更改,仅查看实际值和建议的新值,并在需要时更新数据库

            可能的补充:要填充此表,您可以使用触发器,而不是更改当前代码

            【讨论】:

            • 通用解决方案,并不意味着修改当前表。好吧,它并没有真正尊重所有关系数据库的原则,但是好的……你能想出一种方法来使用 ORM 的相同原则吗?
            • 您能详细说明为什么要将 SQL 代码存储在一个单元格中吗?在我看来,将应用程序逻辑存储在一个表中,当版主进行多次更改时,我真的不明白这有什么帮助......
            • @puredevotion 是的,我不会存储 SQL,而是存储要修改的表的名称。嗯,实际上,这几乎是我提出的第二个解决方案。所有其他答案都非常好,但不太适合我的项目(使用 Doctrine 2,并且已经非常先进)。对于多项更改,我只需在表格中添加另一个条目。
            • 我同意纯爱。而且,@blackhole,如果您使用学说,那是因为您想要数据库抽象,这是一个非常好的模式,如果您将表名保存在其他表中,那么您正在做相反的事情,并且也有一个弱点。此外,您必须将代码放入控制器中以决定使用哪个字段,这不干净,并且对排序搜索很疯狂。我不在乎赏金,但请把事情做好。 wordpress joomla 和其他博客/CMS 使用相同的表来发布和修订。有时你不必决定别人已经做过的事情
            • @Blackhole,也将修订作为同一个表中的新行,与主要和未发布相关,如果您使用学说,您只需要一个存储库和要考虑的字段,例如:$rep- >findByPublished(1) 其余所有代码保持不变,因此您的控制器不知道业务级逻辑,例如哪些是经过审核的,也不需要处理它。然后在您的管理员控制器中编写逻辑以将修订设置为主要帖子并在接受修订时取消发布旧的。简单、干净、可扩展、可维护且有效的模型。
            【解决方案10】:

            另一个想法是将所有更改的数据存储在一个数组中(array[$table][$field][$id]=$value),然后将其存储在一个带有 TEXT 类型字段的简单表中,以 json 编码。这样,您也将有足够的灵活性来更改(添加)其他控件,而无需更改编辑表结构。 :-)

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2011-11-07
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-09-15
              • 1970-01-01
              相关资源
              最近更新 更多