- Модуль: sale
- Путь к файлу: ~/bitrix/modules/sale/lib/location/search/finder.php
- Класс: BitrixSaleLocationSearchFinder
- Вызов: Finder::findNoIndex
static function findNoIndex($parameters)
{
$dbConnection = MainHttpApplication::getConnection();
$dbHelper = $dbConnection->getSqlHelper();
// tables
$locationTable = LocationLocationTable::getTableName();
$locationNameTable = LocationNameLocationTable::getTableName();
$locationGroupTable = LocationGroupLocationTable::getTableName();
$locationSiteTable = LocationSiteLocationTable::getTableName();
$locationTypeTable = LocationTypeTable::getTableName();
//////////////////////////////////
// sql parameters prepare
//////////////////////////////////
$filter = static::parseFilter($parameters['filter']);
$doFilterBySite = false;
$hasLocLinks = false;
$hasGrpLinks = false;
if (($filter['SITE_ID']['VALUE'] ?? '') !== '')
{
$filterSite = $dbHelper->forSql(mb_substr($filter['SITE_ID']['VALUE'], 0, 2));
$hasLocLinks = LocationSiteLocationTable::checkLinkUsage($filterSite, LocationSiteLocationTable::DB_LOCATION_FLAG);
$hasGrpLinks = LocationSiteLocationTable::checkLinkUsage($filterSite, LocationSiteLocationTable::DB_GROUP_FLAG);
$doFilterBySite = true;
}
$doFilterByName = false;
$filterName = '';
$phrase = (string)($filter['PHRASE']['VALUE'] ?? '');
if ($phrase !== '')
{
$doFilterByName = true;
$filterName = $dbHelper->forSql(mb_strtoupper($phrase));
}
$doFilterById = false;
$filterId = null;
if (isset($filter['ID']))
{
if (is_array($filter['ID']['VALUE']))
{
$doFilterById = true;
if (count($filter['ID']['VALUE']) === 1)
{
reset($filter['ID']['VALUE']);
$filterId = (int)current($filter['ID']['VALUE']);
}
else
{
$filterId = $filter['ID']['VALUE'];
}
}
elseif ((int)$filter['ID']['VALUE'])
{
$doFilterById = true;
$filterId = (int)$filter['ID']['VALUE'];
}
}
$doFilterByCode = false;
$filterCode = '';
$codeValue = (int)($filter['CODE']['VALUE'] ?? 0);
if ($codeValue)
{
$doFilterByCode = true;
$filterCode = $dbHelper->forSql((string)$codeValue);
}
unset($codeValue);
if (($filter['NAME.LANGUAGE_ID']['VALUE'] ?? '') !== '')
{
$filterLang = $dbHelper->forSql(mb_substr((string)$filter['NAME.LANGUAGE_ID']['VALUE'], 0, 2));
}
else
{
$filterLang = LANGUAGE_ID;
}
$doFilterByCountry = false;
$filterCountryId = 0;
if (isset($filter['COUNTRY_ID']) && (int)$filter['COUNTRY_ID']['VALUE'] >= 0)
{
$doFilterByCountry = true;
$filterCountryId = (int)$filter['COUNTRY_ID']['VALUE'];
}
$doFilterByParent = false;
$filterParentId = 0;
if (isset($filter['PARENT_ID']) && (int)$filter['PARENT_ID']['VALUE'] >= 0)
{
$doFilterByParent = true;
$filterParentId = (int)$filter['PARENT_ID']['VALUE'];
}
$doFilterByType = false;
$filterTypeId = 0;
if ((int)($filter['TYPE_ID']['VALUE'] ?? 0))
{
$doFilterByType = true;
$filterTypeId = (int)$filter['TYPE_ID']['VALUE'];
}
// filter select fields
$parameters['select'] ??= [];
if(!is_array($parameters['select']))
{
$parameters['select'] = [];
}
$doCountChildren = false;
$map = LocationLocationTable::getMap();
$nameAlias = false;
$allowTypes = [
'integer' => true,
'string' => true,
'float' => true,
'boolean' => true,
];
foreach($parameters['select'] as $alias => $field)
{
if ($field === 'CHILD_CNT')
{
$doCountChildren = true;
}
if ($field === 'NAME.NAME')
{
$nameAlias = $alias;
}
$badField = false;
if (!isset($map[$field]))
{
$badField = true;
}
elseif (
!isset($allowTypes[$map[$field]['data_type']])
|| isset($map[$field]['expression'])
)
{
$badField = true;
}
if ($badField)
{
unset($parameters['select'][$alias]);
}
}
//////////////////////////////////
// sql query build
//////////////////////////////////
// mandatory fields to be selected anyway
// alias => field
$fields = array(
'L.ID' => 'L.ID',
'L.CODE' => 'L.CODE',
'L.SORT' => 'L.SORT',
'LT_SORT' => 'LT.DISPLAY_SORT'
);
if($nameAlias === false || !preg_match('#^[a-zA-Z0-9]+$#', $nameAlias))
{
$fields['NAME'] = 'LN.NAME';
}
else
{
$fields[$nameAlias] = 'LN.NAME';
}
$fields = array_merge($fields, array(
'L.LEFT_MARGIN' => 'L.LEFT_MARGIN',
'L.RIGHT_MARGIN' => 'L.RIGHT_MARGIN'
));
$groupFields = $fields;
// additional fields to select
foreach($parameters['select'] as $alias => $fld)
{
$lFld = 'L.'.$fld;
// check if field is already selected
if((string) $alias === (string) intval($alias))
{
// already selected
if(in_array($lFld, $fields))
continue;
$fields[$lFld] = $lFld;
//$groupFields[$lFld] = $lFld;
}
else // alias is not a number
{
if(isset($fields[$alias]))
continue;
$fields[$alias] = $lFld;
//$groupFields[$alias] = $lFld;
}
$groupFields[$lFld] = $lFld;
}
if ($doCountChildren)
{
$fields['CHILD_CNT'] = 'COUNT(LC.ID)';
}
// make select sql
$selectSql = [];
foreach ($fields as $alias => $fld)
{
if ($fld === $alias)
$selectSql[] = $fld;
else
$selectSql[] = $fld.' as '.$alias;
}
$selectSql = implode(', ', $selectSql);
//$groupSql = implode(', ', array_keys($groupFields));
$groupSql = implode(', ', $groupFields);
$mainSql = "select {$selectSql}
from {$locationTable} L
inner join {$locationNameTable} LN on L.ID = LN.LOCATION_ID
inner join {$locationTypeTable} LT on L.TYPE_ID = LT.ID ".
($doCountChildren ? "
left join {$locationTable} LC on L.ID = LC.PARENT_ID
" : "")."
%SITE_FILTER_CONDITION%
where
%MAIN_FILTER_CONDITION%
%GROUP_BY%
";
$where = array();
$where[] = "LN.LANGUAGE_ID = '" . $filterLang . "'";
if ($doFilterByCountry)
{
$where[] = "L.COUNTRY_ID = " . $filterCountryId . " ";
}
if ($doFilterByParent)
{
$where[] = "L.PARENT_ID = " . $filterParentId . " ";
}
if ($doFilterById)
{
if(is_array($filterId))
{
foreach($filterId as $idx => $id)
{
$filterId[$idx] = (int)$id;
}
$where[] = "L.ID IN (".implode(',', $filterId).")";
}
else
{
$where[] = "L.ID = ".$filterId;
}
}
if($doFilterByCode)
{
$where[] = "L.CODE = '".$filterCode."'";
}
if($doFilterByType)
{
$where[] = "L.TYPE_ID = '" . $filterTypeId . "'";
}
if($doFilterByName)
{
$where[] = "LN.NAME_UPPER like '" . $filterName . "%'";
}
$mainSql = str_replace('%MAIN_FILTER_CONDITION%', implode(' and ', $where), $mainSql);
$needDistinct = false;
$unionized = false;
$artificialNav = false;
if(!$doFilterBySite)
{
$sql = str_replace('%SITE_FILTER_CONDITION%', '', $mainSql);
}
else
{
$sql = array();
if($hasLocLinks)
{
$sql[] = str_replace('%SITE_FILTER_CONDITION%', "
inner join {$locationTable} L2 on L2.LEFT_MARGIN <= L.LEFT_MARGIN and L2.RIGHT_MARGIN >= L.RIGHT_MARGIN
inner join {$locationSiteTable} LS2 on L2.ID = LS2.LOCATION_ID and LS2.LOCATION_TYPE = 'L' and LS2.SITE_ID = '{$filterSite}'
", $mainSql);
}
if($hasGrpLinks)
{
$sql[] = str_replace('%SITE_FILTER_CONDITION%', "
inner join {$locationTable} L2 on L2.LEFT_MARGIN <= L.LEFT_MARGIN and L2.RIGHT_MARGIN >= L.RIGHT_MARGIN
inner join {$locationGroupTable} LG on LG.LOCATION_ID = L2.ID
inner join {$locationSiteTable} LS2 on LG.LOCATION_GROUP_ID = LS2.LOCATION_ID and LS2.LOCATION_TYPE = 'G' and LS2.SITE_ID = '{$filterSite}'
", $mainSql);
$useDistinct = true;
}
$cnt = count($sql);
if($cnt == 1)
{
$needDistinct = true;
}
else
{
// UNION removes duplicates, so distinct is required only when no union here
$unionized = true;
}
$sql = ($cnt > 1 ? '(' : '').implode(') union (', $sql).($cnt > 1 ? ')' : '');
}
// set groupping if needed
$sql = str_replace('%GROUP_BY%', $needDistinct || $doCountChildren ? "group by {$groupSql}" : '', $sql);
$parameters['order'] ??= null;
if (!is_array($parameters['order']))
{
$sql .= " order by 3, 4 asc, 5";
}
else
{
// currenly spike
if (isset($parameters['order']['NAME.NAME']))
{
$sql .= " order by 5 " . ($parameters['order']['NAME.NAME'] == 'asc' ? 'asc' : 'desc');
}
}
$offset = (int)($parameters['offset'] ?? 0);
$limit = (int)($parameters['limit'] ?? 0);
if ($limit)
{
if ($dbConnection->getType() == 'mssql')
{
// due to huge amount of limitations of windowed functions in transact, using artificial nav here
// (does not support UNION and integer indices in ORDER BY)
$artificialNav = true;
}
else
{
$sql = $dbHelper->getTopSql($sql, $limit, $offset);
}
}
$res = $dbConnection->query($sql);
if ($artificialNav)
{
$result = array();
$i = -1;
while($item = $res->fetch())
{
$i++;
if($i < $offset)
continue;
if($i >= $offset + $limit)
break;
$result[] = $item;
}
return new DBArrayResult($result);
}
else
{
return $res;
}
}