14

我正在用 PHP + MySQL 编写一个小型应用程序,并且已经到了有一个对象有几个(到目前为止是 8 个,但预计不会增加)与之关联的标志的地步。这些标志几乎没有关系,尽管有些组合没有意义。该对象代表数据库中的一行(也有一些保存/加载它的方法),所以这个问题也适用于选择存储方法。

问题是 -如何在代码和数据库中最好地表示它们?我可以想到几种方法:

将它们存储在 DB 中的一种方法是在单个整数字段中作为按位标志。在 PHP 方面,我可以想象几种表示它们的方式:

  • 只需导出整数值并定义几个标志常量;让每个需要标志的地方都发挥自己的按位魔法;
  • 定义类方法GetFlag()SetFlag()并对UnsetFlag()私有整数变量执行按位魔术;然后将这些方法作为参数传递给标志常量之一。
  • 定义方法GetFlagA()GetFlagB()(以及 Set 和 Unset 对应项);
  • 定义一堆成员变量,每个变量代表一个标志;将它们设置为从数据库加载并在保存时收集它们。
  • 创建一个成员变量,它是所有标志值的数组。使用预定义的常量作为数组索引来访问每个标志。还在加载/保存时填充/收集数组。

另一种方法是将它们作为单独的 BIT 字段存储在数据库中。在 PHP 中,这将转换为几个成员变量。恕我直言,这会使查询复杂化。

最后一种方法是为所有标志定义另一个表,并为标志和原始对象之间的多对多关系定义一个中间表。恕我直言,所有解​​决方案中最混乱的,考虑到否则只有 3 张桌子。

我没有做过太多的 PHP 开发,所以我不知道最佳实践是什么。在 C# 中,我可能会将它们存储为按位标志,并创建对私有整数执行按位魔术的属性。但是 PHP 没有属性(我使用的是最新的稳定版本)...

4

7 回答 7

36

在您的模型中,该对象具有 8 个布尔属性。这意味着您的数据库表中有 8 个布尔(MySQL 为 TINYINT)列,对象中有 8 个 getter/setter 方法。简单而传统。

重新考虑您当前的方法。想象下一个必须维护这个东西的人会说什么。

CREATE TABLE mytable (myfield BIT(8));

好的,看起来我们将在这里发生一些二进制数据。

INSERT INTO mytable VALUES (b'00101000');

等等,有人再告诉我这些 1 和 0 分别代表什么。

SELECT * FROM mytable;
+------------+
| mybitfield |
+------------+
| (          | 
+------------+

什么?

SELECT * FROM mytable WHERE myfield & b'00101000' = b'00100000';

哇!?哇!?

刺自己的脸


-- 与此同时,在另一个宇宙中,仙女与独角兽玩耍,程序员并不讨厌 DBA……--

SELECT * FROM mytable WHERE field3 = 1 AND field5 = 0;

幸福和阳光!

于 2009-01-10T02:43:17.747 回答
2

如果您确实必须使用位标志,则使用 SET 列将它们存储在数据库中,代码中的关联数组以及打开/关闭标志的方法。如果您需要该格式的数组,请添加单独的方法以将数组转换为单个整数。

使用低级位运算符没有任何好处,因此您不妨使代码可读。

于 2009-01-09T18:46:25.463 回答
1

我编写了这个简单的函数来填补 PHP 和 MySQL ENUM 之间的空白:

function Enum($id)
{
    static $enum = array();

    if (func_num_args() > 1)
    {
        $result = 0;

        if (empty($enum[$id]) === true)
        {
            $enum[$id] = array();
        }

        foreach (array_unique(array_map('strtoupper', array_slice(func_get_args(), 1))) as $argument)
        {
            if (empty($enum[$id][$argument]) === true)
            {
                $enum[$id][$argument] = pow(2, count($enum[$id]));
            }

            $result += $enum[$id][$argument];
        }

        return $result;
    }

    return false;
}

用法:

// sets the bit flags for the "user" namespace and returns the sum of all flags (15)
Enum('user', 'anonymous', 'registed', 'active', 'banned');

Enum('user', 'anonymous'); // 1
Enum('user', 'registed', 'active'); // 2 + 4 = 6
Enum('user', 'registed', 'active', 'banned'); // 2 + 4 + 8 = 14
Enum('user', 'registed', 'banned'); // 2 + 8 = 10

您只需要确保以与 MySQL ENUM 相同的顺序定义枚举列表。

于 2010-06-23T22:49:59.780 回答
0

假设您使用 5.0.5 之后的 MySQL 版本,您可以将列定义为 BIT[此处的位数]。至于 PHP 方面,我可能会使用 Get/SetFlagA、Get/SetFlagB 方法,不需要 unset 方法,因为您可以简单地将布尔值传入 set 方法。

于 2009-01-09T18:23:57.380 回答
0

我会远离按位运算,因为仅通过查看数据库就很难知道设置了哪些标志(除非您出于某种原因试图变得超级高效)。在其他两个选项之间,我认为这取决于这些标志中的每一个具有多少意义。鉴于已经有 8 个,我会倾向于另一张表,它们之间存在多对多关系(抱歉选择了你最不喜欢的)。

于 2009-01-09T18:27:09.947 回答
0

将它们存储在 DB 中的一种方法是在单个整数字段中作为按位标志。

如果你想这样做,你可以使用 __get 和 __set重载方法,这样你就可以简单地检索字段,然后根据需要进行按位运算。

于 2009-01-09T19:28:14.613 回答
0

好的,在考虑了更多之后,我决定为每个标志使用一个成员变量。我本可以使用 getter/setter 方法,但我不会在代码中的其他任何地方使用它们,所以这会过时。另外,通过这种方式,我还可以从 DB 存储方法中抽象出来,以后可以在需要时轻松更改它。

至于数据库 - 我现在将继续使用按位整数 - 主要是因为我几乎完成了软件并且不想再更改它。它根本不会改变可读性。

于 2009-01-09T21:35:40.097 回答