<?
RegEnum("RefBookQuerySortType", "Тип сортировки по полю",
"rqstUnsorted", "Сортировка по полю не осуществляется",
"rqstAscending", "Сортировка по возрастанию",
"rqstDescending", "Сортировка по убыванию"
);
RegEnum("RefBookQueryCriterionOperator", "Тип оператора критерия отбора информации справочников",
"qcoNone", "Не указан",
"qcoEqual", "Равно",
"qcoNotEqual", "Не равно",
"qcoMore", "Больше",
"qcoMoreEqual", "Больше или равно",
"qcoLess", "Меньше",
"qcoLessEqual", "Меньше или равно",
"qcoBetween", "Между",
"qcoIn", "Входит в массив",
"qcoNotIn", "Не входит в массив",
"qcoLike", "Совпадает с",
"qcoNotLike", "Не совпадает с",
"qcoBeginFrom", "Начинается с символа",
"qcoNotBeginFrom", "Начинается не с символа"
);
/**
@desc Элемент фильтра отбора информации справочников
@prop Name Имя переменной фильтра
@prop Value Значения соответствующей переменной
@link RefBookQuery
*/
class RefBookQueryFilterItem extends Record {
public $Name;
public $Value;
/**
* @return integer
* @param integer $Field
* @desc Определение типа свойства класса
*/
static function GetType($Field){
switch($Field){
case ID_NAME: return FT_STRING;
case ID_VALUE: return FT_STRING;
default: return parent::GetType($Field);
}
}
}
/**
@desc Элемент критерия отбора информации справочников
@prop Operator Оператор критерия
@prop Value1 Значения критерия
@prop Value2 Значения критерия
@link RefBookQuery
@link RefBookQueryCriterion
*/
class RefBookQueryCriterionItem extends Object {
public $Operator;
public $Value1 = "";
public $Value2 = "";
/**
* @server
* @return void
* @desc Конструктор
*/
function __construct(){
parent::__construct();
}
/**
* @return integer
* @param integer $Field
* @desc Определение типа свойства класса
*/
static function GetType($Field){
switch($Field){
case ID_OPERATOR: return ID_REFBOOKQUERYCRITERIONOPERATOR;
case ID_VALUE1: return FT_STRING;
case ID_VALUE2: return FT_STRING;
default: return parent::GetType($Field);
}
}
}
/**
@desc Критерий выбора информации справочника
*/
class RefBookQueryCriterion extends Object {
public $Item1;
public $Item2;
public $Item3;
public $Item4;
/**
* @server
* @return void
* @desc Конструктор
*/
function __construct(){
parent::__construct();
$this->Item1 = new RefBookQueryCriterionItem;
$this->Item2 = new RefBookQueryCriterionItem;
$this->Item3 = new RefBookQueryCriterionItem;
$this->Item4 = new RefBookQueryCriterionItem;
}
/**
* @return integer
* @param integer $Field
* @desc Определение типа свойства класса
*/
static function GetType($Field){
switch($Field){
case ID_ITEM1: return ID_REFBOOKQUERYCRITERIONITEM;
case ID_ITEM2: return ID_REFBOOKQUERYCRITERIONITEM;
case ID_ITEM3: return ID_REFBOOKQUERYCRITERIONITEM;
case ID_ITEM4: return ID_REFBOOKQUERYCRITERIONITEM;
default: return parent::GetType($Field);
}
}
}
/**
@desc Поле, участвующее в запросе на получение данных из справочника
@prop GUID Идентификатор поля
@prop Output Включить данное поле в результат
@prop SortType Тип сортировки
@prop SortOrder Порядок сортировки
@prop Criteria Условия
@link RefBookQuery
*/
class RefBookQueryField extends Object {
public $GUID;
public $Name = "";
public $Output = false;
public $SortType;
public $SortOrder;
public $Criterion;
public $ColIndex = -1;
/**
* @server
* @return void
* @desc Конструктор
*/
function __construct(){
parent::__construct();
$this->Criterion = new RefBookQueryCriterion;
}
/**
* @return integer
* @param integer $Field
* @desc Определение типа свойства класса
*/
static function GetType($Field){
switch($Field){
case ID_GUID: return FT_STRING;
case ID_NAME: return FT_STRING;
case ID_OUTPUT: return FT_BOOLEAN;
case ID_SORTTYPE: return ID_REFBOOKQUERYSORTTYPE;
case ID_SORTORDER: return FT_INTEGER;
case ID_CRITERION: return ID_REFBOOKQUERYCRITERION;
case ID_COLINDEX: return FT_INTEGER;
default: return parent::GetType($Field);
}
}
}
/**
@desc Объектное представление запроса на получение данных из справочника
@prop Table Идентификатор таблицы, из которой осуществляется выборка данных
@prop RefField Идентификатор поля, по которому соединяется данная таблица
@prop Fields Список полей, используемых в запросе
@prop Joins Список связанных таблиц
@prop Alias Имя таблицы в запросе. Используется при конвертации в запрос баз данных.
@link RefBook::RefQueryToQuery
*/
class RefBookQuery extends Object {
public $Table = "";
public $RefField = "";
public $Fields;
public $Joins;
public $Alias = "";
/**
* @server
* @return void
* @desc Конструктор
*/
function __construct(){
parent::__construct();
$this->Fields = array();
$this->Joins = array();
$this->Table = EMPTY_GUID_STR;
$this->RefField = EMPTY_GUID_STR;
}
/**
* @return integer
* @param integer $Field
* @desc Определение типа свойства класса
*/
static function GetType($Field){
switch($Field){
case ID_TABLE: return FT_STRING;
case ID_REFFIELD: return FT_STRING;
case ID_FIELDS: return FT_ARRAY | ID_REFBOOKQUERYFIELD;
case ID_JOINS: return FT_ARRAY | ID_REFBOOKQUERY;
case ID_ALIAS: return FT_STRING;
default: return parent::GetType($Field);
}
}
/**
* @server
* @return string[]
* @desc Получение массива идентификаторов используемых полей
* @link RefBookQuery::ConvertToDBQuery
Получение массива идентификаторов полей справочника, используемых
для постоения запроса, включая ссылочные поля и поля подзапросов.
*/
function GetUsedFields(){
if($this->RefField != EMPTY_GUID_STR){
$ret = array($this->RefField);
} else {
$ret = array();
}
for($i = 0; $i < sizeof($this->Fields); $i++){
$ret[] = $this->Fields[$i]->GUID;
}
for($i = 0; $i < sizeof($this->Joins); $i++){
$ret = array_merge($ret, $this->Joins[$i]->GetUsedFields());
}
return $ret;
}
/**
* @server
* @return string[]
* @param RefBookField[] $Fields
* @desc Получение порядка сортировки запроса
*/
function GetSortOrder($Fields){
$ret = array();
for($i = 0; $i < sizeof($this->Fields); $i++){
if($this->Fields[$i]->SortType != rqstUnsorted && array_key_exists($this->Fields[$i]->GUID, $Fields)){
$ret[$this->Fields[$i]->SortOrder-1]["name"] = Stick($this->Alias, DBParseWord($Fields[$this->Fields[$i]->GUID]->Name));
$ret[$this->Fields[$i]->SortOrder-1]["desc"] = ($this->Fields[$i]->SortType == rqstDescending);
}
}
for($i = 0; $i < sizeof($this->Joins); $i++){
$ret = $ret + ($this->Joins[$i]->GetSortOrder($Fields));
}
return $ret;
}
/**
* @server
* @return boolean
* @param DBWhere $Where
* @param RefBookField[] $Fields
* @param RefBookQueryFilterItem[] $Filter
* @desc Формирование блока условий в SQL запросе
*/
function BuildConditions(DBWhere $Where, $Fields, $Filter){
// Настраиваем условия выбора
$Where->BeginGroup();
$HasCondition = false;
for($i = 1; $i <= 4; $i++){
if($this->DoBuildConditions($Where, $Fields, $i, false, $Filter)){
$Where->EndGroup();
$HasCondition = true;
}
}
if(!$HasCondition){
// Добавляем безобидное условие,
// для того, чтобы не возникло ошибки в
// случае отсутствия других условий
$Where->ANDItem(Stick($this->Alias, col_ID), wclNotIsNull);
}
$Where->EndGroup();
if(is_array($Filter)){
for($i = 0; $i < sizeof($Filter); $i++){
if($Filter[$i]->Name == "ID" && $Filter[$i]->Value != ""){
$Where->ANDItem(Stick($this->Alias, col_ID), wclIn, $Filter[$i]->Value, true);
break;
}
}
}
}
/**
* @server
* @return boolean
* @param DBWhere $Where
* @param RefBookField[] $Fields
* @param integer $ItemIdx
* @param boolean $Opened
* @param RefBookQueryFilterItem[] $Filter
* @desc Формирование блока условий в SQL запросе
*/
function DoBuildConditions(DBWhere $Where, $Fields, $ItemIdx, $Opened, $Filter){
$ItemName = sprintf('Item%s', $ItemIdx);
for($i = 0; $i < sizeof($this->Fields); $i++){
$Item = $this->Fields[$i]->Criterion->$ItemName;
if(array_key_exists($this->Fields[$i]->GUID, $Fields)){
if($Item->Operator != qcoNone){
if(!$Opened){
$Where->BeginGroup(true);
$Opened = true;
}
$RefField = $Fields[$this->Fields[$i]->GUID];
$v = array();
$v[0] = $Item->Value1;
$v[1] = $Item->Value2;
for($j = 0; $j < 2; $j++){
$str = $v[$j];
if(strlen($str) >= 4 && $str{0} == "@"){
$tag = substr($str, 1, 2);
$val = substr($str, 4);
switch($tag){
case "DC": $v[$j] = RefBook::CalculateDate(intval($val)); break;
}
}
$v[$j] = $this->ParseScript($v[$j], $Filter);
}
$FieldName = Stick($this->Alias, $RefField->Name);
switch($Item->Operator){
default:
case qcoEqual: $Clause = wclEqual; break;
case qcoNotEqual: $Clause = wclNotEqual; break;
case qcoMore: $Clause = wclMore; break;
case qcoMoreEqual: $Clause = wclMoreEqual; break;
case qcoLess: $Clause = wclLess; break;
case qcoLessEqual: $Clause = wclLessEqual; break;
case qcoBetween: $Clause = wclMoreEqual; break;
case qcoIn: $Clause = wclIn; break;
case qcoNotIn: $Clause = wclNotIn; break;
case qcoLike:
case qcoNotLike:
if($Item->Operator == qcoLike){
$Clause = wclLike;
} else {
$Clause = wclNotLike;
}
$arr = explode(' ', $v[0]);
$arr1 = array();
for($cc = 0; $cc < sizeof($arr); $cc++){
$arr[$cc] = trim($arr[$cc]);
if($arr[$cc]!=""){
$arr1[] = "'".EncodeString(str_replace('*', '%', $arr[$cc]))."%'";
}
}
if(sizeof($arr1) > 0){
if(sizeof($arr1) > 1){
$v[0] = $arr1;
} else {
$v[0] = $arr1[0];
}
} else {
$v[0] = "'%'";
}
break;
case qcoBeginFrom:
case qcoNotBeginFrom:
if($Item->Operator == qcoBeginFrom){
$Clause = wclIn;
} else {
$Clause = wclNotIn;
}
$FieldName = sprintf("ASCII(LEFT(%s, 1))", $RefField->Name);
$chars = $v[0];
$chArr = array();
for($cc = 0; $cc < strlen($chars); $cc++){
$char = strtolower(trim($chars{$cc}));
if(strlen($char) > 0){
if(!in_array(ord($char), $chArr))
$chArr[] = ord($char);
$char = strtoupper($char);
if(!in_array(ord($char), $chArr))
$chArr[] = ord($char);
}
}
$v[0] = implode(', ', $chArr)." /*".$chars."*/";
break;
}
if($Item->Operator != qcoBetween && is_array($v[0])){
$Where->BeginGroup();
for($k = 0; $k < sizeof($v[0]); $k++){
$Where->ORItem($FieldName, $Clause, $v[0][$k], true);
}
$Where->EndGroup();
} else
$Where->ANDItem($FieldName, $Clause, $v[0], true);
if($Item->Operator == qcoBetween){
$Where->ANDItem($FieldName, wclLessEqual, $v[1], true);
}
}
}
}
for($i = 0; $i < sizeof($this->Joins); $i++){
$Opened = $this->Joins[$i]->DoBuildConditions($Where, $Fields, $ItemIdx, $Opened, $Filter);
}
return $Opened;
}
/**
* @server
* @return DBSelect
* @param DBDriver $Sock
* @param RBTTypes $Attrs
* @param boolean $OnlyCalcFields
* @param boolean $ExcludeConditions
* @param RefBookQueryFilterItem[] $Filter
* @desc Преобразование в запрос к базе данных
*/
function ConvertToDBQuery(DBDriver $Sock, $Attrs = null, $OnlyCalcFields = false, $ExcludeConditions = false, $Filter = null){
if(!IsSubclassOf($Attrs, 'Set')){
$Attrs = new RBTTypes();
$Attrs->V = RefBook::GetTableAttributes($this->Table);
}
// Получаем идентификаторы всех используемых
// данным запросом полей.
$UsedFieldsIDs = $this->GetUsedFields();
// Получаем информацию об используемых полях
$UsedFields = RefBook::GetFieldsByIDs($UsedFieldsIDs);
// Создаем запрос
$Query = $Sock->Query(qtSelect, sprintf("%s %s", RefBook::TableName($this->Table), $this->Alias));
$this->BuildDBQuery($Query, $UsedFields, "", $OnlyCalcFields);
if(!$ExcludeConditions){
// Настраиваем условия отбора
$this->BuildConditions($Query->Where, $UsedFields, $Filter);
}
// Настриаваем сортировку
$Order = $this->GetSortOrder($UsedFields);
ksort($Order);
for($i = 0; $i < sizeof($Order); $i++){
$Query->OrderBy($Order[$i]["name"], $Order[$i]["desc"]);
}
// Проверяем наличие в запросе идентификаторов
$FieldNames = array();
for($i = 0; $i < sizeof($this->Fields); $i++){
if(array_key_exists($this->Fields[$i]->GUID, $UsedFields) && $this->Fields[$i]->Output)
$FieldNames[] = strtolower($UsedFields[$this->Fields[$i]->GUID]->Name);
}
if(!in_array("id", $FieldNames)){
$Query->AddEx($this->Alias, col_ID, col_ID);
}
if($Attrs->Contains(rtHierarchic)){
$Query->AddEx($this->Alias, col_Parent, col_Parent);
} else {
$Query->Add("0", col_Parent);
}
if($Attrs->Contains(rtDetail)){
$Query->AddEx($this->Alias, col_Owner, col_Owner);
} else {
$Query->Add("0", col_Owner);
}
return $Query;
}
/**
* @server
* @return void
* @param DBSelect $Query
* @param RefBookField[] $Fields
* @param string $Path
* @param boolean $OnlyCalcFields
* @desc Наполнение запросе информацией
* @link RefBookQuery::ConvertToDBQuery
*/
function BuildDBQuery(DBSelect $Query, $Fields, $Path = "", $OnlyCalcFields = false){
// Добавляем в запрос поля,
// которые должны вернуться
for($i = 0; $i < sizeof($this->Fields); $i++){
if($this->Fields[$i]->Output){
if(array_key_exists($this->Fields[$i]->GUID, $Fields)){
$Field = $Fields[$this->Fields[$i]->GUID];
$n = Stick($Path, $Field->Name);
$this->Fields[$i]->Name = $n;
if($Field->Kind == fkCalculated){
$script = $this->ParseScript($Field->SQLScript);
$Query->Add(sprintf("(%s)", trim($script)), $n);
} elseif(!$OnlyCalcFields) {
if($Field->DataType == fdtGUID){
$Query->AddEx($this->Alias, $Field->Name, $n, ftGUID);
} else {
$Query->AddEx($this->Alias, $Field->Name, $n);
}
}
}
}
}
// Подключаем другие таблицы запроса
for($i = 0; $i < sizeof($this->Joins); $i++){
$RefField = $Fields[$this->Joins[$i]->RefField];
$JoinTable = RefBook::TableName($this->Joins[$i]->Table);
$JoinAlias = $this->Joins[$i]->Alias;
if($RefField->Kind == fkReference)
$Query->InnerJoin($JoinTable, $JoinAlias, $this->Alias, $RefField->Name, wclEqual, col_ID);
else
$Query->LeftJoin($JoinTable, $JoinAlias, $this->Alias, $RefField->Name, wclEqual, col_ID);
$this->Joins[$i]->BuildDBQuery($Query, $Fields, Stick($Path, $RefField->Name), $OnlyCalcFields);
}
}
/**
* @return string
* @param string $Script
* @param RefBookQueryFilterItem[] $Filter
* @desc Обработка формулы
*/
private function ParseScript($Script, $Filter = null){
$Script = str_replace("@SELF", $this->Alias, $Script);
if(is_array($Filter)){
for($i = 0; $i < sizeof($Filter); $i++){
$Script = str_replace("@".$Filter[$i]->Name, $Filter[$i]->Value, $Script);
}
}
return $Script;
}
/**
* @server
* @return integer
* @param string $Name
* @desc Определение индекса колонки списка указанного поля
*/
function GetColIndex($Name){
// Ищем указанную колонку в своем списке
for($i = 0; $i < sizeof($this->Fields); $i++){
if(strtolower($Name) == strtolower($this->Fields[$i]->Name)){
return intval($this->Fields[$i]->ColIndex);
}
}
// Если пришли сюда, значит колонку не нашли.
// Будем искать в связанных таблицах
for($i = 0; $i < sizeof($this->Joins); $i++){
$idx = $this->Joins[$i]->GetColIndex($Name);
if($idx > -1)
return $idx;
}
return -1;
}
}
/**
@desc Путь к элементу обозревателя справочника
@link DesignerRefBookBrowserElement
*/
class RefBookQueryPath extends Object {
public $Tables;
public $Fields;
/**
* @server
* @return void
* @desc Конструктор
*/
function __construct(){
parent::__construct();
$this->Tables = array();
$this->Fields = array();
}
/**
* @return integer
* @param integer $Field
* @desc Определение типа свойства класса
*/
static function GetType($Field){
switch($Field){
case ID_TABLES: return FT_ARRAY | FT_STRING;
case ID_FIELDS: return FT_ARRAY | FT_STRING;
default: return parent::GetType($Field);
}
}
}
?>