diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index 84f123e8d96cd46123c20afe106790c34106dc8d..5d2e347e77b16cb35c22187b86c46af35936840e 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -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',
diff --git a/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php
new file mode 100644
index 0000000000000000000000000000000000000000..df9f225d300ac995b8552edef0a96e2f0dc46d0d
--- /dev/null
+++ b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php
@@ -0,0 +1,291 @@
+<?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');
+    }
+}
diff --git a/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..e5204d9d1d1de5e1206ef89d88296db953f5e739
--- /dev/null
+++ b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php
@@ -0,0 +1,72 @@
+<?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')
+        );
+    }
+}
diff --git a/module/VuFind/src/VuFind/Controller/ChannelsController.php b/module/VuFind/src/VuFind/Controller/ChannelsController.php
index e29414c0c58939d90ac3ca057728fb85661c10e0..d60077c81cd7ed8bcaf58c52bc58be9bf6900a32 100644
--- a/module/VuFind/src/VuFind/Controller/ChannelsController.php
+++ b/module/VuFind/src/VuFind/Controller/ChannelsController.php
@@ -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);
     }
 }
diff --git a/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php b/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php
new file mode 100644
index 0000000000000000000000000000000000000000..72fb8015b4701bf014c80c70c84d119377424c8b
--- /dev/null
+++ b/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php
@@ -0,0 +1,67 @@
+<?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);
+    }
+}