BxDolSearch.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) BoonEx Pty Limited - http://www.boonex.com/
  4. * CC-BY License - http://creativecommons.org/licenses/by/3.0/
  5. *
  6. * @defgroup DolphinCore Dolphin Core
  7. * @{
  8. */
  9. define('BX_DOL_SEARCH_RESULTS_PER_PAGE_DEFAULT', 10);
  10. define('BX_DOL_SEARCH_KEYWORD_PAGE', 'site-search-page');
  11. /**
  12. * Search class for different content.
  13. *
  14. * Allows present content in main search area for all modules.
  15. *
  16. * To add taking part content from your module to search area you need add a record to `sys_objects_search` table:
  17. * @code
  18. * ID - autoincremented id for internal usage
  19. * ObjectName - your unique module name, with vendor prefix, lowercase and spaces are underscored
  20. * Title - language key for module's choicer in global search form
  21. * ClassName - class name which is responsible for search process ad showing result for found results
  22. * ClassPath - file where your ClassName is stored (if class is stored in template folder then you can use key {tmpl})
  23. * @endcode
  24. *
  25. * For using this class you should have unit of BxDolSearchResult class (see description below) with method processing which is responsible for processing of search request.
  26. *
  27. * Example of usage can be seen in the default Dolphin's modules (photos, sounds, events, sites, etc).
  28. *
  29. * Memberships/ACL:
  30. * Doesn't depend on user's membership.
  31. *
  32. * Alerts:
  33. * no alerts available
  34. *
  35. */
  36. class BxDolSearch extends BxDol
  37. {
  38. protected $aClasses = array(); ///< array of all search classes
  39. protected $aChoice = array(); ///< array of current search classes which were choosen in search area
  40. protected $_bLiveSearch = false;
  41. /**
  42. * Constructor
  43. * @param array $aChoice - array of choosen classes (will take a part only existing in `sys_objects_search` table)
  44. */
  45. public function __construct ($aChoice = '')
  46. {
  47. parent::__construct();
  48. $this->aClasses = BxDolDb::getInstance()->fromCache('sys_objects_search', 'getAllWithKey',
  49. 'SELECT `ID` as `id`,
  50. `Title` as `title`,
  51. `ClassName` as `class`,
  52. `ClassPath` as `file`,
  53. `ObjectName`
  54. FROM `sys_objects_search`', 'ObjectName'
  55. );
  56. if (is_array($aChoice) && !empty($aChoice)) {
  57. foreach ($aChoice as $sValue) {
  58. if (isset($this->aClasses[$sValue]))
  59. $this->aChoice[$sValue] = $this->aClasses[$sValue];
  60. }
  61. } else {
  62. $this->aChoice = $this->aClasses;
  63. }
  64. }
  65. /**
  66. * create units for all classes and calling their processing methods
  67. */
  68. public function response ()
  69. {
  70. bx_import('BxDolTemplate');
  71. $sCode = '';
  72. foreach ($this->aChoice as $sKey => $aValue) {
  73. if (!class_exists($aValue['class'])) {
  74. $sClassPath = str_replace('{tmpl}', BxDolTemplate::getInstance()->getCode(), $aValue['file']);
  75. require_once(BX_DIRECTORY_PATH_ROOT . $sClassPath);
  76. }
  77. $oEx = new $aValue['class']();
  78. $oEx->setId($aValue['id']);
  79. $oEx->setLiveSearch($this->_bLiveSearch);
  80. $sCode .= $oEx->processing();
  81. }
  82. return $sCode;
  83. }
  84. public function getEmptyResult ()
  85. {
  86. $sKey = _t('_Empty');
  87. return DesignBoxContent($sKey, MsgBox($sKey), 1);
  88. }
  89. protected function getKeyTitlesPairs ()
  90. {
  91. $a = array();
  92. foreach ($this->aChoice as $sKey => $r)
  93. $a[$sKey] = _t($r['title']);
  94. return $a;
  95. }
  96. public function setLiveSearch($bLiveSearch)
  97. {
  98. $this->_bLiveSearch = $bLiveSearch;
  99. }
  100. }
  101. /*
  102. * Search class for processing search requests and displaying search results.
  103. *
  104. * Allows present content from modules on search params or internal (via fields of class) conditions.
  105. *
  106. * Example of usage (you can see example in any of default Dolhpin's modules):
  107. *
  108. * 1. Extends your own search class from this one (or from BxBaseSearchResult or BxBaseSearchResultSharedMedia classes)
  109. * 2. Set necessary fields of class (using as example BxFilesSearch):
  110. *
  111. * @code
  112. *
  113. * // main field of search class
  114. * $this->aCurrent = array(
  115. *
  116. * // name of module
  117. * 'name' => 'bx_files',
  118. *
  119. * // language key with name of module
  120. * 'title' => '_bx_files',
  121. *
  122. * // main content table
  123. * 'table' => 'bx_files_main',
  124. *
  125. * // array of all fields which can be choosen for select in result query
  126. * 'ownFields' => array('ID', 'Title', 'Uri', 'Desc', 'Date', 'Size', 'Ext', 'Views', 'Rate', 'RateCount', 'Type'),
  127. *
  128. * // array of fields which take a part in search by keyword (global search and related words only - in other cases leave it blank)
  129. * 'searchFields' => array('Title', 'Tags', 'Desc', 'Categories'),
  130. *
  131. * // array of join tables
  132. * // 'type' - type of join
  133. * // 'table' - join table
  134. * // 'mainField' - field from main table for 'on' condition
  135. * // 'onField' - field from joining table for 'on' condition
  136. * // 'joinFields' - array of fields from joining table
  137. *
  138. * 'join' => array(
  139. * 'profile' => array(
  140. * 'type' => 'left',
  141. * 'table' => 'Profiles',
  142. * 'mainField' => 'Owner',
  143. * 'onField' => 'ID',
  144. * 'joinFields' => array('NickName'),
  145. * ),
  146. * // ...
  147. * ),
  148. *
  149. * // array of search parameters
  150. * // 'value' - value of search parameter (can be number, string or array)
  151. * // 'field' - field which will take value of search, may be lefy as blank then in search query will be pasted to WHERE conditon via operator AGAINST
  152. * // 'operator' - operator between field and value (can be 'in', 'not in', '>', '<', 'against', 'like' and '=' by default),
  153. * // 'paramName' - GET param which will keep 'value' for pagianation
  154. *
  155. * 'restriction' => array(
  156. * 'activeStatus' => array('value'=>'approved', 'field'=>'Status', 'operator'=>'=', 'paramName' => 'status'),
  157. * 'albumType' => array('value'=>'', 'field'=>'Type', 'operator'=>'=', 'paramName'=>'albumType', 'table'=>'sys_albums'),
  158. * ),
  159. *
  160. * // array of pagination
  161. * // 'perPage' - units per page
  162. * // 'start' - show search results starting from this number
  163. * // 'num' - number of element to show on current page + 1
  164. *
  165. * 'paginate' => array('perPage' => 10, 'start' => 0, 'num' => 11),
  166. *
  167. * // sort mode - by default it was last (DESC by date)
  168. * 'sorting' => 'last',
  169. *
  170. * // mode of units presentation in search result, can be 'short' or 'full' view
  171. * 'view' => 'full',
  172. *
  173. * // field of id
  174. * 'ident' => 'ID',
  175. *
  176. * // rss feed array
  177. * 'rss' => array(
  178. * 'title' => '',
  179. * 'link' => '',
  180. * 'image' => '',
  181. * 'profile' => 0,
  182. * 'fields' => array (
  183. * 'Link' => '',
  184. * 'Title' => 'Title',
  185. * 'DateTimeUTS' => 'Date',
  186. * 'Desc' => 'Desc',
  187. * 'Photo' => '',
  188. * ),
  189. * ),
  190. * );
  191. *
  192. * // array of fields renamings
  193. * var $aPseud - filling in costructor by function _getPseud
  194. *
  195. * // unique identificator from `sys_objects_search` table
  196. * var $id;
  197. *
  198. * @endcode
  199. *
  200. * Memberships/ACL:
  201. * Doesn't depend on user's membership.
  202. *
  203. * Alerts:
  204. * no alerts available
  205. *
  206. */
  207. class BxDolSearchResult implements iBxDolReplaceable
  208. {
  209. public $aCurrent; ///< search results configuration
  210. protected $aPseud; ///< array of fields renamings
  211. protected $id; ///< unique identificator of block on mixed search results page
  212. protected $bDisplayEmptyMsg = false; ///< display empty message instead of nothing, when no results
  213. protected $sDisplayEmptyMsgKey = ''; ///< custom empty message language key, instead of default "empty" message
  214. protected $bProcessPrivateContent = true; ///< check each item for privacy, if view isn't allowed then display private view instead
  215. protected $bForceAjaxPaginate = false; ///< force ajax paginate
  216. protected $_bLiveSearch = false;
  217. protected $_aMarkers = array (); ///< markers to replace somewhere, usually title and browse url (defined in custom class)
  218. /**
  219. * constructor
  220. * filling identificator field
  221. */
  222. function __construct ()
  223. {
  224. if (isset($this->aPseud['id']))
  225. $this->aCurrent['ident'] = $this->aPseud['id'];
  226. }
  227. public function getId()
  228. {
  229. return $this->id;
  230. }
  231. public function setId($sId)
  232. {
  233. $this->id = $sId;
  234. }
  235. public function setAjaxPaginate($b = true)
  236. {
  237. $this->bForceAjaxPaginate = $b;
  238. }
  239. public function setLiveSearch($bLiveSearch)
  240. {
  241. $this->_bLiveSearch = $bLiveSearch;
  242. }
  243. /**
  244. * Display empty message if there is no content, custom empty message can be used.
  245. * @param $b - boolan value to enable or disable 'empty' message
  246. * @param $sLangKey [optional] - custom 'empty' message
  247. */
  248. public function setDisplayEmptyMsg($b, $sLangKey = '')
  249. {
  250. $this->bDisplayEmptyMsg = $b;
  251. if ($sLangKey)
  252. $this->sDisplayEmptyMsgKey = $sLangKey;
  253. }
  254. /**
  255. * Perform privacy checking for every unit
  256. * @param $b - boolan value to enable or disable privacy checking
  257. */
  258. public function setProcessPrivateContent ($b)
  259. {
  260. $this->bProcessPrivateContent = $b;
  261. }
  262. /**
  263. * Get html box of search results (usually used in grlobal search)
  264. * @return html code
  265. */
  266. function processing ()
  267. {
  268. $sCode = $this->displayResultBlock();
  269. if ($this->aCurrent['paginate']['num'] > 0) {
  270. $sPaginate = $this->showPagination();
  271. $sCode = $this->displaySearchBox($sCode, $sPaginate);
  272. }
  273. else {
  274. $sCode = $this->bDisplayEmptyMsg ? $this->displaySearchBox(MsgBox(_t($this->sDisplayEmptyMsgKey ? $this->sDisplayEmptyMsgKey : '_Empty'))) : '';
  275. }
  276. return $sCode;
  277. }
  278. /**
  279. * Get html output of search result
  280. * @return html code
  281. */
  282. function displayResultBlock ()
  283. {
  284. $aData = $this->getSearchData();
  285. if (count($aData) > 0) {
  286. $sCode .= $this->addCustomParts();
  287. foreach ($aData as $iKey => $aValue) {
  288. $sCode .= $this->displaySearchUnit($aValue);
  289. }
  290. }
  291. return $sCode;
  292. }
  293. /**
  294. * Add different code before html output (usually redeclared)
  295. * no return result
  296. */
  297. function addCustomParts ()
  298. {
  299. }
  300. /**
  301. * Get XML string for rss output
  302. */
  303. function rss ()
  304. {
  305. if (!isset($this->aCurrent['rss']['fields']) || !isset($this->aCurrent['rss']['link']))
  306. return '';
  307. $aData = $this->getSearchData();
  308. $f = &$this->aCurrent['rss']['fields'];
  309. if ($aData) {
  310. foreach ($aData as $k => $a)
  311. $aData[$k][$f['Link']] = $this->getRssUnitLink ($a);
  312. }
  313. bx_import('BxDolRssFactory');
  314. $oRss = new BxDolRssFactory ();
  315. return $oRss->GenRssByCustomData(
  316. $aData,
  317. isset($this->aCurrent['rss']['title']) && $this->aCurrent['rss']['title'] ? $this->aCurrent['rss']['title'] : $this->aCurrent['title'],
  318. $this->aCurrent['rss']['link'],
  319. $this->aCurrent['rss']['fields'],
  320. isset($this->aCurrent['rss']['image']) ? $this->aCurrent['rss']['image'] : '',
  321. isset($this->aCurrent['rss']['profile']) ? $this->aCurrent['rss']['profile'] : 0
  322. );
  323. }
  324. /**
  325. * Output RSS XML with XML header
  326. */
  327. function outputRSS ()
  328. {
  329. header('Content-Type: text/xml; charset=UTF-8');
  330. echo $this->rss();
  331. }
  332. /**
  333. * Return rss unit link (redeclared)
  334. */
  335. function getRssUnitLink (&$a)
  336. {
  337. // override this functions to return permalink to rss unit
  338. }
  339. /**
  340. * Naming fields in query's body
  341. * @param string $sFieldName name of field
  342. * @param string $sTableName name of field's table
  343. * $param string $sOperator of field's calculation (like MAX)
  344. * @param boolean $bRenameMode indicator for renaming and unsetting fields from field of class $this->aPseud
  345. * return $sqlUnit sql code and unsetting elements from aPseud field
  346. */
  347. function setFieldUnit ($sFieldName, $sTableName, $sOperator = '', $bRenameMode = true)
  348. {
  349. if (strlen($sOperator) > 0) {
  350. $sqlUnit = "$sOperator(`$sTableName`.`$sFieldName`)";
  351. }
  352. else
  353. $sqlUnit = "`$sTableName`.`$sFieldName`";
  354. if (isset($this->aPseud) && $bRenameMode !== false) {
  355. $sKey = array_search($sFieldName, $this->aPseud);
  356. if ($sKey !== false) {
  357. $sqlUnit .= " as `$sKey`";
  358. unset($this->aPseud[$sKey]);
  359. }
  360. }
  361. return $sqlUnit . ', ';
  362. }
  363. /**
  364. * Get html code of of every search unit
  365. * @param array $aData array of every search unit
  366. * return html code
  367. */
  368. function displaySearchUnit ($aData)
  369. {
  370. }
  371. /**
  372. * Get html code of search box with search results
  373. * @param string $sCode html code of search results
  374. * $param $sPaginate html code of paginate
  375. * return html code
  376. */
  377. function displaySearchBox ($sCode, $sPaginate = '')
  378. {
  379. }
  380. /**
  381. * Get html code of pagination
  382. */
  383. function showPagination ($bAdmin = false, $bChangePage = true, $bPageReload = true)
  384. {
  385. }
  386. /**
  387. * Get array of data with search results
  388. * return array with data
  389. */
  390. function getSearchData ()
  391. {
  392. $this->aPseud = $this->_getPseud();
  393. $this->setConditionParams();
  394. if ($this->aCurrent['paginate']['num'] > 0) {
  395. $aData = $this->getSearchDataByParams();
  396. return $aData;
  397. }
  398. return array();
  399. }
  400. /**
  401. * Get array with code for sql elements
  402. * @param boolean #bREnameMode indicator of renmaing fields
  403. * return array with joinFields, ownFields, groupBy and join elements
  404. */
  405. function getJoins ($bRenameMode = true)
  406. {
  407. $aSql = array();
  408. // joinFields & join
  409. if (isset($this->aCurrent['join']) && is_array($this->aCurrent['join'])) {
  410. $aSql = array('join'=>'', 'ownFields'=>'', 'joinFields'=>'', 'groupBy'=>'');
  411. foreach ($this->aCurrent['join'] as $sKey => $aValue) {
  412. $sAlias = isset($aValue['table_alias']) ? $aValue['table_alias'] : $aValue['table'];
  413. $sTableAlias = isset($aValue['table_alias']) ? " AS {$aValue['table_alias']} " : '';
  414. if (is_array($aValue['joinFields'])) {
  415. foreach ($aValue['joinFields'] as $sValue) {
  416. $aSql['joinFields'] .= $this->setFieldUnit($sValue, $sAlias, isset($aValue['operator']) ? $aValue['operator'] : null, $bRenameMode);
  417. }
  418. }
  419. // group by
  420. if (isset($aValue['groupTable']))
  421. $aSql['groupBy'] = "GROUP BY `{$aValue['groupTable']}`.`{$aValue['groupField']}`, ";
  422. $sOn = isset($aValue['mainTable']) ? $aValue['mainTable'] : $this->aCurrent['table'];
  423. $aSql['join'] .= " {$aValue['type']} JOIN `{$aValue['table']}` $sTableAlias ON `{$sAlias}`.`{$aValue['onField']}`=`$sOn`.`{$aValue['mainField']}`";
  424. $aSql['ownFields'] .= $this->setFieldUnit($aValue['mainField'], $sOn, '', $bRenameMode);
  425. }
  426. $aSql['joinFields'] = trim($aSql['joinFields'], ', ');
  427. $aSql['groupBy'] = trim($aSql['groupBy'], ', ');
  428. }
  429. return $aSql;
  430. }
  431. /**
  432. * Concat sql parts of query, run it and return result array
  433. * @param $aParams addon param
  434. * return $aData multivariate array
  435. */
  436. function getSearchDataByParams ($aParams = '')
  437. {
  438. $aSql = array('ownFields'=>'', 'joinFields'=>'', 'order'=>'');
  439. // searchFields
  440. foreach ($this->aCurrent['ownFields'] as $sValue)
  441. $aSql['ownFields'] .= $this->setFieldUnit($sValue, $this->aCurrent['table']);
  442. // joinFields & join
  443. $aJoins = $this->getJoins();
  444. if (!empty($aJoins)) {
  445. $aSql['ownFields'] .= $aJoins['ownFields'];
  446. $aSql['joinFields'] .= $aJoins['joinFields'];
  447. $aSql['join'] = $aJoins['join'];
  448. $aSql['groupBy'] = $aJoins['groupBy'];
  449. }
  450. else
  451. $aSql['ownFields'] = trim($aSql['ownFields'], ', ');
  452. // from
  453. $aSql['from'] = " FROM `{$this->aCurrent['table']}`";
  454. // where
  455. $aSql['where'] = $this->getRestriction();
  456. // limit
  457. $aSql['limit'] = $this->getLimit();
  458. // sorting
  459. $this->setSorting();
  460. $aSort = $this->getSorting($this->aCurrent['sorting']);
  461. foreach ($aSort as $sKey => $sValue)
  462. $aSql[$sKey] .= $sValue;
  463. // execution
  464. $sqlQuery = "SELECT " . $aSql['ownFields'];
  465. if (isset($aSql['joinFields']))
  466. $sqlQuery .= ' ' . $aSql['joinFields'];
  467. $sqlQuery .= ' ' . $aSql['from'];
  468. if (isset($aSql['join']))
  469. $sqlQuery .= ' ' . $aSql['join'];
  470. if (isset($aSql['where']))
  471. $sqlQuery .= ' ' . $aSql['where'];
  472. if (isset($aSql['groupBy']))
  473. $sqlQuery .= ' ' . $aSql['groupBy'];
  474. if (isset($aSql['order']))
  475. $sqlQuery .= ' ' . $aSql['order'];
  476. if (isset($aSql['limit']))
  477. $sqlQuery .= ' ' . $aSql['limit'];
  478. // echoDbg($sqlQuery);
  479. $aRes = BxDolDb::getInstance()->getAll($sqlQuery);
  480. return $aRes;
  481. }
  482. /**
  483. * Set class fields condition params and paginate array
  484. */
  485. function setConditionParams()
  486. {
  487. $aWhere = array();
  488. $aWhere[] = '1';
  489. $sKeyword = bx_process_input(bx_get('keyword'));
  490. if ($sKeyword !== false)
  491. $this->aCurrent['restriction']['keyword'] = array(
  492. 'value' => $sKeyword,
  493. 'field' => '',
  494. 'operator' => 'against'
  495. );
  496. if (isset($_GET['ownerName'])) {
  497. $sName = bx_process_input($_GET['ownerName']);
  498. $iUser = (int)BxDolProfileQuery::getInstance()->getIdByNickname($sName);
  499. BxDolMenu::getInstance()->setCurrentProfileID($iUser);
  500. }
  501. elseif (isset($_GET['userID']))
  502. $iUser = bx_process_input($_GET['userID'], BX_DATA_INT);
  503. if (!empty($iUser))
  504. $this->aCurrent['restriction']['owner']['value'] = $iUser;
  505. $this->setPaginate();
  506. $iNum = $this->getNum();
  507. if ($iNum > 0) {
  508. $this->aCurrent['paginate']['num'] = $iNum;
  509. }
  510. else {
  511. $this->aCurrent['paginate']['num'] = 0;
  512. }
  513. }
  514. /**
  515. * Check number of records on current page
  516. * return number of records on current page + 1
  517. */
  518. function getNum ()
  519. {
  520. $aJoins = $this->getJoins(false);
  521. $sqlQuery = "SELECT * FROM `{$this->aCurrent['table']}` " . (isset($aJoins['join']) ? $aJoins['join'] : '' ). $this->getRestriction() . (isset($aJoins['groupBy']) ? $aJoins['groupBy'] : '') . ' ' . $this->getLimit(true);
  522. return count(BxDolDb::getInstance()->getAll($sqlQuery));
  523. }
  524. /**
  525. * Check restriction params and make condition part of query
  526. * return $sqlWhere sql code of query for WHERE part
  527. */
  528. function getRestriction ()
  529. {
  530. $oDb = BxDolDb::getInstance();
  531. $sqlWhere = '';
  532. if (isset($this->aCurrent['restriction'])) {
  533. $aWhere[] = '1';
  534. foreach ($this->aCurrent['restriction'] as $sKey => $aValue) {
  535. $sqlCondition = '';
  536. if (isset($aValue['operator']) && isset($aValue['value']) && $aValue['value'] !== '' && $aValue['value'] !== false && $aValue['value'] !== null) {
  537. $sFieldTable = isset($aValue['table']) ? $aValue['table'] : $this->aCurrent['table'];
  538. $sqlCondition = "`{$sFieldTable}`.`{$aValue['field']}` ";
  539. switch ($aValue['operator']) {
  540. case 'against':
  541. $aCond = isset($aValue['field']) && strlen($aValue['field']) > 0 ? $aValue['field'] : $this->aCurrent['searchFields'];
  542. $sqlCondition = !empty($aCond) ? $this->getSearchFieldsCond($aCond, $aValue['value']) : "";
  543. break;
  544. case 'like':
  545. $sqlCondition .= "LIKE '%" . $oDb->escape($aValue['value']) . "%'";
  546. break;
  547. case 'in':
  548. case 'not in':
  549. $sValuesString = $this->getMultiValues($aValue['value']);
  550. $sqlCondition .= strtoupper($aValue['operator']) . '('.$sValuesString.')';
  551. break;
  552. default:
  553. $sqlCondition .= $aValue['operator'] . (isset($aValue['no_quote_value']) && $aValue['no_quote_value'] ? $aValue['value'] : "'" . $oDb->escape($aValue['value']) . "'");
  554. break;
  555. }
  556. }
  557. if (strlen($sqlCondition) > 0)
  558. $aWhere[] = $sqlCondition;
  559. }
  560. $sqlWhere .= "WHERE ". implode(' AND ', $aWhere);
  561. }
  562. return $sqlWhere;
  563. }
  564. /**
  565. * Get limit part of query
  566. * return $sqlFrom code for limit part pf query
  567. */
  568. function getLimit ($isAddPlusOne = false)
  569. {
  570. if (!isset($this->aCurrent['paginate']))
  571. return;
  572. $sqlFrom = (int)$this->aCurrent['paginate']['start'] > 0 ? (int)$this->aCurrent['paginate']['start'] : 0;
  573. $sqlTo = $this->aCurrent['paginate']['perPage'] + ($isAddPlusOne ? 1 : 0);
  574. if ($sqlTo > 0)
  575. return 'LIMIT ' . $sqlFrom . ', ' . $sqlTo;
  576. }
  577. /**
  578. * Set sorting field of class
  579. */
  580. function setSorting ()
  581. {
  582. $this->aCurrent['sorting'] = isset($_GET[$this->aCurrent['name'] . '_mode']) ? $_GET[$this->aCurrent['name'] . '_mode'] : $this->aCurrent['sorting'];
  583. }
  584. /**
  585. * Get sorting part of query according current sorting mode
  586. * @param string $sSortType sorting type
  587. * return array with sql elements order and ownFields
  588. */
  589. function getSorting ($sSortType = 'last')
  590. {
  591. $aOverride = $this->getAlterOrder();
  592. if (is_array($aOverride) && !empty($aOverride))
  593. return $aOverride;
  594. $aSql = array();
  595. switch ($sSortType) {
  596. case 'rand':
  597. $aSql['order'] = "ORDER BY RAND()";
  598. break;
  599. case 'score':
  600. if (is_array($this->aCurrent['restriction']['keyword'])) {
  601. $aSql['order'] = "ORDER BY `score` DESC";
  602. $aSql['ownFields'] .= $this->getSearchFieldsCond($this->aCurrent['searchFields'], $this->aCurrent['restriction']['keyword']['value'], 'score'). ', ';
  603. }
  604. break;
  605. case 'none':
  606. $aSql['order'] = "";
  607. break;
  608. default:
  609. $aSql['order'] = "ORDER BY `date` DESC";
  610. }
  611. return $aSql;
  612. }
  613. /**
  614. * Return own varaint for sorting (redeclare if necessary)
  615. * return array of sql elements
  616. */
  617. function getAlterOrder ()
  618. {
  619. return array();
  620. }
  621. /**
  622. * Set paginate fields of class according GET params 'start' and 'per_page'
  623. * forcePage is need for setting most important number of current page
  624. */
  625. function setPaginate ()
  626. {
  627. $this->aCurrent['paginate']['perPage'] = (isset($_GET['per_page']) && (int)$_GET['per_page'] != 0) ? (int)$_GET['per_page'] : $this->aCurrent['paginate']['perPage'];
  628. if (empty($this->aCurrent['paginate']['perPage']))
  629. $this->aCurrent['paginate']['perPage'] = BX_DOL_SEARCH_RESULTS_PER_PAGE_DEFAULT;
  630. $this->aCurrent['paginate']['start'] = isset($this->aCurrent['paginate']['forceStart'])
  631. ? (int)$this->aCurrent['paginate']['forceStart']
  632. : (empty($_GET['start']) ? 0 : (int)$_GET['start']);
  633. if ($this->aCurrent['paginate']['start'] < 0)
  634. $this->aCurrent['paginate']['start'] = 0;
  635. }
  636. /**
  637. * Get sql where condition for search fields
  638. * @param array of search fields
  639. * @param string $sKeyword keyword value for search
  640. * @param string sPseud for setting new name for generated set of fields in query
  641. * return sql code of WHERE part in query
  642. */
  643. function getSearchFieldsCond ($aFields, $sKeyword, $sPseud = '')
  644. {
  645. if (!$sKeyword)
  646. return;
  647. $sTable = empty($this->aCurrent['tableSearch']) ? $this->aCurrent['table'] : $this->aCurrent['tableSearch'];
  648. $oDb = BxDolDb::getInstance();
  649. $bLike = getParam('useLikeOperator');
  650. $sKeyword = $oDb->escape($sKeyword);
  651. $aSql = array(
  652. 'function' => 'MATCH',
  653. 'operator' => 'AGAINST',
  654. 'word' => $sKeyword
  655. );
  656. if ($bLike == 'on') {
  657. $aSql = array(
  658. 'function' => 'CONCAT', // TODO: get rid of CONCAT !!!
  659. 'operator' => 'LIKE',
  660. 'word' => '%' . preg_replace('/\s+/', '%', $sKeyword) . '%'
  661. );
  662. }
  663. $sqlWhere = " {$aSql['function']}(";
  664. if (is_array($aFields)) {
  665. foreach ($aFields as $sValue)
  666. $sqlWhere .= "`{$sTable}`.`$sValue`, ";
  667. } else {
  668. $sqlWhere .= "`{$sTable}`.`$aFields`";
  669. }
  670. $sqlWhere = trim($sqlWhere, ', ') . ") {$aSql['operator']} ('{$aSql['word']}')";
  671. if (strlen($sPseud) > 0)
  672. $sqlWhere .= " as `$sPseud`";
  673. return $sqlWhere;
  674. }
  675. /**
  676. * Get set from several values for 'in' and 'not in' operators
  677. * @param $aValues array of values
  678. * return sql code for field with operator IN (NOT IN)
  679. */
  680. function getMultiValues ($aValues)
  681. {
  682. $oDb = BxDolDb::getInstance();
  683. return $oDb->implode_escape($aValues);
  684. }
  685. /**
  686. * System method for filling aPseud array.
  687. * Fill field aPseud for current class (if you will use own getSearchUnit methods then not necessary to redeclare).
  688. */
  689. function _getPseud ()
  690. {
  691. }
  692. /**
  693. * Add replace markers. Markers are replaced in titles and browse urls
  694. * @param $a array of markers as key => value
  695. * @return true on success or false on error
  696. */
  697. public function addMarkers ($a)
  698. {
  699. if (empty($a) || !is_array($a))
  700. return false;
  701. $this->_aMarkers = array_merge ($this->_aMarkers, $a);
  702. return true;
  703. }
  704. /**
  705. * Replace provided markers in a string
  706. * @param $mixed string or array to replace markers in
  707. * @return string where all occured markers are replaced
  708. */
  709. protected function _replaceMarkers ($mixed)
  710. {
  711. return bx_replace_markers($mixed, $this->_aMarkers);
  712. }
  713. }
  714. /** @} */