Я пишу небольшое приложение в PHP + MySQL и дошел до того, что есть объект, у которого есть пара (до сих пор, но не ожидается увеличения) связанных с ним флагов. Флаги довольно несвязаны, хотя есть несколько комбинаций, которые не имели бы никакого смысла. Объект представляет строку в БД (имеет некоторые методы для ее сохранения и загрузки), поэтому вопрос также относится к выбору метода хранения.
Вопрос в том, как наилучшим образом представлять их как в коде, так и в БД? Я могу думать о нескольких путях:
Один из способов их хранения в БД состоит в одном целочисленном поле в виде поразрядных флагов. На стороне PHP я могу представить несколько способов их представления:
GetFlag()
, SetFlag()
и UnsetFlag()
которые выполняют UnsetFlag()
магию для частной целочисленной переменной; Эти методы затем будут переданы одной из констант флага в качестве параметров. GetFlagA()
, GetFlagB()
и т. Д. (Вместе с Set and Unset); Другой способ – сохранить их в БД в виде отдельных полей BIT. В PHP, который затем переводится на несколько переменных-членов. ИМХО это усложнит запросы.
И последний способ – определить анонированную таблицу для всех флагов и промежуточную таблицу для отношений «многие ко многим» между флагами и исходными объектами. ИМХО – самый грязный из всех решений, учитывая, что в противном случае будет только 3 таблицы.
Я не делал много PHP-разработки, поэтому я не знаю, какой будет лучшая практика. В C # я, вероятно, сохранил бы их как побитовые флаги и делал бы свойства, которые выполняют побитную магию на частном целое. Но у PHP нет свойств (я использую последнюю стабильную версию) …
В вашей модели объект имеет 8 булевых свойств. Это означает 8 столбцов boolean (TINYINT для MySQL) в вашей таблице базы данных и 8 методов getter / setter в вашем объекте. Простой и обычный.
Переосмыслите свой нынешний подход. Представьте себе, что будет говорить следующий парень, который должен поддерживать эту вещь.
CREATE TABLE mytable (myfield BIT(8));
Хорошо, похоже, что у нас будут бинарные данные, которые здесь происходят.
INSERT INTO mytable VALUES (b'00101000');
Подождите, кто-нибудь снова скажет мне, что означает каждый из этих 1s и 0s.
SELECT * FROM mytable; +------------+ | mybitfield | +------------+ | ( | +------------+
Какие?
SELECT * FROM mytable WHERE myfield & b'00101000' = b'00100000';
WTF !? WTF !?
бросается в глаза
– между тем, в альтернативной вселенной, где феи играют с единорогами, а программисты не ненавижу администраторов баз данных … –
SELECT * FROM mytable WHERE field3 = 1 AND field5 = 0;
Счастье и солнце!
Если вам действительно нужно использовать битовые флаги, используйте столбец SET для их хранения в БД, ассоциативный массив в коде и методы включения / выключения флагов. Добавьте отдельные методы для преобразования массива в / из одного целого числа, если оно вам нужно в этом формате.
Нет ничего, что можно было бы получить, используя низкоуровневые битовые операторы, чтобы вы могли сделать код читаемым.
Я закодировал эту простую функцию, чтобы заполнить пробел между 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.
Предполагая, что вы используете версию MySQL после 5.0.5, вы можете определить столбец как BIT [ количество бит здесь ]. Что касается стороны PHP, я бы, вероятно, пошел бы с помощью метода Get / SetFlagA, Get / SetFlagB, нет необходимости в методе unset, поскольку вы можете просто использовать логический метод set.
Я бы держался подальше от побитовых операций, так как очень сложно узнать, какие флаги установлены, глядя только в базу данных (если вы по какой-то причине не пытаетесь быть суперэффективными). Между двумя другими вариантами я думаю, что это зависит от того, насколько значителен каждый из этих флагов. Учитывая, что их уже 8, я склоняюсь к другому столу со многими и многими отношениями между ними (извините за выбор наименее любимого).
Один из способов их хранения в БД состоит в одном целочисленном поле в виде поразрядных флагов.
Если вы хотите сделать это, вы можете использовать методы перегрузки __get и __set, чтобы вы могли просто получить поле, а затем выполнить поразрядную арифметику по мере необходимости.
Хорошо, подумав еще об этом, я решил пойти с одной переменной-членом на один флаг. Я мог бы использовать метод метода getter / setter, но я не использую их нигде в моем коде, поэтому это было бы не в стиле. Плюс, таким образом, я также абстрагируюсь от метода хранения БД и позже могу легко изменить это, если это необходимо.
Что касается БД – я собираюсь остаться с побитовым целым на данный момент – в основном потому, что я почти закончил с программным обеспечением и больше не хочу его менять. Это не изменяет читаемость вообще.