Sotomajor.org.ua - PHP development: BitMaskPackage - средство генерации SQL-запросов с использованием битовых масок

Описанный в предыдущей статье метод гораздо удобнее использовать с помощью классов, которые описаны ниже. Этот набор классов
предназначен использовать все описанные возможности битовых масок для хранения бинарных свойств типа да/нет в одной переменной, но при этом
абсолютно абстрагироваться от числовых представлений свойств и битовых операций. Все что нужно – это создать коллекцию свойств и затем передать её экземпляру класа
BitCondition или BitAction, которые помогут легко построить SQL запросы к БД.

corner icon

BitMaskPackage - средство генерации SQL-запросов с использованием битовых масок

2008-04-14 12:24 // // Раздел:

Описанный в предыдущей статье (Использование битовых масок в веб-программировании) метод гораздо удобнее использовать с помощью классов, которые описаны ниже. Этот набор классов позволяет использовать все описанные возможности битовых масок для хранения бинарных свойств типа да/нет в одной переменной, но при этом абсолютно абстрагироваться от числовых представлений свойств и битовых операций. Все что нужно - это создать коллекцию свойств и затем передать её экземпляру класа BitCondition или BitAction, которые помогут легко построить SQL запросы к БД. Ниже будут описаны все public методы классов:

  • BitPropertiesCollection - класс, описывающий коллекцию свойств, которые могут использоваться при построении правил и условий.
  • BitCondition - с помощью этого класса строятся условия секции WHERE на основе свойств, которые описаны в BitPropertiesCollection.
  • BitAction - с помощью этого класса строятся части запросов UPDATE и SELECT на основе свойств, которые описаны в BitPropertiesCollection.

BitPropertiesCollection

__construct ([array names]) - конструктор класса.
Необязательный аргумент - массив имен свойств, каждому из которых будет присвоена степень (номер бита слева направо начиная с нуля). В принципе, разработчик, который использует этот набор классов, может не заботиться о том, какие степени будут иметь свойства. Тем не менее, существует возможность вручную назначить степень свойству (метод setProperty()), который будет описан ниже.

void addProperty (string name) - добавление свойства в коллекцию.
Если появилась необходимость добавить свойство в коллекцию после инстанциирования объекта и указания массива свойств в конструкторе, это можно сделать с помощью этого метода. При его использовании, добавляемому свойству в качестве степени будет назначено минимальное неотрицательное целое число, не превосходящее степеней уже добавленных свойств.

void setProperty (string name, int position) - добавление свойства в коллекцию с указанием степени для него.
Этот метод добавляет свойство name в коллекцию и устанавливает для него степень position. В случае, если для степени position уже есть свойство, оно будет перезаписано.

Следует обратить внимание, что при добавлении этим методом свойства со степенью большей, чем следующая, оставшийся свободный промежуток уже нельзя будет заполнить методом addProperty().

Например, если в коллекции есть свойства со степенями 0, 1 и 2 и методом setProperty() было добавлено свойство со степенью 5, последующий вызов метода addProperty() сможет добавить свойство со степенью 6, а для заполнения степеней 3 и 4 прийдется вызывать метод setProperty() с соответствующими параметрами.

int getPropertyMask (string name) - получение битовой маски свойства по его имени.
Возвращает десятичное представление битовой маски свойства по его имени. Фактически, это значение степени свойства, возведенное в квадрат. Если свойство с таким именем в коллекции отсутствует, выбрасывается исключение.

boolean applyMaskToValue (string name, int value) - проверяет наличие свойства name в значении value.
Возвращает TRUE если значение value содержит свойство name, или FALSE в противном случае.

BitAction

__construct (BitPropertiesCollection &collection) - конструктор класса.
Принимает и устанавливает в качестве переменной-члена класса коллекцию свойств.

void appendRule (string name, boolean value) - добавление свойства name со значением value к правилу запроса.
Добавляет свойство name со значением value к битовой маске, которая будет использована при генерации части запроса. Если свойства с таким именем нету в коллекции, которая была передана в конструктор класса, будет выброшено исключение.

string getUpdateSQLExpression (string field_name) - генерация части запроса типа UPDATE.
Возвращает сгенерированную часть запроса, которая включает имя поля field_name, обрамленное в обратные кавычки, символ "=" и значение, которое нужно присвоить данному полю. Кроме обрамления в обратные кавычки, имя поля field_name не подвергается обработке.

