[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * @package Joomla.Site 5 * @subpackage com_finder 6 * 7 * @copyright (C) 2011 Open Source Matters, Inc. <https://www.joomla.org> 8 * @license GNU General Public License version 2 or later; see LICENSE.txt 9 */ 10 11 namespace Joomla\Component\Finder\Site\Model; 12 13 use Joomla\CMS\Factory; 14 use Joomla\CMS\Language\Multilanguage; 15 use Joomla\CMS\MVC\Model\ListModel; 16 use Joomla\Component\Finder\Administrator\Indexer\Query; 17 use Joomla\String\StringHelper; 18 19 // phpcs:disable PSR1.Files.SideEffects 20 \defined('_JEXEC') or die; 21 // phpcs:enable PSR1.Files.SideEffects 22 23 /** 24 * Search model class for the Finder package. 25 * 26 * @since 2.5 27 */ 28 class SearchModel extends ListModel 29 { 30 /** 31 * Context string for the model type 32 * 33 * @var string 34 * @since 2.5 35 */ 36 protected $context = 'com_finder.search'; 37 38 /** 39 * The query object is an instance of Query which contains and 40 * models the entire search query including the text input; static and 41 * dynamic taxonomy filters; date filters; etc. 42 * 43 * @var Query 44 * @since 2.5 45 */ 46 protected $searchquery; 47 48 /** 49 * An array of all excluded terms ids. 50 * 51 * @var array 52 * @since 2.5 53 */ 54 protected $excludedTerms = array(); 55 56 /** 57 * An array of all included terms ids. 58 * 59 * @var array 60 * @since 2.5 61 */ 62 protected $includedTerms = array(); 63 64 /** 65 * An array of all required terms ids. 66 * 67 * @var array 68 * @since 2.5 69 */ 70 protected $requiredTerms = array(); 71 72 /** 73 * Method to get the results of the query. 74 * 75 * @return array An array of Result objects. 76 * 77 * @since 2.5 78 * @throws \Exception on database error. 79 */ 80 public function getItems() 81 { 82 $items = parent::getItems(); 83 84 // Check the data. 85 if (empty($items)) { 86 return null; 87 } 88 89 $results = array(); 90 91 // Convert the rows to result objects. 92 foreach ($items as $rk => $row) { 93 // Build the result object. 94 if (is_resource($row->object)) { 95 $result = unserialize(stream_get_contents($row->object)); 96 } else { 97 $result = unserialize($row->object); 98 } 99 100 $result->cleanURL = $result->route; 101 102 // Add the result back to the stack. 103 $results[] = $result; 104 } 105 106 // Return the results. 107 return $results; 108 } 109 110 /** 111 * Method to get the query object. 112 * 113 * @return Query A query object. 114 * 115 * @since 2.5 116 */ 117 public function getQuery() 118 { 119 // Return the query object. 120 return $this->searchquery; 121 } 122 123 /** 124 * Method to build a database query to load the list data. 125 * 126 * @return \Joomla\Database\DatabaseQuery A database query. 127 * 128 * @since 2.5 129 */ 130 protected function getListQuery() 131 { 132 // Create a new query object. 133 $db = $this->getDatabase(); 134 $query = $db->getQuery(true); 135 136 // Select the required fields from the table. 137 $query->select( 138 $this->getState( 139 'list.select', 140 'l.link_id, l.object' 141 ) 142 ); 143 144 $query->from('#__finder_links AS l'); 145 146 $user = Factory::getUser(); 147 $groups = $this->getState('user.groups', $user->getAuthorisedViewLevels()); 148 $query->whereIn($db->quoteName('l.access'), $groups) 149 ->where('l.state = 1') 150 ->where('l.published = 1'); 151 152 // Get the current date, minus seconds. 153 $nowDate = $db->quote(substr_replace(Factory::getDate()->toSql(), '00', -2)); 154 155 // Add the publish up and publish down filters. 156 $query->where('(l.publish_start_date IS NULL OR l.publish_start_date <= ' . $nowDate . ')') 157 ->where('(l.publish_end_date IS NULL OR l.publish_end_date >= ' . $nowDate . ')'); 158 159 $query->group('l.link_id'); 160 $query->group('l.object'); 161 162 /* 163 * Add the taxonomy filters to the query. We have to join the taxonomy 164 * map table for each group so that we can use AND clauses across 165 * groups. Within each group there can be an array of values that will 166 * use OR clauses. 167 */ 168 if (!empty($this->searchquery->filters)) { 169 // Convert the associative array to a numerically indexed array. 170 $groups = array_values($this->searchquery->filters); 171 $taxonomies = call_user_func_array('array_merge', array_values($this->searchquery->filters)); 172 173 $query->join('INNER', $db->quoteName('#__finder_taxonomy_map') . ' AS t ON t.link_id = l.link_id') 174 ->where('t.node_id IN (' . implode(',', array_unique($taxonomies)) . ')'); 175 176 // Iterate through each taxonomy group. 177 for ($i = 0, $c = count($groups); $i < $c; $i++) { 178 $query->having('SUM(CASE WHEN t.node_id IN (' . implode(',', $groups[$i]) . ') THEN 1 ELSE 0 END) > 0'); 179 } 180 } 181 182 // Add the start date filter to the query. 183 if (!empty($this->searchquery->date1)) { 184 // Escape the date. 185 $date1 = $db->quote($this->searchquery->date1); 186 187 // Add the appropriate WHERE condition. 188 if ($this->searchquery->when1 === 'before') { 189 $query->where($db->quoteName('l.start_date') . ' <= ' . $date1); 190 } elseif ($this->searchquery->when1 === 'after') { 191 $query->where($db->quoteName('l.start_date') . ' >= ' . $date1); 192 } else { 193 $query->where($db->quoteName('l.start_date') . ' = ' . $date1); 194 } 195 } 196 197 // Add the end date filter to the query. 198 if (!empty($this->searchquery->date2)) { 199 // Escape the date. 200 $date2 = $db->quote($this->searchquery->date2); 201 202 // Add the appropriate WHERE condition. 203 if ($this->searchquery->when2 === 'before') { 204 $query->where($db->quoteName('l.start_date') . ' <= ' . $date2); 205 } elseif ($this->searchquery->when2 === 'after') { 206 $query->where($db->quoteName('l.start_date') . ' >= ' . $date2); 207 } else { 208 $query->where($db->quoteName('l.start_date') . ' = ' . $date2); 209 } 210 } 211 212 // Filter by language 213 if ($this->getState('filter.language')) { 214 $query->where('l.language IN (' . $db->quote(Factory::getLanguage()->getTag()) . ', ' . $db->quote('*') . ')'); 215 } 216 217 // Get the result ordering and direction. 218 $ordering = $this->getState('list.ordering', 'm.weight'); 219 $direction = $this->getState('list.direction', 'DESC'); 220 221 /* 222 * If we are ordering by relevance we have to add up the relevance 223 * scores that are contained in the ordering field. 224 */ 225 if ($ordering === 'm.weight') { 226 // Get the base query and add the ordering information. 227 $query->select('SUM(' . $db->escape($ordering) . ') AS ordering'); 228 } else { 229 /** 230 * If we are not ordering by relevance, we just have to add 231 * the unique items to the set. 232 */ 233 // Get the base query and add the ordering information. 234 $query->select($db->escape($ordering) . ' AS ordering'); 235 } 236 237 $query->order('ordering ' . $db->escape($direction)); 238 239 /* 240 * If there are no optional or required search terms in the query, we 241 * can get the results in one relatively simple database query. 242 */ 243 if (empty($this->includedTerms) && $this->searchquery->empty && $this->searchquery->input == '') { 244 // Return the results. 245 return $query; 246 } 247 248 /* 249 * If there are no optional or required search terms in the query and 250 * empty searches are not allowed, we return an empty query. 251 * If the search term is not empty and empty searches are allowed, 252 * but no terms were found, we return an empty query as well. 253 */ 254 if ( 255 empty($this->includedTerms) 256 && (!$this->searchquery->empty || ($this->searchquery->empty && $this->searchquery->input != '')) 257 ) { 258 // Since we need to return a query, we simplify this one. 259 $query->clear('join') 260 ->clear('where') 261 ->clear('bounded') 262 ->clear('having') 263 ->clear('group') 264 ->where('false'); 265 266 return $query; 267 } 268 269 $included = call_user_func_array('array_merge', array_values($this->includedTerms)); 270 $query->join('INNER', $db->quoteName('#__finder_links_terms') . ' AS m ON m.link_id = l.link_id') 271 ->where('m.term_id IN (' . implode(',', $included) . ')'); 272 273 // Check if there are any excluded terms to deal with. 274 if (count($this->excludedTerms)) { 275 $query2 = $db->getQuery(true); 276 $query2->select('e.link_id') 277 ->from($db->quoteName('#__finder_links_terms', 'e')) 278 ->where('e.term_id IN (' . implode(',', $this->excludedTerms) . ')'); 279 $query->where('l.link_id NOT IN (' . $query2 . ')'); 280 } 281 282 /* 283 * The query contains required search terms. 284 */ 285 if (count($this->requiredTerms)) { 286 foreach ($this->requiredTerms as $terms) { 287 if (count($terms)) { 288 $query->having('SUM(CASE WHEN m.term_id IN (' . implode(',', $terms) . ') THEN 1 ELSE 0 END) > 0'); 289 } else { 290 $query->where('false'); 291 break; 292 } 293 } 294 } 295 296 return $query; 297 } 298 299 /** 300 * Method to get a store id based on model the configuration state. 301 * 302 * This is necessary because the model is used by the component and 303 * different modules that might need different sets of data or different 304 * ordering requirements. 305 * 306 * @param string $id An identifier string to generate the store id. [optional] 307 * @param boolean $page True to store the data paged, false to store all data. [optional] 308 * 309 * @return string A store id. 310 * 311 * @since 2.5 312 */ 313 protected function getStoreId($id = '', $page = true) 314 { 315 // Get the query object. 316 $query = $this->getQuery(); 317 318 // Add the search query state. 319 $id .= ':' . $query->input; 320 $id .= ':' . $query->language; 321 $id .= ':' . $query->filter; 322 $id .= ':' . serialize($query->filters); 323 $id .= ':' . $query->date1; 324 $id .= ':' . $query->date2; 325 $id .= ':' . $query->when1; 326 $id .= ':' . $query->when2; 327 328 if ($page) { 329 // Add the list state for page specific data. 330 $id .= ':' . $this->getState('list.start'); 331 $id .= ':' . $this->getState('list.limit'); 332 $id .= ':' . $this->getState('list.ordering'); 333 $id .= ':' . $this->getState('list.direction'); 334 } 335 336 return parent::getStoreId($id); 337 } 338 339 /** 340 * Method to auto-populate the model state. Calling getState in this method will result in recursion. 341 * 342 * @param string $ordering An optional ordering field. [optional] 343 * @param string $direction An optional direction. [optional] 344 * 345 * @return void 346 * 347 * @since 2.5 348 */ 349 protected function populateState($ordering = null, $direction = null) 350 { 351 // Get the configuration options. 352 $app = Factory::getApplication(); 353 $input = $app->input; 354 $params = $app->getParams(); 355 $user = Factory::getUser(); 356 $language = Factory::getLanguage(); 357 358 $this->setState('filter.language', Multilanguage::isEnabled()); 359 360 $request = $input->request; 361 $options = array(); 362 363 // Get the empty query setting. 364 $options['empty'] = $params->get('allow_empty_query', 0); 365 366 // Get the static taxonomy filters. 367 $options['filter'] = $request->getInt('f', $params->get('f', '')); 368 369 // Get the dynamic taxonomy filters. 370 $options['filters'] = $request->get('t', $params->get('t', array()), 'array'); 371 372 // Get the query string. 373 $options['input'] = $request->getString('q', $params->get('q', '')); 374 375 // Get the query language. 376 $options['language'] = $request->getCmd('l', $params->get('l', $language->getTag())); 377 378 // Set the word match mode 379 $options['word_match'] = $params->get('word_match', 'exact'); 380 381 // Get the start date and start date modifier filters. 382 $options['date1'] = $request->getString('d1', $params->get('d1', '')); 383 $options['when1'] = $request->getString('w1', $params->get('w1', '')); 384 385 // Get the end date and end date modifier filters. 386 $options['date2'] = $request->getString('d2', $params->get('d2', '')); 387 $options['when2'] = $request->getString('w2', $params->get('w2', '')); 388 389 // Load the query object. 390 $this->searchquery = new Query($options, $this->getDatabase()); 391 392 // Load the query token data. 393 $this->excludedTerms = $this->searchquery->getExcludedTermIds(); 394 $this->includedTerms = $this->searchquery->getIncludedTermIds(); 395 $this->requiredTerms = $this->searchquery->getRequiredTermIds(); 396 397 // Load the list state. 398 $this->setState('list.start', $input->get('limitstart', 0, 'uint')); 399 $this->setState('list.limit', $input->get('limit', $params->get('list_limit', $app->get('list_limit', 20)), 'uint')); 400 401 /* 402 * Load the sort ordering. 403 * Currently this is 'hard' coded via menu item parameter but may not satisfy a users need. 404 * More flexibility was way more user friendly. So we allow the user to pass a custom value 405 * from the pool of fields that are indexed like the 'title' field. 406 * Also, we allow this parameter to be passed in either case (lower/upper). 407 */ 408 $order = $input->getWord('o', $params->get('sort_order', 'relevance')); 409 $order = StringHelper::strtolower($order); 410 $this->setState('list.raworder', $order); 411 412 switch ($order) { 413 case 'date': 414 $this->setState('list.ordering', 'l.start_date'); 415 break; 416 417 case 'price': 418 $this->setState('list.ordering', 'l.list_price'); 419 break; 420 421 case ($order === 'relevance' && !empty($this->includedTerms)): 422 $this->setState('list.ordering', 'm.weight'); 423 break; 424 425 case 'title': 426 $this->setState('list.ordering', 'l.title'); 427 break; 428 429 default: 430 $this->setState('list.ordering', 'l.link_id'); 431 $this->setState('list.raworder'); 432 break; 433 } 434 435 /* 436 * Load the sort direction. 437 * Currently this is 'hard' coded via menu item parameter but may not satisfy a users need. 438 * More flexibility was way more user friendly. So we allow to be inverted. 439 * Also, we allow this parameter to be passed in either case (lower/upper). 440 */ 441 $dirn = $input->getWord('od', $params->get('sort_direction', 'desc')); 442 $dirn = StringHelper::strtolower($dirn); 443 444 switch ($dirn) { 445 case 'asc': 446 $this->setState('list.direction', 'ASC'); 447 break; 448 449 default: 450 $this->setState('list.direction', 'DESC'); 451 break; 452 } 453 454 // Set the match limit. 455 $this->setState('match.limit', 1000); 456 457 // Load the parameters. 458 $this->setState('params', $params); 459 460 // Load the user state. 461 $this->setState('user.id', (int) $user->get('id')); 462 $this->setState('user.groups', $user->getAuthorisedViewLevels()); 463 } 464 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |