Skip to content
Snippets Groups Projects
Commit 542f4fef authored by Demian Katz's avatar Demian Katz Committed by GitHub
Browse files

Refactor channel loading (#1174)

- Separate loading logic from controller
- Improve readability and reusability of code
parent a33f6443
Branches
Tags
No related merge requests found
......@@ -112,7 +112,7 @@ $config = [
'VuFind\Controller\BrowseController' => 'VuFind\Controller\AbstractBaseWithConfigFactory',
'VuFind\Controller\BrowZineController' => 'VuFind\Controller\AbstractBaseFactory',
'VuFind\Controller\CartController' => 'VuFind\Controller\CartControllerFactory',
'VuFind\Controller\ChannelsController' => 'VuFind\Controller\AbstractBaseFactory',
'VuFind\Controller\ChannelsController' => 'VuFind\Controller\ChannelsControllerFactory',
'VuFind\Controller\CollectionController' => 'VuFind\Controller\AbstractBaseWithConfigFactory',
'VuFind\Controller\CollectionsController' => 'VuFind\Controller\AbstractBaseWithConfigFactory',
'VuFind\Controller\CombinedController' => 'VuFind\Controller\AbstractBaseFactory',
......@@ -288,6 +288,7 @@ $config = [
'VuFind\Autocomplete\Suggester' => 'VuFind\Autocomplete\SuggesterFactory',
'VuFind\Cache\Manager' => 'VuFind\Cache\ManagerFactory',
'VuFind\Cart' => 'VuFind\CartFactory',
'VuFind\ChannelProvider\ChannelLoader' => 'VuFind\ChannelProvider\ChannelLoaderFactory',
'VuFind\ChannelProvider\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory',
'VuFind\Config\AccountCapabilities' => 'VuFind\Config\AccountCapabilitiesFactory',
'VuFind\Config\PluginManager' => 'VuFind\Config\PluginManagerFactory',
......
<?php
/**
* Channel loader
*
* PHP version 7
*
* Copyright (C) Villanova University 2016.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Channels
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development Wiki
*/
namespace VuFind\ChannelProvider;
use VuFind\Cache\Manager as CacheManager;
use VuFind\ChannelProvider\PluginManager as ChannelManager;
use VuFind\Record\Loader as RecordLoader;
use VuFind\Search\Base\Results;
use VuFind\Search\SearchRunner;
use Zend\Config\Config;
/**
* Channel loader
*
* @category VuFind
* @package Channels
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development Wiki
*/
class ChannelLoader
{
/**
* Cache manager
*
* @var CacheManager
*/
protected $cacheManager;
/**
* Channel manager
*
* @var ChannelManager
*/
protected $channelManager;
/**
* Channel configuration
*
* @var Config
*/
protected $config;
/**
* Record loader
*
* @var RecordLoader
*/
protected $recordLoader;
/**
* Search runner
*
* @var SearchRunner
*/
protected $searchRunner;
/**
* Constructor
*
* @param Config $config Channels configuration
* @param CacheManager $cache Cache manager
* @param ChannelManager $cm Channel manager
* @param SearchRunner $runner Search runner
* @param RecordLoader $loader Record loader
*/
public function __construct(Config $config, CacheManager $cache,
ChannelManager $cm, SearchRunner $runner, RecordLoader $loader
) {
$this->config = $config;
$this->cacheManager = $cache;
$this->channelManager = $cm;
$this->searchRunner = $runner;
$this->recordLoader = $loader;
}
/**
* Get a search results object configured by channel providers.
*
* @param array $searchRequest Search request parameters
* @param array $providers Array of channel providers
* @param string $source Backend to use
*
* @return Results
*/
protected function performChannelSearch($searchRequest, $providers, $source)
{
// Perform search and configure providers:
$callback = function ($runner, $params) use ($providers) {
foreach ($providers as $provider) {
$provider->configureSearchParams($params);
}
};
return $this->searchRunner->run($searchRequest, $source, $callback);
}
/**
* Get channel details using an array of providers and a populated search
* results object.
*
* @param array $providers Array of channel providers
* @param Results $results Search results object from performChannelSearch
* @param string $token Optional channel token
*
* @return array
*/
protected function getChannelsFromResults($providers, Results $results, $token)
{
// Collect details:
$channels = [];
foreach ($providers as $provider) {
$channels = array_merge(
$channels, $provider->getFromSearch($results, $token)
);
}
return $channels;
}
/**
* Get an array of channel providers matching the provided IDs (or just one,
* if the channelProvider GET parameter is set).
*
* @param string $source Search backend ID
* @param array $configSection Configuration section to load ID list from
* @param string $activeId Currently selected channel ID (if any; used
* when making an AJAX request for a single additional channel)
*
* @return array
*/
protected function getChannelProviders($source, $configSection, $activeId = null)
{
$providerIds = isset($this->config->{"source.$source"}->$configSection)
? $this->config->{"source.$source"}->$configSection->toArray() : [];
$finalIds = (!empty($activeId) && in_array($activeId, $providerIds))
? [$activeId] : $providerIds;
return array_map([$this, 'getChannelProvider'], $finalIds);
}
/**
* Convenience method to retrieve a channel provider.
*
* @param string $providerId Channel provider name and optional config
* (colon-delimited)
*
* @return ChannelProviderInterface
*/
protected function getChannelProvider($providerId)
{
// The provider ID consists of a service name and an optional config
// section -- break out the relevant parts:
list($serviceName, $configSection) = explode(':', $providerId . ':');
// Load configuration, using default value if necessary:
if (empty($configSection)) {
$configSection = "provider.$serviceName";
}
$options = isset($this->config->{$configSection})
? $this->config->{$configSection}->toArray() : [];
// Load the service, and configure appropriately:
$provider = $this->channelManager->get($serviceName);
$provider->setProviderId($providerId);
$provider->setOptions($options);
return $provider;
}
/**
* Generates static front page of channels.
*
* @param string $token Channel token (optional, used for AJAX fetching)
* @param string $activeChannel Channel being requested (optional, used w/ token)
* @param string $activeSource Search backend to use (null to use configured
* default).
*
* @return array
*/
public function getHomeContext($token = null, $activeChannel = null,
$activeSource = null
) {
// Load appropriate channel objects:
$defaultSource = $this->config->General->default_home_source
?? DEFAULT_SEARCH_BACKEND;
$source = $activeSource ?? $defaultSource;
$providers = $this->getChannelProviders($source, 'home', $activeChannel);
// Set up the cache, if appropriate:
if ($this->config->General->cache_home_channels ?? false) {
$providerIds = array_map('get_class', $providers);
$parts = [implode(',', $providerIds), $source, $token];
$cacheKey = md5(implode('-', $parts));
$cache = $this->cacheManager->getCache('object', 'homeChannels');
} else {
$cacheKey = false;
}
// Fetch channel data from cache, or populate cache if necessary:
if (!($channels = $cacheKey ? $cache->getItem($cacheKey) : false)) {
$results = $this->performChannelSearch([], $providers, $source);
$channels = $this->getChannelsFromResults($providers, $results, $token);
if ($cacheKey) {
$cache->setItem($cacheKey, $channels);
}
}
// Return context array:
return compact('token', 'channels');
}
/**
* Generates channels for a record.
*
* @param string $recordId Record ID to load
* @param string $token Channel token (optional, used for AJAX fetching)
* @param string $activeChannel Channel being requested (optional, used w/ token)
* @param string $source Search backend to use
*
* @return array
*/
public function getRecordContext($recordId, $token = null,
$activeChannel = null, $source = DEFAULT_SEARCH_BACKEND
) {
// Load record:
$driver = $this->recordLoader->load($recordId, $source);
// Load appropriate channel objects:
$providers = $this->getChannelProviders($source, 'record', $activeChannel);
// Collect details:
$channels = [];
foreach ($providers as $provider) {
$channels = array_merge(
$channels, $provider->getFromRecord($driver, $token)
);
}
// Return context array:
return compact('driver', 'channels', 'token');
}
/**
* Generates channels for a search.
*
* @param array $searchRequest Request parameters
* @param string $token Channel token (optional, used for AJAX fetching)
* @param string $activeChannel Channel being requested (optional, used w/ token)
* @param string $source Search backend to use
*
* @return array
*/
public function getSearchContext($searchRequest = [], $token = null,
$activeChannel = null, $source = DEFAULT_SEARCH_BACKEND
) {
// Load appropriate channel objects:
$providers = $this->getChannelProviders($source, 'search', $activeChannel);
// Perform search:
$results = $this->performChannelSearch($searchRequest, $providers, $source);
// Collect details:
$lookfor = $searchRequest['lookfor'] ?? null;
$channels = $this->getChannelsFromResults($providers, $results, $token);
// Return context array:
return compact('results', 'lookfor', 'channels', 'token');
}
}
<?php
/**
* Factory for channel loader.
*
* PHP version 5
*
* Copyright (C) Villanova University 2018.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Channels
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development Wiki
*/
namespace VuFind\ChannelProvider;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
/**
* Factory for channel loader.
*
* @category VuFind
* @package Channels
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development Wiki
*/
class ChannelLoaderFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container Service manager
* @param string $requestedName Service being created
* @param null|array $options Extra options (optional)
*
* @return object
*
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException if any other error occurs
*/
public function __invoke(ContainerInterface $container, $requestedName,
array $options = null
) {
if ($options !== null) {
throw new \Exception('Unexpected options sent to factory!');
}
return new $requestedName(
$container->get('VuFind\Config\PluginManager')->get('channels'),
$container->get('VuFind\Cache\Manager'),
$container->get('VuFind\ChannelProvider\PluginManager'),
$container->get('VuFind\Search\SearchRunner'),
$container->get('VuFind\Record\Loader')
);
}
}
......@@ -27,7 +27,7 @@
*/
namespace VuFind\Controller;
use Zend\Config\Config;
use VuFind\ChannelProvider\ChannelLoader;
/**
* Channels Class
......@@ -43,31 +43,20 @@ use Zend\Config\Config;
class ChannelsController extends AbstractBase
{
/**
* Retrieve channel information for the Channels/Home page.
* Channel loader
*
* @param array $providers Array of channel providers
* @param string $searchClassId Search class ID
* @param string $token Channel token
* @var ChannelLoader
*/
protected $loader;
/**
* Constructor
*
* @return array
* @param ChannelLoader $loader Channel loader
*/
protected function getHomeChannels($providers, $searchClassId, $token)
public function __construct(ChannelLoader $loader)
{
$callback = function ($runner, $params, $searchClassId) use ($providers) {
foreach ($providers as $provider) {
$provider->configureSearchParams($params);
}
};
$runner = $this->serviceLocator->get('VuFind\Search\SearchRunner');
$results = $runner->run([], $searchClassId, $callback);
$channels = [];
foreach ($providers as $provider) {
$channels = array_merge(
$channels, $provider->getFromSearch($results, $token)
);
}
return $channels;
$this->loader = $loader;
}
/**
......@@ -77,31 +66,11 @@ class ChannelsController extends AbstractBase
*/
public function homeAction()
{
$config = $this->getConfig('channels');
$defaultSearchClassId
= $config->General->default_home_source ?? DEFAULT_SEARCH_BACKEND;
$searchClassId = $this->params()->fromQuery('source', $defaultSearchClassId);
$providerIds = isset($config->{"source.$searchClassId"}->home)
? $config->{"source.$searchClassId"}->home->toArray() : [];
$providers = $this->getChannelProviderArray($providerIds, $config);
$source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND);
$activeChannel = $this->params()->fromQuery('channelProvider');
$token = $this->params()->fromQuery('channelToken');
if ($config->General->cache_home_channels ?? false) {
$parts = [implode(',', $providerIds), $searchClassId, $token];
$cacheKey = md5(implode('-', $parts));
$cache = $this->serviceLocator->get('VuFind\Cache\Manager')
->getCache('object', 'homeChannels');
} else {
$cacheKey = false;
}
$channels = $cacheKey ? $cache->getItem($cacheKey) : false;
if (!$channels) {
$channels = $this->getHomeChannels($providers, $searchClassId, $token);
if ($cacheKey) {
$cache->setItem($cacheKey, $channels);
}
}
return $this->createViewModel(compact('token', 'channels'));
$context = $this->loader->getHomeContext($token, $activeChannel, $source);
return $this->createViewModel($context);
}
/**
......@@ -111,25 +80,13 @@ class ChannelsController extends AbstractBase
*/
public function recordAction()
{
$view = $this->createViewModel();
$loader = $this->getRecordLoader();
$recordId = $this->params()->fromQuery('id');
$source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND);
$view->driver = $loader->load($this->params()->fromQuery('id'), $source);
$config = $this->getConfig('channels');
$providerIds = isset($config->{"source.$source"}->record)
? $config->{"source.$source"}->record->toArray() : [];
$view->channels = [];
$view->token = $this->params()->fromQuery('channelToken');
$providers = $this->getChannelProviderArray($providerIds, $config);
foreach ($providers as $provider) {
$view->channels = array_merge(
$view->channels,
$provider->getFromRecord($view->driver, $view->token)
);
}
return $view;
$activeChannel = $this->params()->fromQuery('channelProvider');
$token = $this->params()->fromQuery('channelToken');
$context = $this->loader
->getRecordContext($recordId, $token, $activeChannel, $source);
return $this->createViewModel($context);
}
/**
......@@ -139,89 +96,14 @@ class ChannelsController extends AbstractBase
*/
public function searchAction()
{
$view = $this->createViewModel();
$runner = $this->serviceLocator->get('VuFind\Search\SearchRunner');
// Send both GET and POST variables to search class:
$request = $this->getRequest()->getQuery()->toArray()
+ $this->getRequest()->getPost()->toArray();
$searchClassId = $this->params()
->fromQuery('source', DEFAULT_SEARCH_BACKEND);
$config = $this->getConfig('channels');
$providerIds = isset($config->{"source.$searchClassId"}->search)
? $config->{"source.$searchClassId"}->search->toArray() : [];
$providers = $this->getChannelProviderArray($providerIds, $config);
$callback = function ($runner, $params, $searchClassId) use ($providers) {
foreach ($providers as $provider) {
$provider->configureSearchParams($params);
}
};
$view->results = $runner->run($request, $searchClassId, $callback);
$view->channels = [];
$view->lookfor = $this->params()->fromQuery('lookfor');
$view->token = $this->params()->fromQuery('channelToken');
foreach ($providers as $provider) {
$view->channels = array_merge(
$view->channels,
$provider->getFromSearch($view->results, $view->token)
);
}
return $view;
}
/**
* Get an array of channel providers matching the provided IDs (or just one,
* if the channelProvider GET parameter is set).
*
* @param array $providerIds Array of IDs to load
* @param Config $config Channel configuration
*
* @return array
*/
protected function getChannelProviderArray($providerIds, $config)
{
$id = $this->params()->fromQuery('channelProvider');
if (!empty($id) && in_array($id, $providerIds)) {
return [$this->getChannelProvider($id, $config)];
}
$results = [];
foreach ($providerIds as $id) {
$results[] = $this->getChannelProvider($id, $config);
}
return $results;
}
/**
* Convenience method to retrieve a channel provider.
*
* @param string $providerId Channel provider name and optional config
* (colon-delimited)
* @param Config $config Channel configuration
*
* @return \VuFind\ChannelProvider\ChannelProviderInterface
*/
protected function getChannelProvider($providerId, Config $config)
{
// The provider ID consists of a service name and an optional config
// section -- break out the relevant parts:
list($serviceName, $configSection) = explode(':', $providerId . ':');
// Load configuration, using default value if necessary:
if (empty($configSection)) {
$configSection = "provider.$serviceName";
}
$options = isset($config->{$configSection})
? $config->{$configSection}->toArray() : [];
// Load the service, and configure appropriately:
$provider = $this->serviceLocator
->get('VuFind\ChannelProvider\PluginManager')->get($serviceName);
$provider->setProviderId($providerId);
$provider->setOptions($options);
return $provider;
$source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND);
$activeChannel = $this->params()->fromQuery('channelProvider');
$token = $this->params()->fromQuery('channelToken');
$context = $this->loader
->getSearchContext($request, $token, $activeChannel, $source);
return $this->createViewModel($context);
}
}
<?php
/**
* Channels controller factory.
*
* PHP version 7
*
* Copyright (C) Villanova University 2018.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* @category VuFind
* @package Controller
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development Wiki
*/
namespace VuFind\Controller;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\Factory\FactoryInterface;
/**
* Channels controller factory.
*
* @category VuFind
* @package Controller
* @author Demian Katz <demian.katz@villanova.edu>
* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License
* @link https://vufind.org/wiki/development Wiki
*/
class ChannelsControllerFactory implements FactoryInterface
{
/**
* Create an object
*
* @param ContainerInterface $container Service manager
* @param string $requestedName Service being created
* @param null|array $options Extra options (optional)
*
* @return object
*
* @throws ServiceNotFoundException if unable to resolve the service.
* @throws ServiceNotCreatedException if an exception is raised when
* creating a service.
* @throws ContainerException if any other error occurs
*/
public function __invoke(ContainerInterface $container, $requestedName,
array $options = null
) {
if (!empty($options)) {
throw new \Exception('Unexpected options sent to factory.');
}
$loader = $container->get('VuFind\ChannelProvider\ChannelLoader');
return new $requestedName($loader);
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment