From e15237998d6de3fd4e58042077c986cab0d2a972 Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Wed, 13 Mar 2013 14:49:36 -0400 Subject: [PATCH] Refactored WorldCat search to use new search subsystem. --- module/VuFind/config/module.config.php | 7 +- .../Search/Factory/WorldCatBackendFactory.php | 153 +++++++++ .../src/VuFind/Search/WorldCat/Results.php | 99 ++---- .../src/VuFind/View/Helper/Root/WorldCat.php | 9 +- .../VuFindSearch/Backend/SRU/Connector.php} | 4 +- .../VuFindSearch/Backend/WorldCat/Backend.php | 306 ++++++++++++++++++ .../Backend/WorldCat/Connector.php} | 104 +++--- .../Backend/WorldCat/QueryBuilder.php | 33 +- .../Response/XML/RecordCollection.php | 241 ++++++++++++++ .../Response/XML/RecordCollectionFactory.php | 97 ++++++ themes/root/theme.config.php | 2 +- 11 files changed, 934 insertions(+), 121 deletions(-) create mode 100644 module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php rename module/{VuFind/src/VuFind/Connection/SRU.php => VuFindSearch/src/VuFindSearch/Backend/SRU/Connector.php} (98%) create mode 100644 module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Backend.php rename module/{VuFind/src/VuFind/Connection/WorldCat.php => VuFindSearch/src/VuFindSearch/Backend/WorldCat/Connector.php} (52%) create mode 100644 module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollection.php create mode 100644 module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index 6831976a851..a1e2ba4581f 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -276,12 +276,6 @@ $config = array( return $translator; }, - 'VuFind\WorldCatConnection' => function ($sm) { - return new \VuFind\Connection\WorldCat( - $sm->get('VuFind\Config')->get('config'), - $sm->get('VuFind\Http')->createClient() - ); - }, 'VuFind\WorldCatUtils' => function ($sm) { $config = $sm->get('VuFind\Config')->get('config'); $wcId = isset($config->WorldCat->id) @@ -720,6 +714,7 @@ $config = array( 'Solr' => 'VuFind\Search\Factory\SolrDefaultBackendFactory', 'SolrAuth' => 'VuFind\Search\Factory\SolrAuthBackendFactory', 'SolrReserves' => 'VuFind\Search\Factory\SolrReservesBackendFactory', + 'WorldCat' => 'VuFind\Search\Factory\WorldCatBackendFactory', ) ), 'session' => array( diff --git a/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php b/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php new file mode 100644 index 00000000000..bdba88b11c0 --- /dev/null +++ b/module/VuFind/src/VuFind/Search/Factory/WorldCatBackendFactory.php @@ -0,0 +1,153 @@ +<?php + +/** + * Factory for WorldCat backends. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2013. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org Main Site + */ + +namespace VuFind\Search\Factory; + +use VuFindSearch\Backend\BackendInterface; +use VuFindSearch\Backend\WorldCat\Response\XML\RecordCollectionFactory; +use VuFindSearch\Backend\WorldCat\QueryBuilder; +use VuFindSearch\Backend\WorldCat\Connector; +use VuFindSearch\Backend\WorldCat\Backend; + +use Zend\ServiceManager\ServiceLocatorInterface; +use Zend\ServiceManager\FactoryInterface; + +/** + * Factory for WorldCat backends. + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org Main Site + */ +class WorldCatBackendFactory implements FactoryInterface +{ + /** + * Logger. + * + * @var Zend\Log\LoggerInterface + */ + protected $logger; + + /** + * Superior service manager. + * + * @var ServiceLocatorInterface + */ + protected $serviceLocator; + + /** + * VuFind configuration + * + * @var \Zend\Config\Config + */ + protected $config; + + /** + * Create the backend. + * + * @param ServiceLocatorInterface $serviceLocator Superior service manager + * + * @return BackendInterface + */ + public function createService (ServiceLocatorInterface $serviceLocator) + { + $this->serviceLocator = $serviceLocator; + $this->config = $this->serviceLocator->get('VuFind\Config')->get('config'); + if ($this->serviceLocator->has('VuFind\Logger')) { + $this->logger = $this->serviceLocator->get('VuFind\Logger'); + } + $connector = $this->createConnector(); + $backend = $this->createBackend($connector); + return $backend; + } + + /** + * Create the WorldCat backend. + * + * @param Connector $connector Connector + * + * @return Backend + */ + protected function createBackend (Connector $connector) + { + $backend = new Backend($connector, $this->createRecordCollectionFactory()); + $backend->setLogger($this->logger); + $backend->setQueryBuilder($this->createQueryBuilder()); + return $backend; + } + + /** + * Create the WorldCat connector. + * + * @return Connector + */ + protected function createConnector () + { + $wsKey = isset($this->config->WorldCat->apiKey) + ? $this->config->WorldCat->apiKey : null; + $limitCodes = isset($this->config->WorldCat->LimitCodes) + ? $this->config->WorldCat->LimitCodes : null; + $connector = new Connector( + $wsKey, $limitCodes, + $this->serviceLocator->get('VuFind\Http')->createClient() + ); + $connector->setLogger($this->logger); + return $connector; + } + + /** + * Create the WorldCat query builder. + * + * @return QueryBuilder + */ + protected function createQueryBuilder () + { + $exclude = isset($this->config->WorldCat->OCLCCode) + ? $this->config->WorldCat->OCLCCode : null; + return new QueryBuilder($exclude); + } + + /** + * Create the record collection factory + * + * @return RecordCollectionFactory + */ + protected function createRecordCollectionFactory () + { + $manager = $this->serviceLocator->get('VuFind\RecordDriverPluginManager'); + $callback = function ($data) use ($manager) { + $driver = $manager->get('WorldCat'); + $driver->setRawData($data); + return $driver; + }; + return new RecordCollectionFactory($callback); + } +} \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Search/WorldCat/Results.php b/module/VuFind/src/VuFind/Search/WorldCat/Results.php index 0321537d906..8f180a0fb21 100644 --- a/module/VuFind/src/VuFind/Search/WorldCat/Results.php +++ b/module/VuFind/src/VuFind/Search/WorldCat/Results.php @@ -26,8 +26,9 @@ * @link http://www.vufind.org Main Page */ namespace VuFind\Search\WorldCat; -use VuFind\Exception\RecordMissing as RecordMissingException, - VuFind\Search\Base\Results as BaseResults; +use VuFind\Exception\RecordMissing as RecordMissingException; +use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\ParamBag; /** * WorldCat Search Parameters @@ -38,23 +39,8 @@ use VuFind\Exception\RecordMissing as RecordMissingException, * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://www.vufind.org Main Page */ -class Results extends BaseResults +class Results extends \VuFind\Search\Base\Results { - /** - * Raw search response: - */ - protected $rawResponse = null; - - /** - * Get a connection to the WorldCat API. - * - * @return \VuFind\Connection\WorldCat - */ - public function getWorldCatConnection() - { - return $this->getServiceLocator()->get('VuFind\WorldCatConnection'); - } - /** * Support method for performAndProcessSearch -- perform a search based on the * parameters passed to the object. @@ -63,34 +49,14 @@ class Results extends BaseResults */ protected function performSearch() { - // Collect the search parameters: - $config = $this->getServiceLocator()->get('VuFind\Config')->get('config'); - $wc = $this->getWorldCatConnection(); - $queryBuilder = new \VuFindSearch\Backend\WorldCat\QueryBuilder(); - $query = $queryBuilder->build($this->getParams()->getQuery())->get('query'); - $query = $query[0]; - - // Perform the search: - $this->rawResponse = $wc->search( - $query, $config->WorldCat->OCLCCode, $this->getParams()->getPage(), - $this->getParams()->getLimit(), $this->getParams()->getSort() - ); + $query = $this->getParams()->getQuery(); + $limit = $this->getParams()->getLimit(); + $offset = $this->getStartRecord() - 1; + $params = $this->createBackendParameters($query, $this->getParams()); + $collection = $this->getSearchService()->search('WorldCat', $query, $offset, $limit, $params); - // How many results were there? - $this->resultTotal = isset($this->rawResponse->numberOfRecords) - ? intval($this->rawResponse->numberOfRecords) : 0; - - // Construct record drivers for all the items in the response: - $this->results = array(); - if (isset($this->rawResponse->records->record) - && count($this->rawResponse->records->record) > 0 - ) { - foreach ($this->rawResponse->records->record as $current) { - $this->results[] = $this->initRecordDriver( - $current->recordData->record->asXML() - ); - } - } + $this->resultTotal = $collection->getTotal(); + $this->results = $collection->getRecords(); } /** @@ -103,31 +69,15 @@ class Results extends BaseResults */ public function getRecord($id) { - $wc = $this->getWorldCatConnection(); - $record = $wc->getRecord($id); - if (empty($record)) { + $collection = $this->getSearchService()->retrieve('WorldCat', $id); + + if (count($collection) == 0) { throw new RecordMissingException( 'Record ' . $id . ' does not exist.' ); } - return $this->initRecordDriver($record); - } - /** - * Support method for performSearch(): given a WorldCat MARC record, - * construct an appropriate record driver object. - * - * @param string $data Raw record data - * - * @return \VuFind\RecordDriver\Base - */ - protected function initRecordDriver($data) - { - $factory = $this->getServiceLocator() - ->get('VuFind\RecordDriverPluginManager'); - $driver = $factory->get('WorldCat'); - $driver->setRawData($data); - return $driver; + return current($collection->getRecords()); } /** @@ -143,4 +93,23 @@ class Results extends BaseResults // No facets in WorldCat: return array(); } + + /** + * Create search backend parameters for advanced features. + * + * @param Params $params Search parameters + * + * @return ParamBag + * @tag NEW SEARCH + */ + protected function createBackendParameters (AbstractQuery $query, Params $params) + { + $backendParams = new ParamBag(); + + // Sort + $sort = $params->getSort(); + $backendParams->set('sortKeys', empty($sort) ? 'relevance' : $sort); + + return $backendParams; + } } \ No newline at end of file diff --git a/module/VuFind/src/VuFind/View/Helper/Root/WorldCat.php b/module/VuFind/src/VuFind/View/Helper/Root/WorldCat.php index 57e38e8c460..01b0e8da8eb 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/WorldCat.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/WorldCat.php @@ -26,6 +26,7 @@ * @link http://vufind.org/wiki/vufind2:developer_manual Wiki */ namespace VuFind\View\Helper\Root; +use VuFindSearch\Backend\WorldCat\Connector; use Zend\View\Helper\AbstractHelper; /** @@ -42,16 +43,16 @@ class WorldCat extends AbstractHelper /** * WorldCat connection * - * @var \VuFind\Connection\WorldCat + * @var Connector */ protected $wc; /** * Constructor * - * @param \VuFind\Connection\WorldCat $wc WorldCat connection + * @param Connector $wc WorldCat connection */ - public function __construct(\VuFind\Connection\WorldCat $wc) + public function __construct(Connector $wc) { $this->wc = $wc; } @@ -61,7 +62,7 @@ class WorldCat extends AbstractHelper * * @param string $id Record ID * - * @return SimpleXMLElement + * @return \SimpleXMLElement */ public function getHoldings($id) { diff --git a/module/VuFind/src/VuFind/Connection/SRU.php b/module/VuFindSearch/src/VuFindSearch/Backend/SRU/Connector.php similarity index 98% rename from module/VuFind/src/VuFind/Connection/SRU.php rename to module/VuFindSearch/src/VuFindSearch/Backend/SRU/Connector.php index cfd00134b6f..22661528112 100644 --- a/module/VuFind/src/VuFind/Connection/SRU.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/SRU/Connector.php @@ -25,7 +25,7 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:developer_manual Wiki */ -namespace VuFind\Connection; +namespace VuFindSearch\Backend\SRU; use VuFind\XSLT\Processor as XSLTProcessor, Zend\Log\LoggerInterface; /** @@ -37,7 +37,7 @@ use VuFind\XSLT\Processor as XSLTProcessor, Zend\Log\LoggerInterface; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:developer_manual Wiki */ -class SRU implements \Zend\Log\LoggerAwareInterface +class Connector implements \Zend\Log\LoggerAwareInterface { /** * Logger object for debug info (or false for no debugging). diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Backend.php new file mode 100644 index 00000000000..f939039c6de --- /dev/null +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Backend.php @@ -0,0 +1,306 @@ +<?php + +/** + * WorldCat backend. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org + */ + +namespace VuFindSearch\Backend\WorldCat; + +use VuFindSearch\Query\AbstractQuery; + +use VuFindSearch\ParamBag; + +use VuFindSearch\Response\RecordCollectionInterface; +use VuFindSearch\Response\RecordCollectionFactoryInterface; + +use VuFindSearch\Backend\BackendInterface; + +use Zend\Log\LoggerInterface; + +/** + * WorldCat backend. + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org + */ +class Backend implements BackendInterface +{ + /** + * Record collection factory. + * + * @var RecordCollectionFactoryInterface + */ + protected $collectionFactory; + + /** + * Logger, if any. + * + * @var LoggerInterface + */ + protected $logger; + + /** + * Connector. + * + * @var Connector + */ + protected $connector; + + /** + * Backend identifier. + * + * @var string + */ + protected $identifier; + + /** + * Query builder. + * + * @var QueryBuilder + */ + protected $queryBuilder; + + /** + * Constructor. + * + * @param Connector $connector WorldCat connector + * @param RecordCollectionFactoryInterface $factory Record collection factory + * + * @return void + */ + public function __construct (Connector $connector, + RecordCollectionFactoryInterface $factory + ) { + $this->setRecordCollectionFactory($factory); + $this->connector = $connector; + $this->identifier = null; + } + + /** + * Set the backend identifier. + * + * @param string $identifier Backend identifier + * + * @return void + */ + public function setIdentifier ($identifier) + { + $this->identifier = $identifier; + } + + /** + * Perform a search and return record collection. + * + * @param AbstractQuery $query Search query + * @param integer $offset Search offset + * @param integer $limit Search limit + * @param ParamBag $params Search backend parameters + * + * @return RecordCollectionInterface + */ + public function search (AbstractQuery $query, $offset, $limit, + ParamBag $params = null + ) { + $response = $this->connector->search( + $query, $offset, $limit, $this->getQueryBuilder(), $params + ); + $collection = $this->createRecordCollection($response); + $this->injectSourceIdentifier($collection); + return $collection; + } + + /** + * Retrieve a single document. + * + * @param string $id Document identifier + * @param ParamBag $params Search backend parameters + * + * @return RecordCollectionInterface + */ + public function retrieve ($id, ParamBag $params = null) + { + $response = $this->connector->getRecord($id, $params); + $collection = $this->createRecordCollection($response); + $this->injectSourceIdentifier($collection); + return $collection; + } + + /** + * Return similar records. + * + * @param string $id Id of record to compare with + * @param ParamBag $params Search backend parameters + * + * @return RecordCollectionInterface + */ + public function similar ($id, ParamBag $params = null) + { + // Not supported here -- see \VuFind\Related\WorldCatSimilar for an alternate + // approach. + return $this->createRecordCollection( + array( + 'docs' => array(), + 'time' => 0, + 'total' => 0, + 'offset' => 0 + ) + ); + } + + /** + * Set the Logger. + * + * @param LoggerInterface $logger Logger + * + * @return void + */ + public function setLogger (LoggerInterface $logger) + { + $this->logger = $logger; + } + + /** + * Return query builder. + * + * Lazy loads an empty QueryBuilder if none was set. + * + * @return QueryBuilder + */ + public function getQueryBuilder () + { + if (!$this->queryBuilder) { + $this->queryBuilder = new QueryBuilder(); + } + return $this->queryBuilder; + } + + /** + * Set the query builder. + * + * @param QueryBuilder $queryBuilder Query builder + * + * @return void + * + * @todo Typehint QueryBuilderInterface + */ + public function setQueryBuilder (QueryBuilder $queryBuilder) + { + $this->queryBuilder = $queryBuilder; + } + + /** + * Return backend identifier. + * + * @return string + */ + public function getIdentifier () + { + return $this->identifier; + } + + /** + * Set the record collection factory. + * + * @param RecordCollectionFactoryInterface $factory Factory + * + * @return void + */ + public function setRecordCollectionFactory (RecordCollectionFactoryInterface $factory) + { + $this->collectionFactory = $factory; + } + + /** + * Return the record collection factory. + * + * Lazy loads a generic collection factory. + * + * @return RecordCollectionFactoryInterface + */ + public function getRecordCollectionFactory () + { + return $this->collectionFactory; + } + + /** + * Return the WorldCat connector. + * + * @return Connector + */ + public function getConnector () + { + return $this->connector; + } + + /// Internal API + + /** + * Inject source identifier in record collection and all contained records. + * + * @param ResponseInterface $response Response + * + * @return void + */ + protected function injectSourceIdentifier (RecordCollectionInterface $response) + { + $response->setSourceIdentifier($this->identifier); + foreach ($response as $record) { + $record->setSourceIdentifier($this->identifier); + } + return $response; + } + + /** + * Send a message to the logger. + * + * @param string $level Log level + * @param string $message Log message + * @param array $context Log context + * + * @return void + */ + protected function log ($level, $message, array $context = array()) + { + if ($this->logger) { + $this->logger->$level($message, $context); + } + } + + /** + * Create record collection. + * + * @param array $records Records to process + * + * @return RecordCollectionInterface + */ + protected function createRecordCollection ($records) + { + return $this->getRecordCollectionFactory()->factory($records); + } +} \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Connection/WorldCat.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Connector.php similarity index 52% rename from module/VuFind/src/VuFind/Connection/WorldCat.php rename to module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Connector.php index f148ddeae80..0336b5ffc24 100644 --- a/module/VuFind/src/VuFind/Connection/WorldCat.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Connector.php @@ -26,7 +26,9 @@ * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:developer_manual Wiki */ -namespace VuFind\Connection; +namespace VuFindSearch\Backend\WorldCat; +use VuFindSearch\Query\AbstractQuery; +use VuFindSearch\ParamBag; /** * WorldCat SRU Search Interface @@ -37,7 +39,7 @@ namespace VuFind\Connection; * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link http://vufind.org/wiki/vufind2:developer_manual Wiki */ -class WorldCat extends SRU +class Connector extends \VuFindSearch\Backend\SRU\Connector { /** * OCLC API key @@ -56,19 +58,16 @@ class WorldCat extends SRU /** * Constructor * - * @param \Zend\Config\Config $config VuFind configuration - * @param \Zend\Http\Client $client An HTTP client object + * @param string $wsKey Web services key + * @param string $limitCodes OCLC codes to use for limiting + * @param \Zend\Http\Client $client An HTTP client object */ - public function __construct(\Zend\Config\Config $config, - \Zend\Http\Client $client - ) { + public function __construct($wsKey, $limitCodes, \Zend\Http\Client $client) { parent::__construct( 'http://www.worldcat.org/webservices/catalog/search/sru', $client ); - $this->wskey = isset($config->WorldCat->apiKey) - ? $config->WorldCat->apiKey : null; - $this->limitCodes = isset($config->WorldCat->LimitCodes) - ? $config->WorldCat->LimitCodes : null; + $this->wskey = $wsKey; + $this->limitCodes = $limitCodes; } /** @@ -95,58 +94,83 @@ class WorldCat extends SRU /** * Retrieve a specific record. * - * @param string $id Record ID to retrieve + * @param string $id Record ID to retrieve + * @param ParamBag $params Parameters * * @throws \Exception * @return string MARC XML */ - public function getRecord($id) + public function getRecord($id, ParamBag $params = null) { + $params = $params ?: new ParamBag(); + $params->set('servicelevel', 'full'); + $params->set('wskey', $this->wskey); + $this->client->resetParameters(); $uri = 'http://www.worldcat.org/webservices/catalog/content/' . $id; - $uri .= "?wskey={$this->wskey}&servicelevel=full"; + $uri .= '?' . implode('&', $params->request()); $this->client->setUri($uri); $this->debug('Connect: ' . $uri); + $start = microtime(true); $result = $this->client->setMethod('POST')->send(); + $length = microtime(true) - $start; $this->checkForHttpError($result); - return $result->getBody(); + // Check for error message in response: + $body = $result->getBody(); + $xml = simplexml_load_string($body); + $error = isset($xml->diagnostic); + + return array( + 'docs' => $error ? array() : array($body), + 'offset' => 0, + 'total' => $error ? 0 : 1, + 'time' => $length + ); } /** - * Search + * Execute a search. * - * @param string $query The search query - * @param string $oclcCode An OCLC code to exclude from results - * @param int $page The page of records to start with - * @param int $limit The number of records to return per page - * @param string $sort The value to be used by for sorting + * @param AbstractQuery $query Search query + * @param integer $offset Search offset + * @param integer $limit Search limit + * @param QueryBuilder $queryBuilder Query builder + * @param ParamBag $params Parameters * - * @throws \Exception - * @return array An array of query results + * @return array */ - public function search($query, $oclcCode = null, $page = 1, $limit = 10, - $sort = null + public function search (AbstractQuery $query, $offset, $limit, + QueryBuilder $queryBuilder, ParamBag $params = null ) { - // Exclude current library from results - if ($oclcCode) { - $query .= ' not srw.li all "' . $oclcCode . '"'; - } - - // Submit query - $start = ($page-1) * $limit; - $params = array('query' => $query, - 'startRecord' => $start, - 'maximumRecords' => $limit, - 'sortKeys' => empty($sort) ? 'relevance' : $sort, - 'servicelevel' => 'full', - 'wskey' => $this->wskey); + $params = $params ?: new ParamBag(); + $params->set('startRecord', $offset); + $params->set('maximumRecords', $limit); + $params->set('servicelevel', 'full'); + $params->set('wskey', $this->wskey); + $params->mergeWith($queryBuilder->build($query)); // Establish a limitation on searching by OCLC Codes if (!empty($this->limitCodes)) { - $params['oclcsymbol'] = $this->limitCodes; + $params->set('oclcsymbol', $this->limitCodes); } - return simplexml_load_string($this->call('POST', $params, false)); + $start = microtime(true); + $response = $this->call('POST', $params->getArrayCopy(), false); + $length = microtime(true) - $start; + + $xml = simplexml_load_string($response); + $docs = isset($xml->records->record) ? $xml->records->record : array(); + $finalDocs = array(); + foreach ($docs as $doc) { + $finalDocs[] = $doc->recordData->asXML(); + } + return array( + 'docs' => $finalDocs, + 'offset' => $offset, + 'total' => isset($xml->numberOfRecords) ? (int)$xml->numberOfRecords : 0, + 'time' => $length + ); } + } diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/QueryBuilder.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/QueryBuilder.php index 6db74ad4658..8eb765ea922 100644 --- a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/QueryBuilder.php +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/QueryBuilder.php @@ -50,8 +50,25 @@ use VuFindSearch\ParamBag; */ class QueryBuilder { + /** + * OCLC code to exclude from results + * + * @var string + */ + protected $oclcCodeToExclude; + /// Public API + /** + * Constructor + * + * @param string $exclude OCLC code to exclude from results + */ + public function __construct($exclude = null) + { + $this->oclcCodeToExclude = $exclude; + } + /** * Return WorldCat search parameters based on a user query and params. * @@ -61,8 +78,17 @@ class QueryBuilder */ public function build (AbstractQuery $query) { + // Build base query + $queryStr = $this->abstractQueryToString($query); + + // Exclude current library from results (if applicable) + if (null !== $this->oclcCodeToExclude) { + $queryStr .= ' not srw.li all "' . $this->oclcCodeToExclude . '"'; + } + + // Send back results $params = new ParamBag(); - $params->set('query', $this->abstractQueryToString($query)); + $params->set('query', $queryStr); return $params; } @@ -141,11 +167,12 @@ class QueryBuilder protected function queryToString(Query $query) { // Clean and validate input: - $lookfor = str_replace('"', '', $query->getString()); $index = $query->getHandler(); if (empty($index)) { - $index = 'srw.kw'; + // No handler? Just accept query string as-is; no modifications needed. + return $query->getString(); } + $lookfor = str_replace('"', '', $query->getString()); // The index may contain multiple parts -- we want to search all listed index // fields: diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollection.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollection.php new file mode 100644 index 00000000000..a23cd499269 --- /dev/null +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollection.php @@ -0,0 +1,241 @@ +<?php + +/** + * WorldCat record collection. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org + */ + +namespace VuFindSearch\Backend\WorldCat\Response\XML; + +use VuFindSearch\Response\RecordCollectionInterface; +use VuFindSearch\Response\RecordInterface; + +use VuFindSearch\Exception\RuntimeException; + +/** + * WorldCat record collection. + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org + */ +class RecordCollection implements RecordCollectionInterface +{ + /** + * Raw response. + * + * @var array + */ + protected $response; + + /** + * Response records. + * + * @var array + */ + protected $records; + + /** + * Constructor. + * + * @param array $response WorldCat response + * @param int $offset Starting offset + * @param int $time Search execution time (in MS) + * @param int $total Total record count (optional) + * + * @return void + */ + public function __construct (array $response) + { + $this->response = $response; + $this->records = array(); + $this->offset = $response['offset']; + $this->rewind(); + } + + /** + * Return total number of records found. + * + * @return int + */ + public function getTotal () + { + return $this->response['total']; + } + + /** + * Return query time in milli-seconds. + * + * @return float + */ + public function getQueryTime () + { + return $this->response['time']; + } + + /** + * Return facet information. + * + * @return array + */ + public function getFacets () + { + return array(); // not supported by WorldCat + } + + /** + * Return records. + * + * @return array + */ + public function getRecords () + { + return $this->records; + } + + /** + * Return offset in the total search result set. + * + * @return int + */ + public function getOffset () + { + return $this->offset; + } + + /** + * Return first record in response. + * + * @return RecordInterface|null + */ + public function first () + { + return isset($this->records[$this->offset]) ? $this->records[$this->offset] : null; + } + + /** + * Set the source backend identifier. + * + * @param string $identifier Backend identifier + * + * @return void + */ + public function setSourceIdentifier ($identifier) + { + $this->source = $identifier; + } + + /** + * Return the source backend identifier. + * + * @return string + */ + public function getSourceIdentifier () + { + return $this->source; + } + + /** + * Add a record to the collection. + * + * @param RecordInterface $record Record to add + * + * @return void + */ + public function add (RecordInterface $record) + { + if (!in_array($record, $this->records, true)) { + $this->records[$this->pointer] = $record; + $this->next(); + } + } + + /// Iterator interface + + /** + * Return true if current collection index is valid. + * + * @return boolean + */ + public function valid () + { + return isset($this->records[$this->pointer]); + } + + /** + * Return record at current collection index. + * + * @return RecordInterface + */ + public function current () + { + return $this->records[$this->pointer]; + } + + /** + * Rewind collection index. + * + * @return void + */ + public function rewind () + { + $this->pointer = $this->offset; + } + + /** + * Move to next collection index. + * + * @return void + */ + public function next () + { + $this->pointer++; + } + + /** + * Return current collection index. + * + * @return integer + */ + public function key () + { + return $this->pointer; + } + + /// Countable interface + + /** + * Return number of records in collection. + * + * @return integer + */ + public function count () + { + return count($this->records); + } + +} \ No newline at end of file diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php new file mode 100644 index 00000000000..e7e310bc3bc --- /dev/null +++ b/module/VuFindSearch/src/VuFindSearch/Backend/WorldCat/Response/XML/RecordCollectionFactory.php @@ -0,0 +1,97 @@ +<?php + +/** + * Simple XML-based factory for record collection. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org + */ + +namespace VuFindSearch\Backend\WorldCat\Response\XML; + +use VuFindSearch\Response\RecordCollectionFactoryInterface; +use VuFindSearch\Exception\InvalidArgumentException; + +/** + * Simple XML-based factory for record collection. + * + * @category VuFind2 + * @package Search + * @author David Maus <maus@hab.de> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org + */ +class RecordCollectionFactory implements RecordCollectionFactoryInterface +{ + /** + * Factory to turn data into a record object. + * + * @var Callable + */ + protected $recordFactory; + + /** + * Class of collection. + * + * @var string + */ + protected $collectionClass; + + /** + * Constructor. + * + * @param string $recordClass Class of collection records + * @param string $collectionClass Class of collection + * + * @return void + */ + public function __construct ($recordFactory = null, $collectionClass = null) { + if (!is_callable($recordFactory)) { + throw new InvalidArgumentException('Record factory must be callable.'); + } + $this->recordFactory = $recordFactory; + $this->collectionClass = (null === $collectionClass) + ? 'VuFindSearch\Backend\WorldCat\Response\XML\RecordCollection' + : $collectionClass; + } + + /** + * Return record collection. + * + * @param array $response Collection of XML documents + * + * @return RecordCollection + */ + public function factory ($response) + { + if (!is_array($response)) { + throw new InvalidArgumentException(sprintf('Unexpected type of value: Expected array, got %s', gettype($response))); + } + $collection = new $this->collectionClass($response); + foreach ($response['docs'] as $doc) { + $collection->add(call_user_func($this->recordFactory, $doc)); + } + return $collection; + } + +} \ No newline at end of file diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php index 3193669a6c6..65702beebbd 100644 --- a/themes/root/theme.config.php +++ b/themes/root/theme.config.php @@ -116,7 +116,7 @@ return array( }, 'worldcat' => function ($sm) { return new \VuFind\View\Helper\Root\WorldCat( - $sm->getServiceLocator()->get('VuFind\WorldCatConnection') + $sm->getServiceLocator()->get('VuFind\Search')->getBackend('WorldCat')->getConnector() ); } ), -- GitLab