CMS  Version 3.9
search_manager.inc
Go to the documentation of this file.
1 <?php
7 /**************************************************************
8 
9  Copyright (c) 2011 Sonjara, Inc
10 
11  Permission is hereby granted, free of charge, to any person
12  obtaining a copy of this software and associated documentation
13  files (the "Software"), to deal in the Software without
14  restriction, including without limitation the rights to use,
15  copy, modify, merge, publish, distribute, sublicense, and/or sell
16  copies of the Software, and to permit persons to whom the
17  Software is furnished to do so, subject to the following
18  conditions:
19 
20  The above copyright notice and this permission notice shall be
21  included in all copies or substantial portions of the Software.
22 
23  Except as contained in this notice, the name(s) of the above
24  copyright holders shall not be used in advertising or otherwise
25  to promote the sale, use or other dealings in this Software
26  without prior written authorization.
27 
28  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
30  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
32  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
33  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
34  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35  OTHER DEALINGS IN THE SOFTWARE.
36 
37 *****************************************************************/
38 
39 Fakoli::using("search", "component", "taxonomy");
40 Fakoli::usingFeature("paged_list", "facet_manager");
48 {
49  static $map;
50 
52  var $useFacetFilter = false;
53  var $displayMode = "list";
54 
55  // Facet dimensions
56  var $facetDropdown = true;
57  var $facetMaxWidth = "200px";
58  var $facetWidth = "200px";
59  var $facetHeight = "120px";
60 
61  function SearchManager($results = null)
62  {
63  $this->searchResults = $results;
64  }
65 
66  static function upgradeComponent($version)
67  {
68  Cache::invalidate("fakoli_search_map");
69  }
70 
71  static function setDefaults()
72  {
73  Settings::setDefaultValue("search", "search_result_format", "Combined List", "String", "Specifies the display format to use when displaying results", "", "Combined List\nGrouped by Type");
74  Settings::setDefaultValue("search", "results_per_page", "20", "Number", "Specifies the number of results to show per page. Set to zero for no pagination.");
75  Settings::setDefaultValue("search", "show_text_fragment", false, "Boolean", "Specifies whether to show with the search results. Note that not all search types will return a text fragment");
76 
78  }
79 
80  static function buildSearchMap()
81  {
83  {
84  SearchManager::$map = Cache::get("fakoli_search_map");
85  }
86 
88  {
89  $map = array();
90  $map = ComponentManager::fireEvent("RegisterSearchables", $map);
91  Cache::put("fakoli_search_map", $map);
93  }
94  }
95 
96  function search($params)
97  {
98  $searchResults = array();
99 
100  $searchHandler = ComponentManager::fireEvent("OverrideSearchHandler");
101 
102  if ($searchHandler)
103  {
104  trace("Search Handler: ".get_class($searchHandler), 3);
105  $this->searchResults = $searchHandler->search($params, $this);
106  }
107  else
108  {
109  trace("Search Handler: Using built-in SearchManager", 3);
110  $this->doSearch($params);
111  }
112 
113  return $this;
114  }
115 
116  function formatResults()
117  {
118  switch($this->displayMode)
119  {
120  case "table":
121  $this->list = new DataListView($this->extractDataItems(), "search_results");
122  $this->list->emptyMessage = "Your search did not match any items.";
123  $this->list->writeIdTag = true;
124  $this->list->sortable = true;
125  $this->list->filter = false;
126  break;
127 
128  case "list":
129  default:
130  $this->list = new PagedList($this->searchResults, "search_results", array(SearchManager, formatSearchResult));
131  $this->list->emptyList = "Your search did not match any items.";
132  $this->list->writeIdTag = true;
133  }
134 
135  if ($this->enableFacetFilter)
136  {
137  $this->facetManager = new FacetManager('facet_manager', $this->list);
138  $classes = $this->getSearchableClasses();
139  trace("Facet Classes: ".implode(", ", $classes), 3);
140 
141  if ($this->searchResultTypeFacet)
142  {
143  $this->facetManager->addFacet("Search Result Types", new SearchResultTypeFacetFilter($this->searchResults));
144  }
145 
146  TaxonomyManager::addFacets($this->facetManager, $classes, $this->facetDropdown, $this->facetMaxWidth, $this->facetWidth, $this->facetHeight);
147  }
148 
149  return $this;
150  }
151 
152  function doSearch($params)
153  {
155 
156  if (is_object($params))
157  {
158  $class_array = $_REQUEST["searchable_classes:"];
159  }
160  else
161  {
162  $params = trim($params);
163  }
164 
165  $searchResults = array();
166  if (!$params)
167  {
168  $this->searchResults = array();
169  }
170  else
171  {
172  trace(print_r(SearchManager::$map, true), 3);
173 
174  foreach(SearchManager::$map as $component => $searchables)
175  {
177 
178  foreach($searchables as $searchable)
179  {
180  if ($class_array && !array_key_exists($searchable, $class_array)) continue;
181 
182  $obj = new $searchable;
183  $results = $obj->search($params);
184  $searchResults = array_merge($searchResults, $results);
185  }
186  }
187 
188  $this->searchResults = $searchResults;
189  }
190 
191  trace("SEARCH: ".count($searchResults)." results found", 3);
192  return $searchResults;
193  }
194 
195 
197  {
199  if (isset($_REQUEST["searchable_classes:"]))
200  {
201  $class_array = $_REQUEST["searchable_classes:"];
202  }
203 
204  $cl = array();
205 
206  foreach(SearchManager::$map as $component => $searchables)
207  {
208  foreach($searchables as $searchable)
209  {
210  if ($class_array && !array_key_exists($searchable, $class_array)) continue;
211  $cl[] = $searchable;
212  }
213  }
214 
215  return $cl;
216  }
217 
218  function extractDataItems()
219  {
220  $items = array();
221 
222  foreach($this->searchResults as $result)
223  {
224  $items[] = $result->item;
225  }
226 
227  return $items;
228  }
229 
230  function sortByRelevance()
231  {
232  trace("Sorting ".count($this->searchResults)." items by relevance", 3);
233  usort($this->searchResults, array(SearchManager, compareRelevance));
234  return $this;
235  }
236 
237  static function compareRelevance($item1, $item2)
238  {
239  return ($item1->relevance() > $item2->relevance())?-1:1;
240  }
241 
242  function sortByTitle()
243  {
244  trace("Sorting ".count($this->searchResults)." items by title", 3);
245  usort($this->searchResults, array(SearchManager, compareTitle));
246  return $this;
247  }
248 
249  static function compareTitle($item1, $item2)
250  {
251  return strcmp($item1->title(), $item2->title());
252  }
253 
254  function sortByDate()
255  {
256  trace("Sorting ".count($this->searchResults)." items by date", 3);
257  usort($this->searchResults, array(SearchManager, compareDate));
258  return $this;
259  }
260 
261  static function compareDate($item1, $item2)
262  {
263  return strtotime($item2->date()) - strtotime($item1->date());
264  }
265 
266  static function formatSearchResult($item)
267  {
268  return $item->summary();
269  }
270 
271  function drawFacets($id = null)
272  {
273  trace("Drawing Facets", 3);
274  trace(getBacktrace(0), 3);
275 
276  global $script;
277  if ($this->drawnFacets || !$this->facetManager) return;
278 
279  if ($id)
280  {
281  $this->facetManager->id = $id;
282  }
283 
284  $script .= $this->facetManager->writeScript();
285  echo $this->facetManager->drawForm();
286 
287  $this->drawnFacets = true;
288  }
289 
290  function showResults($pageSize = 0)
291  {
292  global $script;
293 
294  if (!$this->list)
295  {
296  $this->formatResults();
297  }
298 
299  if ($pageSize == 0) $pageSize = Settings::getValue("search", "results_per_page");
300 
301  $this->list->pageSize = $pageSize;
302 
303  $script .= $this->list->writeScript();
304 
305  $num = count($this->searchResults);
306 
307  if ($num)
308  {
309  echo "<p><em>$num ".pluralize("match", $num)." found.</em></p>";
310  }
311 
312  $this->drawFacets();
313 
314  switch($this->displayMode)
315  {
316  case "table":
317  $this->list->drawView();
318  break;
319 
320  case "list":
321  default:
322  $this->list->drawList();
323  break;
324  }
325  }
326 
327  static function wrap($items, $resultClass)
328  {
329  $results = array();
330 
331  if (is_array($items))
332  {
333  foreach($items as $item)
334  {
335  $result = new $resultClass($item);
336  // Store some class metadata in the wrapper record
337  $result->innerClass = get_class($item);
338  $result->prettyClassName = $result->prettifyClassName(true);
339 
340  $results[] = $result;
341  }
342  }
343 
344  return $results;
345  }
346 }
347 ?>
$component
Definition: help.inc:38
static fireEvent($event, $parameter=null, $mustBeConsumed=false)
Fire an event to all subscribers as detailed in their manifests.
static usingFeature()
Uses the specified framework feature(s).
Definition: core.inc:388
static using()
Import the datamodels, views and manifest for the specified component(s).
Definition: core.inc:116
The SearchManager class provides a simple API for searching across all Searchable records in the appl...
static compareDate($item1, $item2)
SearchManager($results=null)
static compareTitle($item1, $item2)
static wrap($items, $resultClass)
static compareRelevance($item1, $item2)
showResults($pageSize=0)
static formatSearchResult($item)
static upgradeComponent($version)
drawFacets($id=null)
static setDefaults()
static buildSearchMap()
static getValue($component, $name)
Retrieve the value of the specified Setting.
Definition: settings.inc:104
static setDefaultValue($component, $name, $value, $field_type="String", $annotation="", $category="", $options="", $weight=0)
Sets the default value of the given component setting.
Definition: settings.inc:174
static addFacets($manager, $classes, $dropdown=true, $max_width="200px", $width="200px", $height="120px")
$result