int getInsertSQLExpression (void) - генерация значения для запроса типа INSERT.
Возвращает значение для использования в запросе типа INSERT.
Следует заметить, что возвращается совокупность битовых масок только тех свойств, которые были добавлены в экземпляр класса с помощью метода appendRule() с указанием второго параметра TRUE. Это связано с тем, что при добавлении новой строки в таблицу, значения поля не существует как такового, поэтому оно не может содержать свойств, которые нужно сбросить. Все свойства, добавленные с параметром FALSE - игнорируются.

BitCondition

Этот класс позволяет строить условия для SQL-запросов. Каждый экземпляр класса можно представить как группу условий, разделенных операторами OR или AND. Каждая группа может иметь только один тип условия, который задается методом setRuleType(). Если группа содержит только один элемент, то тип условия можно не указывать.

В эту группу можно добавлять как одиночное условие (с помощью метода appendRule()), так и другую группу-экземпляр класса (с помощью метода extendRule()).

__construct (BitPropertiesCollection &collection) - конструктор класса.
Принимает и устанавливает в качестве переменной-члена класса коллекцию свойств.

setRuleType (string type) - устанавливает тип условия для группы.
Параметр type может принимать значения OR или AND. При указании некорректного типа будет выброшено исключение.

getRuleType (void) - возвращает текущий тип условия.
Возвращает тип условия в верхнем регистре, либо NULL если тип условия не был задан.

void appendRule (string name, boolean value) - добавление свойства name со значением value к условия запроса.
Добавляет свойство name со значением value к битовой маске, которая будет использована при генерации условия запроса. Если свойства с таким именем нету в коллекции, которая была передана в конструктор класса, будет выброшено исключение.

void extendRule (BitCondition rule) - Добавляет группу rule в текущую группу.
Добавляет группу rule в текущую группу связывая добавляемую группу с условиями текущей группы установленным типом условия. Более подробно - см. примеры.

string getSQLCondition (string field_name) - генерация части запроса типа UPDATE.
Возвращает сгенерированную часть запроса, которая должна идти после WHERE.

Примеры

Примеры находятся в директории examples. Скачать BitMaskPackage 0.2 можно здесь. Приведенные примеры в основном, реализуют тесты, описанные в статье Использование битовых масок в веб-программировании.

test_1.php

Нижеуказанный код наполняет таблицы users_classic и users_bm случайными значениями. SQL запрос на создание таблиц в файле test_1.sql

php:
<?php
//Подключение набора классов
require_once '../class.sbm_bit_action.php';
require_once '../class.sbm_bit_condition.php';
require_once '../class.sbm_bit_properties_collection.php';

//Подключение к БД
mysql_connect('localhost', 'root', 'root');
mysql_select_db('test');

//Количество добавляемых записей
define ('records_count', 500);

//Ассоциативный массив имен свойств
$properties = array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', 'p14', 'p15');

//Инстанциирование BitPropertiesCollection
$collection = new BitPropertiesCollection($properties);

//Цикл генерации случайных значений для каждой записи
for ($i = 1; $i <= records_count; $i++)
{
        //Инстанциирование BitAction
        $action = new BitAction($collection);

        //Генерируем случайное значение для каждого свойства текущей записи
        $values = array();
        foreach ($properties as $p)
        {
                $val = rand(0,1);
                $action->appendRule($p, (bool)$val);
                $values[] = $val;
        }
       
        //Запрос вставки в обычную таблицу
        $cSQL = 'INSERT INTO `users_classic` (`'.implode('`, `', $properties).'`) VALUES ("'.implode('", "', $values).'")';
        mysql_query($cSQL) or die(mysql_error());
       
        //Запрос вставки в таблицу, использующую битовые маски
        $bmSQL = 'INSERT INTO `users_bm` (`status`) VALUES ("'.$action->getInsertSQLExpression().'")';
        mysql_query($bmSQL) or die(mysql_error());
}

exit;

test_2.php

Выборка по условию: не p1 или не p2 или p3.

php:
<?php
require_once '../class.sbm_bit_action.php';
require_once '../class.sbm_bit_condition.php';
require_once '../class.sbm_bit_properties_collection.php';

$properties = array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', 'p14', 'p15');
$collection = new BitPropertiesCollection($properties);

$condition = new BitCondition($collection, 'or');
$condition->appendRule('p1', false);
$condition->appendRule('p2', false);
$condition->appendRule('p3', true);

echo 'SELECT * FROM `users_bm` WHERE '.$condition->getSQLCondition('status');

test_3.php

Установить свойство p2 и сбросить свойство p3 для всех, у кого установлено свойство p1.

php:
<?php
require_once '../class.sbm_bit_action.php';
require_once '../class.sbm_bit_condition.php';
require_once '../class.sbm_bit_properties_collection.php';

$properties = array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', 'p14', 'p15');
$collection = new BitPropertiesCollection($properties);

$condition = new BitCondition($collection);
$condition->appendRule('p1', true);

$action = new BitAction($collection);
$action->appendRule('p2', true);
$action->appendRule('p3', false);

echo 'UPDATE `users_bm` SET '.$action->getUpdateSQLExpression('status').' WHERE '.$condition->getSQLCondition('status');
//Выведет: UPDATE `users_bm` SET `status` = `status` & ~4 | 2 WHERE `status` & 1 = 1

test_6.php

Примеры test_4.php и test_5.php очень похожи на первый, поэтому я их не буду здесь описывать. А вот 6 пример представляет собой выборку со сложным условием: свойства p1, p2, p3, p4, p5 установлены И (p6 или p7 не установлено ) И (p8 или p9 p10 или p11 не установлено) И (p12 или p13 установлено или (p14 установлено и p15 не установлено)).

На практике врядли прийдётся строить такого монстра, но здесь можно хорошо увидеть как работает метод extendRule().

php:
<?php
require_once '../class.sbm_bit_action.php';
require_once '../class.sbm_bit_condition.php';
require_once '../class.sbm_bit_properties_collection.php';

$properties = array('p1', 'p2', 'p3', 'p4', 'p5', 'p6', 'p7', 'p8', 'p9', 'p10', 'p11', 'p12', 'p13', 'p14', 'p15');
$collection = new BitPropertiesCollection($properties);

$condition = new BitCondition($collection, 'and');
$condition->appendRule('p1', true);
$condition->appendRule('p2', true);
$condition->appendRule('p3', true);
$condition->appendRule('p4', true);
$condition->appendRule('p5', true);

$condition1 = new BitCondition($collection, 'or');
$condition1->appendRule('p6', false);
$condition1->appendRule('p7', false);

$condition2 = new BitCondition($collection, 'or');
$condition2->appendRule('p8', false);
$condition2->appendRule('p9', false);
$condition2->appendRule('p10', false);
$condition2->appendRule('p11', false);

$condition3 = new BitCondition($collection, 'or');
$condition3->appendRule('p12', true);
$condition3->appendRule('p13', true);

$condition4 = new BitCondition($collection, 'and');
$condition4->appendRule('p14', true);
$condition4->appendRule('p15', false);

$condition3->extendRule($condition4);
$condition->extendRule($condition1);
$condition->extendRule($condition2);
$condition->extendRule($condition3);

echo 'SELECT * FROM `users_bm` WHERE '.$condition->getSQLCondition('status');
/*
Будет выведено:
SELECT * FROM `users_bm` WHERE `status` & 31 = 31 AND ((((`status` & 32 = 0) OR (`status` & 64 = 0))) AND (((`status` & 128 = 0) OR (`status` & 256 = 0) OR (`status` & 512 = 0) OR (`status` & 1024 = 0))) AND (((`status` & 2048 = 2048) OR (`status` & 4096 = 4096) OR (`status` & 8192 = 8192 AND `status` & 16384 = 0))))
*/

Резюме

Если у вас возникнут какие-то вопросы по использованию класса, предложения по улучшению или баг-репорт, то можно оставлять в коментах или писать мне на мыло (оно есть в разделе "Контакты").

Скачать BitMaskPackage можно здесь

  • — Написал: Сим · 2008-08-11 23:04 · #

    А на каких языках Вы вообще пишете?

  • — Написал: Сотомайор · 2008-08-12 09:51 · #

    Преимущественно PHP.

Добавить комментарий: