dmBridge PHP API
DMObjectQuery.php
00001 <?php
00002 #
00003 # dmBridge: a data access framework for CONTENTdm(R)
00004 #
00005 # Copyright © 2009, 2010, 2011 Board of Regents of the Nevada System of Higher
00006 # Education, on behalf of the University of Nevada, Las Vegas
00007 #
00008 
00015 class DMObjectQuery extends DMAbstractQuery implements DMQuery,
00016       DMURIAddressable {
00017 
00018    const DEFAULT_LIMIT = 20;
00019 
00021    private static $current;
00022 
00024    private $facet_terms = array();
00026    private $max_rating;
00028    private $min_rating;
00030    private $sort_fields = array();
00035    private $suggestion = 1;
00036 
00037 
00042    public static function getCurrent() {
00043       return self::$current;
00044    }
00045 
00050    public static function setCurrent(DMObjectQuery $oq) {
00051       self::$current = $oq;
00052    }
00053 
00054    public function __construct() {
00055       parent::__construct();
00056       $this->setNumResultsPerPage(self::DEFAULT_LIMIT);
00057    }
00058 
00062    public function addFacetTerm(DMFacetTerm $f) {
00063       $this->facet_terms[] = $f;
00064    }
00065 
00069    public function getFacetTerms() {
00070       return $this->facet_terms;
00071    }
00072 
00079    public function getFeedURI() {
00080       return $this->getURI(DMBridgeComponent::TemplateEngine, "atom");
00081    }
00082 
00087    public function getRatingRange() {
00088       return array($this->min_rating, $this->max_rating);
00089    }
00090 
00099    public function setRatingRange($min, $max) {
00100       if ($min > $max) {
00101          throw new DMIllegalArgumentException(
00102                DMLocalizedString::getString("RATING_LARGER_THAN_MAX"));
00103       }
00104       $this->min_rating = abs($min);
00105       $this->max_rating = abs($max);
00106    }
00107 
00112    public function getSearchResults() {
00113       if ($this->min_rating !== null && $this->max_rating !== null) {
00114          return $this->getSearchResultsByRating();
00115       } else {
00116          return $this->getCdmSearchResults();
00117       }
00118    }
00119 
00120    private function getSearchResultsByRating() {
00121       $ds = DMDataStoreFactory::getDataStore();
00122       $total = 0;
00123       $this->results = $ds->getHighestRatedObjects(
00124             $this->getCollections(), $this->getPage(),
00125             $this->getNumResultsPerPage(), $total);
00126       $this->num_results = $total;
00127       return $this->results;
00128    }
00129 
00134    private function getCdmSearchResults() {
00135       if (count($this->results)) {
00136          return $this->results;
00137       }
00138 
00139       $terms = $this->getPredicates();
00140 
00141       // transform $terms into the format required by dmQuery()
00142       $dmqterms = $field = array();
00143       $count = count($terms);
00144       $cdm_no_likey = array(" and ");
00145 
00146       $stopwords_pathname = $_SERVER['DOCUMENT_ROOT'] . "/stopwords.txt";
00147       $stopwords = array();
00148       $fh = null;
00149       if (file_exists($stopwords_pathname)) {
00150          $fh = fopen($stopwords_pathname, "r");
00151          $tmp = fread($fh, filesize($stopwords_pathname));
00152          $stopwords = explode("\n", $tmp);
00153       }
00154 
00155       for ($i = 0; $i < $count; $i++) {
00156          if (!$terms[$i]->isValid()) {
00157             continue;
00158          }
00159          $field[$i] = (strtolower($terms[$i]->getField()->getNick()) == "any")
00160             ? "CISOSEARCHALL" : $terms[$i]->getField()->getNick();
00161 
00162          // remove all stop words
00163          foreach ($stopwords as $stopword) {
00164             $terms[$i]->setString(
00165                   str_replace(" " . $stopword . " ", "", $terms[$i]->getString()));
00166          }
00167 
00168          if (!$terms[$i]->getString()) {
00169             continue;
00170          }
00171          $dmqterms[$i] = array(
00172             'field' => $field[$i],
00173             'string' => str_replace($cdm_no_likey, "", $terms[$i]->getString()),
00174             'mode' => $terms[$i]->getMode()
00175          );
00176       }
00177 
00178       if ($fh) {
00179          fclose($fh);
00180       }
00181 
00182       $aliases = array();
00183       foreach ($this->getCollections() as $c) {
00184          if ($c->getAlias() == "/dmdefault") {
00185             $aliases = array("all");
00186             break;
00187          }
00188          $aliases[] = $c->getAlias();
00189       }
00190 
00191       if (count($aliases) < 1) {
00192          $aliases = array("all");
00193       }
00194 
00195       $objects = $this->getObjects();
00196       $ptr = count($objects) ? $objects[0]->getPtr() : -1;
00197       $suppress = ($ptr > -1) ? 0 : 1;
00198 
00199       $collections = $results = array();
00200       $sort_fields = $this->transformSortFieldsForCdm();
00201 
00202       // dmQuery() will modify $facets by reference
00203       $facets = array();
00204       foreach ($this->getFacetTerms() as $f) {
00205          $facets[] = $f->getField()->getNick();
00206       }
00207       $facets = implode(":", $facets);
00208       $facets_to_compare = $facets;
00209 
00210       $this->suggestion = 1;
00211 
00212       $results = dmQuery($aliases, $dmqterms, array('title'),
00213          $sort_fields, $this->getNumResultsPerPage(),
00214          $this->getStart() + 1, $this->num_results, $suppress, $ptr,
00215          $this->suggestion, $facets);
00216       // see comment in previous case statement
00217       foreach ($results as $r) {
00218          $collections[] = DMCollectionFactory::getCollection($r["collection"]);
00219       }
00220       // dmQuery may or may not change $facets; if not, don't try to
00221       // instantiate facets
00222       if ($facets != $facets_to_compare) {
00223          $this->facet_terms = $this->instantiateFacetsFromCdmXML(
00224                $facets, $collections);
00225       }
00226 
00227       // Assemble results
00228       $i = 0;
00229       foreach ($results as $r) {
00230          $this->results[] = DMObjectFactory::getObject(
00231                $collections[$i], $r['pointer']);
00232          $i++;
00233       }
00234       return $this->results;
00235    }
00236 
00245    private function instantiateFacetsFromCdmXML($xml, array $collections) {
00246       if (empty($xml)) {
00247          return array();
00248       }
00249       // dmQuery() returns invalid XML. Who knows if there any other
00250       // entities... or angle brackets (!!)... we need to escape.
00251       $xml = str_replace('&', '&amp;', $xml);
00252 
00253       if (count(array_unique($collections)) > 1 || count($collections) < 1) {
00254          $collections = array(DMCollectionFactory::getCollection("/dmdefault"));
00255       }
00256 
00257       $dxml = new DOMDocument('1.0', 'utf-8');
00258       if (!$dxml->loadXML($xml)) {
00259          return array();
00260       }
00261       // I guess we'll just assume that there are equal numbers of each tag...
00262       $labels = $names = $counts = array();
00263       foreach ($dxml->documentElement->getElementsByTagName('label') as $node) {
00264          $labels[] = $node->nodeValue;
00265       }
00266       foreach ($dxml->documentElement->getElementsByTagName('name') as $node) {
00267          $names[] = $node->nodeValue;
00268       }
00269       foreach ($dxml->documentElement->getElementsByTagName('count') as $node) {
00270          $counts[] = $node->nodeValue;
00271       }
00272       $facets = array();
00273       $count = count($names);
00274       for ($i = 0; $i < $count; $i++) {
00275          if (!is_object($collections[0]->getField($labels[$i]))) {
00276             continue;
00277          }
00278          $field = clone $collections[0]->getField($labels[$i]);
00279          // If the facet name is already part of the query, skip it
00280          foreach ($this->getPredicates() as $st) {
00281             if ($st->getString() == $names[$i]) {
00282                continue(2);
00283             }
00284          }
00285          // The facet names are all lowercase!!!!
00286          $field->setValue(ucwords($names[$i]));
00287          $ft = new DMFacetTerm($field, $counts[$i]);
00288 
00289          $q = clone $this;
00290          $st = new DMQueryPredicate();
00291          $st->setString($names[$i]);
00292          $st->setField($field);
00293          $st->setMode("all");
00294          $q->addPredicate($st);
00295 
00296          $ft->setURI($q->getURI());
00297          $facets[] = $ft;
00298       }
00299       return $facets;
00300    }
00301 
00306    public function setNumResultsPerPage($rpp) {
00307       // cdm PHP API defines 1024 as the max
00308       $this->results_per_page = ($rpp > 1024) ? 1024 : (int) abs($rpp);
00309    }
00310 
00315    public function arePredicates() {
00316       $terms = $this->getPredicates();
00317       if (count($terms) >= 1 && $terms[0] instanceof DMQueryPredicate) {
00318          return (!$terms[0]->isBrowse());
00319       }
00320       return false;
00321    }
00322 
00328    public function setSortFields(array $fields) {
00329       // if invalid fields are provided, default to sorting by title ascending
00330       $keys = array();
00331       foreach ($fields as $field => $direction) {
00332          $keys[] = $field;
00333       }
00334       if (count($fields) < 2 || !$keys[0]) {
00335          $this->sort_fields = array("title" => "asc");
00336       }
00337       $this->sort_fields = $fields;
00338    }
00339 
00346    public function getSuggestion() {
00347       return $this->suggestion;
00348    }
00349 
00350    private function transformSortFieldsForCdm() {
00351       $transformed = array();
00352       foreach ($this->getSortFields() as $field => $direction) {
00353          if ($direction == "desc") {
00354             $transformed[] = "reverse";
00355          } else {
00356             $transformed[] = $field;
00357          }
00358       }
00359       return $transformed;
00360    }
00361 
00371    public function getURI($component = DMBridgeComponent::TemplateEngine,
00372          $representation = null, DMTemplateSet $ts = null,
00373          DMInternalURI $inherited_uri = null) {
00374       $terms = $this->getPredicates();
00375       $collections = $this->getCollections();
00376       $qs = array();
00377       // page
00378       if ($this->getPage() > 1) {
00379          $qs['page'] = $this->getPage();
00380       }
00381       // limit
00382       if ($ts && $this->getNumResultsPerPage()
00383             != $ts->getNumResultsPerPage()) {
00384          $qs['rpp'] = $this->getNumResultsPerPage();
00385       }
00386 
00387       // query predicates
00388       for ($i = 1; $i <= count($terms); $i++) {
00389          if ($terms[$i-1]->getField()) {
00390             $qs['CISOOP' . $i] = $terms[$i-1]->getMode();
00391             $qs['CISOFIELD' . $i] = $terms[$i-1]->getField()->getNick();
00392             $qs['CISOBOX' . $i] = $terms[$i-1]->getString();
00393          }
00394       }
00395       // collections
00396       $params = $component == DMBridgeComponent::HTTPAPI
00397          ? "api/" . DMBridgeVersion::getLatestHTTPAPIVersion() : "objects";
00398 
00399       $parts = DMHTTPRequest::getCurrent()->getURI()->getParamComponents();
00400       if (count($parts) > 1 && $parts[0] == "objects"
00401             && DMCollection::exists("/" . $parts[1])) {
00402          $params .= "/" . $parts[1];
00403       } else {
00404          $aliases = array();
00405          for ($i = 1; $i <= count($collections); $i++) {
00406             $aliases[] = $collections[$i-1]->getAlias();
00407          }
00408          $qs['CISOROOT'] = implode(",", $aliases);
00409       }
00410 
00411       if (!array_key_exists("CISOBOX1", $qs)) {
00412          if (array_key_exists("CISOROOT", $qs)) {
00413             unset($qs['CISOROOT']);
00414          }
00415       } else if (count($collections)) {
00416          $qs['CISOROOT'] = $collections[0];
00417       }
00418 
00419       if ($inherited_uri) {
00420          $inherited_uri = clone $inherited_uri;
00421          $inherited_uri->setParams($params);
00422          foreach ($qs as $key => $value) {
00423             $inherited_uri->setQueryValue($key, $value);
00424          }
00425          $inherited_uri->setExtension($representation);
00426          return $inherited_uri;
00427       }
00428       return DMInternalURI::getURIWithParams($params, $qs, $representation);
00429    }
00430 
00431 }
 All Data Structures Functions Variables