PageRenderTime 29ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/sale/lib/location/search/finder.php

https://gitlab.com/alexprowars/bitrix
PHP | 826 lines | 616 code | 159 blank | 51 comment | 127 complexity | 085235382c2787cc566886fd86b48dba MD5 | raw file
  1. <?php
  2. /**
  3. * Bitrix Framework
  4. * @package Bitrix\Sale\Location
  5. * @subpackage sale
  6. * @copyright 2001-2014 Bitrix
  7. */
  8. namespace Bitrix\Sale\Location\Search;
  9. use Bitrix\Main;
  10. use Bitrix\Main\DB;
  11. use Bitrix\Main\Localization\Loc;
  12. use Bitrix\Main\Config\Option;
  13. use Bitrix\Sale\Location;
  14. use Bitrix\Sale\Location\Util\Assert;
  15. Loc::loadMessages(__FILE__);
  16. class Finder
  17. {
  18. const SALE_LOCATION_INDEXED_TYPES_OPT = 'sale.location.indexed_types';
  19. const SALE_LOCATION_INDEXED_LANGUAGES_OPT = 'sale.location.indexed_langs';
  20. const SALE_LOCATION_INDEX_VALID_OPT = 'sale.location.index_valid';
  21. protected static $allowedOperations = array(
  22. '=' => true
  23. );
  24. public static function checkIndexValid()
  25. {
  26. return Option::get('sale', self::SALE_LOCATION_INDEX_VALID_OPT, '', '') == 'Y';
  27. }
  28. public static function setIndexValid()
  29. {
  30. Option::set('sale', self::SALE_LOCATION_INDEX_VALID_OPT, 'Y', '');
  31. }
  32. public static function setIndexInvalid()
  33. {
  34. Option::set('sale', self::SALE_LOCATION_INDEX_VALID_OPT, 'N', '');
  35. }
  36. public static function getIndexedTypes()
  37. {
  38. $types = Option::get('sale', self::SALE_LOCATION_INDEXED_TYPES_OPT, '', '');
  39. $typesFromDb = static::getTypesFromDb();
  40. if($types == '') // means "all"
  41. return array_keys($typesFromDb);
  42. $types = explode(':', $types);
  43. $result = array();
  44. if(is_array($types))
  45. {
  46. foreach($types as $type)
  47. {
  48. $type = intval($type);
  49. if(isset($typesFromDb[$type]))
  50. $result[] = $type;
  51. }
  52. }
  53. return array_unique($result);
  54. }
  55. public static function setIndexedTypes($types = array())
  56. {
  57. $result = array();
  58. if(is_array($types) && !empty($types))
  59. {
  60. $typesFromDb = static::getTypesFromDb();
  61. foreach($types as $type)
  62. {
  63. $type = intval($type);
  64. if(isset($typesFromDb[$type]))
  65. $result[] = $type;
  66. }
  67. $result = array_unique($result);
  68. }
  69. Option::set('sale', self::SALE_LOCATION_INDEXED_TYPES_OPT, implode(':', $result), '');
  70. }
  71. public static function getIndexedLanguages()
  72. {
  73. $langs = Option::get('sale', self::SALE_LOCATION_INDEXED_LANGUAGES_OPT, '', '');
  74. $langsFromDb = static::getLangsFromDb();
  75. if($langs == '')
  76. return array_keys($langsFromDb);
  77. $result = array();
  78. $langs = explode(':', $langs);
  79. if(is_array($langs))
  80. {
  81. foreach($langs as $lang)
  82. {
  83. if(isset($langsFromDb[$lang]))
  84. $result[] = $lang;
  85. }
  86. }
  87. return array_unique($result);
  88. }
  89. public static function setIndexedLanguages($langs = array())
  90. {
  91. if(is_array($langs) && !empty($langs))
  92. $langs = array_unique($langs);
  93. else
  94. $langs = array();
  95. $result = array();
  96. if(is_array($langs) && !empty($langs))
  97. {
  98. $langsFromDb = static::getLangsFromDb();
  99. foreach($langs as $lang)
  100. {
  101. if(isset($langsFromDb[$lang]))
  102. $result[] = $lang;
  103. }
  104. $result = array_unique($result);
  105. }
  106. Option::set('sale', self::SALE_LOCATION_INDEXED_LANGUAGES_OPT, implode(':', $result), '');
  107. }
  108. protected static function getLangsFromDb()
  109. {
  110. $langsFromDb = array();
  111. $res = \Bitrix\Main\Localization\LanguageTable::getList(array('select' => array('ID')));
  112. while($item = $res->fetch())
  113. $langsFromDb[$item['ID']] = true;
  114. return $langsFromDb;
  115. }
  116. protected static function getTypesFromDb()
  117. {
  118. $typesFromDb = array();
  119. $res = Location\TypeTable::getList(array('select' => array('ID')));
  120. while($item = $res->fetch())
  121. $typesFromDb[intval($item['ID'])] = true;
  122. return $typesFromDb;
  123. }
  124. /**
  125. *
  126. * $parameters is an ORM`s getList compatible array of parameters
  127. *
  128. *
  129. */
  130. public static function find($parameters, $behaviour = array('FALLBACK_TO_NOINDEX_ON_NOTFOUND' => true, 'USE_INDEX' => true, 'USE_ORM' => true))
  131. {
  132. /////////////////////////////////
  133. // parameter check and process
  134. Assert::expectArray($parameters, '$parameters');
  135. if(!is_array($behaviour))
  136. $behaviour = array();
  137. if(!isset($behaviour['FALLBACK_TO_NOINDEX_ON_NOTFOUND']))
  138. $behaviour['FALLBACK_TO_NOINDEX_ON_NOTFOUND'] = true;
  139. if(!isset($behaviour['USE_INDEX']))
  140. $behaviour['USE_INDEX'] = true;
  141. if(!isset($behaviour['USE_ORM']))
  142. $behaviour['USE_ORM'] = true;
  143. if(!isset($parameters['select']))
  144. $parameters['select'] = array('ID');
  145. Assert::expectArray($parameters['select'], '$parameters[select]');
  146. if(isset($parameters['filter']))
  147. {
  148. Assert::expectArray($parameters['filter'], '$parameters[filter]');
  149. // spikes, refactor later
  150. if(isset($parameters['filter']['PHRASE']) || isset($parameters['filter']['=PHRASE']))
  151. {
  152. $key = isset($parameters['filter']['PHRASE']) ? 'PHRASE' : '=PHRASE';
  153. $parameters['filter'][$key] = Assert::expectStringNotNull($parameters['filter'][$key], '$parameters[filter]['.$key.']');
  154. $parameters['filter'][$key] = str_replace('%', '', $parameters['filter'][$key]); // cannot pass '%' to like
  155. }
  156. if(isset($parameters['filter']['SITE_ID']) || isset($parameters['filter']['=SITE_ID']))
  157. {
  158. $key = isset($parameters['filter']['SITE_ID']) ? 'SITE_ID' : '=SITE_ID';
  159. $parameters['filter'][$key] = Assert::expectStringNotNull($parameters['filter'][$key], '$parameters[filter]['.$key.']'); // stronger here
  160. if(!Location\SiteLocationTable::checkLinkUsageAny($parameters['filter'][$key]))
  161. unset($parameters['filter'][$key]);
  162. }
  163. }
  164. if(isset($parameters['limit']))
  165. $parameters['limit'] = Assert::expectIntegerNonNegative($parameters['limit'], '$parameters[limit]');
  166. if(isset($parameters['offset']))
  167. $parameters['offset'] = Assert::expectIntegerNonNegative($parameters['offset'], '$parameters[offset]');
  168. /////////////////////////////////
  169. if(
  170. (isset($parameters['filter']['PHRASE']) || isset($parameters['filter']['SITE_ID']) || isset($parameters['filter']['=PHRASE']) || isset($parameters['filter']['=SITE_ID']))
  171. ||
  172. $behaviour['USE_ORM'] === false
  173. )
  174. {
  175. if(static::checkIndexValid() && $behaviour['USE_INDEX'])
  176. {
  177. $result = static::findUsingIndex($parameters);
  178. if(!$behaviour['FALLBACK_TO_NOINDEX_ON_NOTFOUND'])
  179. {
  180. return $result;
  181. }
  182. else
  183. {
  184. $temporalBuffer = array();
  185. while($item = $result->fetch())
  186. {
  187. $temporalBuffer[] = $item;
  188. }
  189. if(empty($temporalBuffer))
  190. {
  191. return static::findNoIndex($parameters);
  192. }
  193. else
  194. {
  195. return new DB\ArrayResult($temporalBuffer);
  196. }
  197. }
  198. }
  199. else
  200. {
  201. return static::findNoIndex($parameters);
  202. }
  203. }
  204. else
  205. {
  206. return Location\LocationTable::getList($parameters);
  207. }
  208. }
  209. protected static function parseFilter($filter)
  210. {
  211. $parsed = array();
  212. if(is_array($filter))
  213. {
  214. foreach($filter as $field => $value)
  215. {
  216. $found = array();
  217. preg_match("#^(=?)(.+)#", $field, $found);
  218. if($found[1] <> '')
  219. {
  220. $op = $found[1];
  221. }
  222. else
  223. {
  224. $op = '=';
  225. }
  226. if(!isset(static::$allowedOperations[$op]))
  227. throw new Main\ArgumentException('Unknown modifier in the filter');
  228. $fieldParsed = $found[2];
  229. $parsed[$fieldParsed] = array(
  230. 'OP' => $op <> ''? $op : '=',
  231. 'VALUE' => $value
  232. );
  233. }
  234. }
  235. return $parsed;
  236. }
  237. protected static function findUsingIndex($parameters)
  238. {
  239. $query = array();
  240. $dbConnection = Main\HttpApplication::getConnection();
  241. $dbHelper = Main\HttpApplication::getConnection()->getSqlHelper();
  242. $filter = static::parseFilter($parameters['filter']);
  243. $filterByPhrase = isset($filter['PHRASE']) && mb_strlen($filter['PHRASE']['VALUE']);
  244. if($filterByPhrase) // filter by phrase
  245. {
  246. $bounds = WordTable::getBoundsForPhrase($filter['PHRASE']['VALUE']);
  247. $firstBound = array_shift($bounds);
  248. $k = 0;
  249. foreach($bounds as $bound)
  250. {
  251. $query['JOIN'][] = " inner join ".ChainTable::getTableName()." A".$k." on A.LOCATION_ID = A".$k.".LOCATION_ID and (
  252. ".($bound['INF'] == $bound['SUP']
  253. ? " A".$k.".POSITION = '".$bound['INF']."'"
  254. : " A".$k.".POSITION >= '".$bound['INF']."' and A".$k.".POSITION <= '".$bound['SUP']."'"
  255. )."
  256. )";
  257. $k++;
  258. }
  259. $query['WHERE'][] = (
  260. $firstBound['INF'] == $firstBound['SUP']
  261. ? " A.POSITION = '".$firstBound['INF']."'"
  262. : " A.POSITION >= '".$firstBound['INF']."' and A.POSITION <= '".$firstBound['SUP']."'"
  263. );
  264. $mainTableJoinCondition = 'A.LOCATION_ID';
  265. }
  266. else
  267. {
  268. $mainTableJoinCondition = 'L.ID';
  269. }
  270. // site link search
  271. if(mb_strlen($filter['SITE_ID']['VALUE']) && SiteLinkTable::checkTableExists())
  272. {
  273. $query['JOIN'][] = "inner join ".SiteLinkTable::getTableName()." SL on SL.LOCATION_ID = ".$mainTableJoinCondition." and SL.SITE_ID = '".$dbHelper->forSql($filter['SITE_ID']['VALUE'])."'";
  274. }
  275. // process filter and select statements
  276. // at least, we support here basic field selection and filtration + NAME.NAME and NAME.LANGUAGE_ID
  277. $map = Location\LocationTable::getMap();
  278. $nameRequired = false;
  279. $locationRequired = false;
  280. if(is_array($parameters['select']))
  281. {
  282. foreach($parameters['select'] as $alias => $field)
  283. {
  284. if($field == 'NAME.NAME' || $field == 'NAME.LANGUAGE_ID')
  285. {
  286. $nameRequired = true;
  287. continue;
  288. }
  289. if(
  290. !isset($map[$field]) ||
  291. !in_array($map[$field]['data_type'], array('integer', 'string', 'float', 'boolean')) ||
  292. isset($map[$field]['expression'])
  293. )
  294. {
  295. unset($parameters['select'][$alias]);
  296. }
  297. $locationRequired = true;
  298. }
  299. }
  300. foreach($filter as $field => $params)
  301. {
  302. if($field == 'NAME.NAME' || $field == 'NAME.LANGUAGE_ID')
  303. {
  304. $nameRequired = true;
  305. continue;
  306. }
  307. if(
  308. !isset($map[$field]) ||
  309. !in_array($map[$field]['data_type'], array('integer', 'string', 'float', 'boolean')) ||
  310. isset($map[$field]['expression'])
  311. )
  312. {
  313. unset($filter[$field]);
  314. }
  315. $locationRequired = true;
  316. }
  317. // data join, only if extended select specified
  318. if($locationRequired && $filterByPhrase)
  319. $query['JOIN'][] = "inner join ".Location\LocationTable::getTableName()." L on A.LOCATION_ID = L.ID";
  320. if($nameRequired)
  321. $query['JOIN'][] = "inner join ".Location\Name\LocationTable::getTableName()." NAME on NAME.LOCATION_ID = ".$mainTableJoinCondition; // and N.LANGUAGE_ID = 'ru'
  322. // making select
  323. if(is_array($parameters['select']))
  324. {
  325. $select = array();
  326. foreach($parameters['select'] as $alias => $field)
  327. {
  328. if($field != 'NAME.NAME' && $field != 'NAME.LANGUAGE_ID')
  329. $field = 'L.'.$dbHelper->forSql($field);
  330. if((string) $alias === (string) intval($alias))
  331. $select[] = $field;
  332. else
  333. $select[] = $field.' as '.$dbHelper->forSql($alias);
  334. }
  335. $sqlSelect = implode(', ', $select);
  336. }
  337. else
  338. $sqlSelect = $mainTableJoinCondition.' as ID';
  339. // making filter
  340. foreach($filter as $field => $params)
  341. {
  342. if($field != 'NAME.NAME' && $field != 'NAME.LANGUAGE_ID')
  343. $field = 'L.'.$dbHelper->forSql($field);
  344. $values = $params['VALUE'];
  345. if(!is_array($values))
  346. $values = array($values);
  347. foreach($values as $value)
  348. $query['WHERE'][] = $field.' '.$params['OP']." '".$dbHelper->forSql($value)."'";
  349. }
  350. if($filterByPhrase)
  351. {
  352. $sql = "
  353. select ".($dbConnection->getType() != 'mysql' ? '' : 'distinct')/*fix this in more clever way later*/."
  354. ".$sqlSelect.(\Bitrix\Sale\Location\DB\Helper::needSelectFieldsInOrderByWhenDistinct() ? ', A.RELEVANCY' : '')."
  355. from ".ChainTable::getTableName()." A
  356. ".implode(' ', $query['JOIN'])."
  357. ".(count($query['WHERE']) ? 'where ' : '').implode(' and ', $query['WHERE'])."
  358. order by A.RELEVANCY asc
  359. ";
  360. }
  361. else
  362. {
  363. $sql = "
  364. select
  365. ".$sqlSelect."
  366. from ".Location\LocationTable::getTableName()." L
  367. ".implode(' ', $query['JOIN'])."
  368. ".(count($query['WHERE']) ? 'where ' : '').implode(' and ', $query['WHERE'])."
  369. ";
  370. }
  371. $offset = intval($parameters['offset']);
  372. $limit = intval($parameters['limit']);
  373. if($limit)
  374. $sql = $dbHelper->getTopSql($sql, $limit, $offset);
  375. $res = $dbConnection->query($sql);
  376. return $res;
  377. }
  378. /**
  379. *
  380. *
  381. * @param
  382. *
  383. * @return
  384. */
  385. protected static function findNoIndex($parameters)
  386. {
  387. $dbConnection = Main\HttpApplication::getConnection();
  388. $dbHelper = $dbConnection->getSqlHelper();
  389. // tables
  390. $locationTable = Location\LocationTable::getTableName();
  391. $locationNameTable = Location\Name\LocationTable::getTableName();
  392. $locationGroupTable = Location\GroupLocationTable::getTableName();
  393. $locationSiteTable = Location\SiteLocationTable::getTableName();
  394. $locationTypeTable = Location\TypeTable::getTableName();
  395. //////////////////////////////////
  396. // sql parameters prepare
  397. //////////////////////////////////
  398. $filter = static::parseFilter($parameters['filter']);
  399. if($filter['SITE_ID']['VALUE'] <> '')
  400. {
  401. $filterSite = $dbHelper->forSql(mb_substr($filter['SITE_ID']['VALUE'], 0, 2));
  402. $hasLocLinks = Location\SiteLocationTable::checkLinkUsage($filterSite, Location\SiteLocationTable::DB_LOCATION_FLAG);
  403. $hasGrpLinks = Location\SiteLocationTable::checkLinkUsage($filterSite, Location\SiteLocationTable::DB_GROUP_FLAG);
  404. $doFilterBySite = true;
  405. }
  406. if($filter['PHRASE']['VALUE'] <> '')
  407. {
  408. $doFilterByName = true;
  409. $filterName = ToUpper($dbHelper->forSql($filter['PHRASE']['VALUE']));
  410. }
  411. if(is_array($filter['ID']['VALUE']))
  412. {
  413. $doFilterById = true;
  414. if(count($filter['ID']['VALUE']) === 1)
  415. {
  416. reset($filter['ID']['VALUE']);
  417. $filterId = (int)current($filter['ID']['VALUE']);
  418. }
  419. else
  420. {
  421. $filterId = $filter['ID']['VALUE'];
  422. }
  423. }
  424. elseif(intval($filter['ID']['VALUE']))
  425. {
  426. $doFilterById = true;
  427. $filterId = intval($filter['ID']['VALUE']);
  428. }
  429. if(intval($filter['CODE']['VALUE']))
  430. {
  431. $doFilterByCode = true;
  432. $filterCode = $dbHelper->forSql($filter['CODE']['VALUE']);
  433. }
  434. $doFilterByLang = true;
  435. if($filter['NAME.LANGUAGE_ID']['VALUE'] <> '')
  436. {
  437. $filterLang = $dbHelper->forSql(mb_substr($filter['NAME.LANGUAGE_ID']['VALUE'], 0, 2));
  438. }
  439. else
  440. {
  441. $filterLang = LANGUAGE_ID;
  442. }
  443. if (isset($filter['COUNTRY_ID']) && (int)$filter['COUNTRY_ID']['VALUE'] >= 0)
  444. {
  445. $doFilterByCountry = true;
  446. $filterCountryId = (int)$filter['COUNTRY_ID']['VALUE'];
  447. }
  448. if(isset($filter['PARENT_ID']) && intval($filter['PARENT_ID']['VALUE']) >= 0)
  449. {
  450. $doFilterByParent = true;
  451. $filterParentId = intval($filter['PARENT_ID']['VALUE']);
  452. }
  453. if(intval($filter['TYPE_ID']['VALUE']))
  454. {
  455. $doFilterByType = true;
  456. $filterTypeId = intval($filter['TYPE_ID']['VALUE']);
  457. }
  458. // filter select fields
  459. if(!is_array($parameters['select']))
  460. $parameters['select'] = array();
  461. $map = Location\LocationTable::getMap();
  462. $nameAlias = false;
  463. foreach($parameters['select'] as $alias => $field)
  464. {
  465. if($field == 'CHILD_CNT')
  466. $doCountChildren = true;
  467. if($field == 'NAME.NAME')
  468. $nameAlias = $alias;
  469. if(/*in_array($field, array('ID', 'CODE', 'SORT', 'LEFT_MARGIN', 'RIGHT_MARGIN')) || */
  470. !isset($map[$field]) ||
  471. !in_array($map[$field]['data_type'], array('integer', 'string', 'float', 'boolean')) ||
  472. isset($map[$field]['expression'])
  473. )
  474. {
  475. unset($parameters['select'][$alias]);
  476. }
  477. }
  478. //////////////////////////////////
  479. // sql query build
  480. //////////////////////////////////
  481. // mandatory fields to be selected anyway
  482. // alias => field
  483. $fields = array(
  484. 'L.ID' => 'L.ID',
  485. 'L.CODE' => 'L.CODE',
  486. 'L.SORT' => 'L.SORT',
  487. 'LT_SORT' => 'LT.DISPLAY_SORT'
  488. );
  489. if($nameAlias === false || !preg_match('#^[a-zA-Z0-9]+$#', $nameAlias))
  490. {
  491. $fields['NAME'] = 'LN.NAME';
  492. }
  493. else
  494. {
  495. $fields[$nameAlias] = 'LN.NAME';
  496. }
  497. $fields = array_merge($fields, array(
  498. 'L.LEFT_MARGIN' => 'L.LEFT_MARGIN',
  499. 'L.RIGHT_MARGIN' => 'L.RIGHT_MARGIN'
  500. ));
  501. $groupFields = $fields;
  502. // additional fields to select
  503. foreach($parameters['select'] as $alias => $fld)
  504. {
  505. $lFld = 'L.'.$fld;
  506. // check if field is already selected
  507. if((string) $alias === (string) intval($alias))
  508. {
  509. // already selected
  510. if(in_array($lFld, $fields))
  511. continue;
  512. $fields[$lFld] = $lFld;
  513. //$groupFields[$lFld] = $lFld;
  514. }
  515. else // alias is not a number
  516. {
  517. if(isset($fields[$alias]))
  518. continue;
  519. $fields[$alias] = $lFld;
  520. //$groupFields[$alias] = $lFld;
  521. }
  522. $groupFields[$lFld] = $lFld;
  523. }
  524. if($doCountChildren)
  525. $fields['CHILD_CNT'] = 'COUNT(LC.ID)';
  526. // make select sql
  527. $selectSql = array();
  528. foreach($fields as $alias => $fld)
  529. {
  530. if($fld == $alias)
  531. $selectSql[] = $fld;
  532. else
  533. $selectSql[] = $fld.' as '.$alias;
  534. }
  535. $selectSql = implode(', ', $selectSql);
  536. //$groupSql = implode(', ', array_keys($groupFields));
  537. $groupSql = implode(', ', $groupFields);
  538. $mainSql = "select {$selectSql}
  539. from {$locationTable} L
  540. inner join {$locationNameTable} LN on L.ID = LN.LOCATION_ID
  541. inner join {$locationTypeTable} LT on L.TYPE_ID = LT.ID ".
  542. ($doCountChildren ? "
  543. left join {$locationTable} LC on L.ID = LC.PARENT_ID
  544. " : "")."
  545. %SITE_FILTER_CONDITION%
  546. where
  547. %MAIN_FILTER_CONDITION%
  548. %GROUP_BY%
  549. ";
  550. $where = array();
  551. if($doFilterByLang)
  552. $where[] = "LN.LANGUAGE_ID = '".$filterLang."'";
  553. if ($doFilterByCountry)
  554. {
  555. $where[] = "L.COUNTRY_ID = " . (int)$filterCountryId . " ";
  556. }
  557. if($doFilterByParent)
  558. $where[] = "L.PARENT_ID = '".$filterParentId."'";
  559. if($doFilterById)
  560. {
  561. if(is_array($filterId))
  562. {
  563. foreach($filterId as $idx => $id)
  564. {
  565. $filterId[$idx] = (int)$id;
  566. }
  567. $where[] = "L.ID IN (".implode(',', $filterId).")";
  568. }
  569. else
  570. {
  571. $where[] = "L.ID = ".$filterId;
  572. }
  573. }
  574. if($doFilterByCode)
  575. $where[] = "L.CODE = '".$filterCode."'";
  576. if($doFilterByType)
  577. $where[] = "L.TYPE_ID = '".$filterTypeId."'";
  578. if($doFilterByName)
  579. $where[] = "LN.NAME_UPPER like '".$filterName."%'";
  580. $mainSql = str_replace('%MAIN_FILTER_CONDITION%', implode(' and ', $where), $mainSql);
  581. $needDistinct = false;
  582. $unionized = false;
  583. $artificialNav = false;
  584. if(!$doFilterBySite)
  585. {
  586. $sql = str_replace('%SITE_FILTER_CONDITION%', '', $mainSql);
  587. }
  588. else
  589. {
  590. $sql = array();
  591. if($hasLocLinks)
  592. {
  593. $sql[] = str_replace('%SITE_FILTER_CONDITION%', "
  594. inner join {$locationTable} L2 on L2.LEFT_MARGIN <= L.LEFT_MARGIN and L2.RIGHT_MARGIN >= L.RIGHT_MARGIN
  595. inner join {$locationSiteTable} LS2 on L2.ID = LS2.LOCATION_ID and LS2.LOCATION_TYPE = 'L' and LS2.SITE_ID = '{$filterSite}'
  596. ", $mainSql);
  597. }
  598. if($hasGrpLinks)
  599. {
  600. $sql[] = str_replace('%SITE_FILTER_CONDITION%', "
  601. inner join {$locationTable} L2 on L2.LEFT_MARGIN <= L.LEFT_MARGIN and L2.RIGHT_MARGIN >= L.RIGHT_MARGIN
  602. inner join {$locationGroupTable} LG on LG.LOCATION_ID = L2.ID
  603. inner join {$locationSiteTable} LS2 on LG.LOCATION_GROUP_ID = LS2.LOCATION_ID and LS2.LOCATION_TYPE = 'G' and LS2.SITE_ID = '{$filterSite}'
  604. ", $mainSql);
  605. $useDistinct = true;
  606. }
  607. $cnt = count($sql);
  608. if($cnt == 1)
  609. {
  610. $needDistinct = true;
  611. }
  612. else
  613. {
  614. // UNION removes duplicates, so distinct is required only when no union here
  615. $unionized = true;
  616. }
  617. $sql = ($cnt > 1 ? '(' : '').implode(') union (', $sql).($cnt > 1 ? ')' : '');
  618. }
  619. // set groupping if needed
  620. $sql = str_replace('%GROUP_BY%', $needDistinct || $doCountChildren ? "group by {$groupSql}" : '', $sql);
  621. if(!is_array($parameters['order']))
  622. {
  623. $sql .= " order by 3, 4 asc, 5";
  624. }
  625. else
  626. {
  627. // currenly spike
  628. if(isset($parameters['order']['NAME.NAME']))
  629. $sql .= " order by 5 ".($parameters['order']['NAME.NAME'] == 'asc' ? 'asc' : 'desc');
  630. }
  631. $offset = intval($parameters['offset']);
  632. $limit = intval($parameters['limit']);
  633. if($limit)
  634. {
  635. if($dbConnection->getType() == 'mssql')
  636. {
  637. // due to huge amount of limitations of windowed functions in transact, using artificial nav here
  638. // (does not support UNION and integer indices in ORDER BY)
  639. $artificialNav = true;
  640. }
  641. else
  642. {
  643. $sql = $dbHelper->getTopSql($sql, $limit, $offset);
  644. }
  645. }
  646. $res = $dbConnection->query($sql);
  647. if($artificialNav)
  648. {
  649. $result = array();
  650. $i = -1;
  651. while($item = $res->fetch())
  652. {
  653. $i++;
  654. if($i < $offset)
  655. continue;
  656. if($i >= $offset + $limit)
  657. break;
  658. $result[] = $item;
  659. }
  660. return new DB\ArrayResult($result);
  661. }
  662. else
  663. {
  664. return $res;
  665. }
  666. }
  667. }