From 814406b6bb459c0ea354a06970cadd2af9c5005e Mon Sep 17 00:00:00 2001
From: Demian Katz <demian.katz@villanova.edu>
Date: Tue, 16 Apr 2013 15:09:42 -0400
Subject: [PATCH] Centralized record loading in search service/record Loader.
 Eliminated getRecord/getRecords in Results classes.

---
 module/VuFind/config/module.config.php        | 32 +++++---
 module/VuFind/src/VuFind/Record/Loader.php    | 77 ++++++++-----------
 .../VuFind/src/VuFind/Search/Base/Results.php | 37 ---------
 .../src/VuFind/Search/EmptySet/Results.php    | 13 ----
 .../src/VuFind/Search/Favorites/Results.php   | 14 ----
 .../src/VuFind/Search/MixedList/Results.php   | 14 ----
 .../VuFind/src/VuFind/Search/Solr/Results.php | 53 -------------
 .../src/VuFind/Search/Summon/Results.php      | 21 -----
 .../VuFind/src/VuFind/Search/Tags/Results.php | 14 ----
 .../src/VuFind/Search/WorldCat/Results.php    | 21 -----
 .../src/VuFindSearch/Backend/Solr/Backend.php | 42 +++++++++-
 .../Feature/RetrieveBatchInterface.php        | 58 ++++++++++++++
 .../VuFindSearch/src/VuFindSearch/Service.php | 41 ++++++++++
 13 files changed, 190 insertions(+), 247 deletions(-)
 create mode 100644 module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php

diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php
index a135c84a797..c098ec11ace 100644
--- a/module/VuFind/config/module.config.php
+++ b/module/VuFind/config/module.config.php
@@ -228,6 +228,12 @@ $config = array(
                 return $logger;
             },
             'VuFind\Mailer' => 'VuFind\Mailer\Factory',
+            'VuFind\RecordLoader' => function ($sm) {
+                return new \VuFind\Record\Loader(
+                    $sm->get('VuFind\Search'),
+                    $sm->get('VuFind\RecordDriverPluginManager')
+                );
+            },
             'VuFind\RecordRouter' => function ($sm) {
                 return new \VuFind\Record\Router(
                     $sm->get('VuFind\RecordLoader'),
@@ -239,6 +245,17 @@ $config = array(
                     $sm->get('VuFind\Config')->get('config')
                 );
             },
+            'VuFind\Search\BackendManager' => function ($sm) {
+                $config = $sm->get('config');
+                $smConfig = new \Zend\ServiceManager\Config(
+                    $config['vufind']['plugin_managers']['search_backend']
+                );
+                $registry = $sm->createScopedServiceManager();
+                $smConfig->configureServiceManager($registry);
+                $manager  = new \VuFind\Search\BackendManager($registry);
+
+                return $manager;
+            },
             'VuFind\SearchSpecsReader' => function ($sm) {
                 return new \VuFind\Config\SearchSpecsReader(
                     $sm->get('VuFind\CacheManager')
@@ -288,20 +305,8 @@ $config = array(
                     ? $config->WorldCat->id : false;
                 return new \VuFind\Connection\WorldCatUtils($wcId);
             },
-            'VuFind\Search\BackendManager' => function ($sm) {
-                $config = $sm->get('config');
-                $smConfig = new \Zend\ServiceManager\Config(
-                    $config['vufind']['plugin_managers']['search_backend']
-                );
-                $registry = $sm->createScopedServiceManager();
-                $smConfig->configureServiceManager($registry);
-                $manager  = new \VuFind\Search\BackendManager($registry);
-
-                return $manager;
-            },
         ),
         'invokables' => array(
-            'VuFind\RecordLoader' => 'VuFind\Record\Loader',
             'VuFind\SessionManager' => 'Zend\Session\SessionManager',
         ),
         'initializers' => array(
@@ -792,10 +797,13 @@ $config = array(
                     'WorldCat' => 'VuFind\Search\Factory\WorldCatBackendFactory',
                 ),
                 'aliases' => array(
+                    // Allow Solr core names to be used as aliases for services:
                     'authority' => 'SolrAuth',
                     'biblio' => 'Solr',
                     'reserves' => 'SolrReserves',
                     'stats' => 'SolrStats',
+                    // Legacy:
+                    'VuFind' => 'Solr',
                 )
             ),
             'search_options' => array(
diff --git a/module/VuFind/src/VuFind/Record/Loader.php b/module/VuFind/src/VuFind/Record/Loader.php
index 9c6ae70f292..e5a7c1cff7d 100644
--- a/module/VuFind/src/VuFind/Record/Loader.php
+++ b/module/VuFind/src/VuFind/Record/Loader.php
@@ -26,8 +26,9 @@
  * @link     http://vufind.org   Main Site
  */
 namespace VuFind\Record;
-use Zend\ServiceManager\ServiceLocatorAwareInterface,
-    Zend\ServiceManager\ServiceLocatorInterface;
+use VuFind\Exception\RecordMissing as RecordMissingException,
+    VuFind\RecordDriver\PluginManager as RecordFactory,
+    VuFindSearch\Service as SearchService;
 
 /**
  * Record loader
@@ -38,32 +39,33 @@ use Zend\ServiceManager\ServiceLocatorAwareInterface,
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://vufind.org   Main Site
  */
-class Loader implements ServiceLocatorAwareInterface
+class Loader
 {
     /**
-     * Service locator
+     * Record factory
      *
-     * @var ServiceLocatorInterface
+     * @var RecordFactory
      */
-    protected $serviceLocator;
+    protected $recordFactory;
 
     /**
-     * Given a record source, return the search object that can load that type of
-     * record.
+     * Search service
      *
-     * @param string $source Record source
+     * @var SearchService
+     */
+    protected $searchService;
+
+    /**
+     * Constructor
      *
-     * @throws \Exception
-     * @return \VuFind\Search\Base\Results
+     * @param SearchService $searchService Search service
+     * @param RecordFactory $recordFactory Record loader
      */
-    protected function getClassForSource($source)
-    {
-        // Legacy hack: translate "VuFind" source from database into "Solr":
-        if ($source == 'VuFind') {
-            $source = 'Solr';
-        }
-        return $this->getServiceLocator()->get('VuFind\SearchResultsPluginManager')
-            ->get($source);
+    public function __construct(SearchService $searchService,
+        RecordFactory $recordFactory
+    ) {
+        $this->searchService = $searchService;
+        $this->recordFactory = $recordFactory;
     }
 
     /**
@@ -77,7 +79,13 @@ class Loader implements ServiceLocatorAwareInterface
      */
     public function load($id, $source = 'VuFind')
     {
-        return $this->getClassForSource($source)->getRecord($id);
+        $results = $this->searchService->retrieve($source, $id)->getRecords();
+        if (count($results) < 1) {
+            throw new RecordMissingException(
+                'Record ' . $source . ':' . $id . ' does not exist.'
+            );
+        }
+        return $results[0];
     }
 
     /**
@@ -92,7 +100,7 @@ class Loader implements ServiceLocatorAwareInterface
      */
      public function loadBatchForSource($ids, $source = 'VuFind')
      {
-         return $this->getClassForSource($source)->getRecords($ids);
+         return $this->searchService->retrieveBatch($source, $ids)->getRecords();
      }
 
     /**
@@ -143,9 +151,7 @@ class Loader implements ServiceLocatorAwareInterface
                 $fields = isset($details['extra_fields'])
                     ? $details['extra_fields'] : array();
                 $fields['id'] = $details['id'];
-                $factory = $this->getServiceLocator()
-                    ->get('VuFind\RecordDriverPluginManager');
-                $retVal[$i] = $factory->get('Missing');
+                $retVal[$i] = $this->recordFactory->get('Missing');
                 $retVal[$i]->setRawData($fields);
                 $retVal[$i]->setSourceIdentifier($details['source']);
             }
@@ -155,27 +161,4 @@ class Loader implements ServiceLocatorAwareInterface
         ksort($retVal);
         return $retVal;
     }
-
-    /**
-     * Set the service locator.
-     *
-     * @param ServiceLocatorInterface $serviceLocator Locator to register
-     *
-     * @return Manager
-     */
-    public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
-    {
-        $this->serviceLocator = $serviceLocator;
-        return $this;
-    }
-
-    /**
-     * Get the service locator.
-     *
-     * @return \Zend\ServiceManager\ServiceLocatorInterface
-     */
-    public function getServiceLocator()
-    {
-        return $this->serviceLocator;
-    }
 }
diff --git a/module/VuFind/src/VuFind/Search/Base/Results.php b/module/VuFind/src/VuFind/Search/Base/Results.php
index c8e707a8398..21d65f4495b 100644
--- a/module/VuFind/src/VuFind/Search/Base/Results.php
+++ b/module/VuFind/src/VuFind/Search/Base/Results.php
@@ -196,43 +196,6 @@ abstract class Results implements ServiceLocatorAwareInterface
      */
     abstract protected function performSearch();
 
-    /**
-     * Method to retrieve a record by ID.  Returns a record driver object.
-     *
-     * @param string $id Unique identifier of record
-     *
-     * @return \VuFind\RecordDriver\AbstractBase
-     */
-    public function getRecord($id)
-    {
-        // This needs to be defined in subclasses:
-        throw new \Exception('getRecord needs to be defined.');
-    }
-
-    /**
-     * Method to retrieve an array of records by ID.
-     *
-     * @param array $ids Array of unique record identifiers.
-     *
-     * @return array
-     */
-    public function getRecords($ids)
-    {
-        // This is the default, dumb behavior for retrieving multiple records --
-        // just call getRecord() repeatedly.  For efficiency, this method should
-        // be overridden in child classes when possible to reduce API calls, etc.
-        $retVal = array();
-        foreach ($ids as $id) {
-            try {
-                $retVal[] = $this->getRecord($id);
-            } catch (\Exception $e) {
-                // Just omit missing records from the return array; calling code in
-                // \VuFind\Record\Loader::loadBatch() will deal with this.
-            }
-        }
-        return $retVal;
-    }
-
     /**
      * Get spelling suggestion information.
      *
diff --git a/module/VuFind/src/VuFind/Search/EmptySet/Results.php b/module/VuFind/src/VuFind/Search/EmptySet/Results.php
index ac39d259255..0fa94845a42 100644
--- a/module/VuFind/src/VuFind/Search/EmptySet/Results.php
+++ b/module/VuFind/src/VuFind/Search/EmptySet/Results.php
@@ -63,17 +63,4 @@ class Results extends BaseResults
     {
         return array();
     }
-
-    /**
-     * Method to retrieve a record by ID.  Returns a record driver object.
-     *
-     * @param string $id Unique identifier of record
-     *
-     * @throws \Exception
-     * @return \VuFind\RecordDriver\AbstractBase
-     */
-    public function getRecord($id)
-    {
-        throw new \Exception('Cannot get record from empty set.');
-    }
 }
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Search/Favorites/Results.php b/module/VuFind/src/VuFind/Search/Favorites/Results.php
index 9873bdfbc23..1fa64498733 100644
--- a/module/VuFind/src/VuFind/Search/Favorites/Results.php
+++ b/module/VuFind/src/VuFind/Search/Favorites/Results.php
@@ -172,20 +172,6 @@ class Results extends BaseResults
             ->loadBatch($recordsToRequest);
     }
 
-    /**
-     * Method to retrieve a record by ID.  Returns a record driver object.
-     *
-     * @param string $id Unique identifier of record
-     *
-     * @return \VuFind\RecordDriver\Base
-     */
-    public function getRecord($id)
-    {
-        throw new \Exception(
-            'getRecord not supported by VuFind\Search\Favorites\Results'
-        );
-    }
-
     /**
      * Get an array of tags being applied as filters.
      *
diff --git a/module/VuFind/src/VuFind/Search/MixedList/Results.php b/module/VuFind/src/VuFind/Search/MixedList/Results.php
index 60184a73c72..0b06e65d382 100644
--- a/module/VuFind/src/VuFind/Search/MixedList/Results.php
+++ b/module/VuFind/src/VuFind/Search/MixedList/Results.php
@@ -66,18 +66,4 @@ class Results extends BaseResults
             ->loadBatch($recordsToRequest);
         $this->resultTotal = count($this->results);
     }
-
-    /**
-     * Method to retrieve a record by ID.  Returns a record driver object.
-     *
-     * @param string $id Unique identifier of record
-     *
-     * @return \VuFind\RecordDriver\Base
-     */
-    public function getRecord($id)
-    {
-        throw new \Exception(
-            'getRecord not supported by VuFind\Search\MixedList\Results'
-        );
-    }
 }
\ No newline at end of file
diff --git a/module/VuFind/src/VuFind/Search/Solr/Results.php b/module/VuFind/src/VuFind/Search/Solr/Results.php
index 304a6f367f0..eacd6b139b5 100644
--- a/module/VuFind/src/VuFind/Search/Solr/Results.php
+++ b/module/VuFind/src/VuFind/Search/Solr/Results.php
@@ -414,59 +414,6 @@ class Results extends BaseResults
         return $list;
     }
 
-    /**
-     * Method to retrieve a record by ID.  Returns a record driver object.
-     *
-     * @param string $id Unique identifier of record
-     *
-     * @throws RecordMissingException
-     * @return \VuFind\RecordDriver\Base
-     */
-    public function getRecord($id)
-    {
-        $collection = $this->getSearchService()->retrieve($this->backendId, $id);
-
-        if (count($collection) == 0) {
-            throw new RecordMissingException(
-                'Record ' . $id . ' does not exist.'
-            );
-        }
-
-        return current($collection->getRecords());
-    }
-
-    /**
-     * Method to retrieve an array of records by ID.
-     *
-     * @param array $ids Array of unique record identifiers.
-     *
-     * @return array
-     */
-    public function getRecords($ids)
-    {
-        // Figure out how many records to retrieve at the same time --
-        // we'll use either 100 or the ID request limit, whichever is smaller.
-        $results = $this->getServiceLocator()
-            ->get('VuFind\SearchResultsPluginManager')->get('Solr');
-        $params = $results->getParams();
-        $pageSize = $params->getQueryIDLimit();
-        if ($pageSize < 1 || $pageSize > 100) {
-            $pageSize = 100;
-        }
-        $params->setLimit($pageSize);
-
-        // Retrieve records a page at a time:
-        $retVal = array();
-        while (count($ids) > 0) {
-            $currentPage = array_splice($ids, 0, $pageSize, array());
-            $params->setQueryIDs($currentPage);
-            $results->performAndProcessSearch();
-            $retVal = array_merge($retVal, $results->getResults());
-        }
-
-        return $retVal;
-    }
-
     /**
      * Method to retrieve records similar to the provided ID.  Returns an
      * array of record driver objects.
diff --git a/module/VuFind/src/VuFind/Search/Summon/Results.php b/module/VuFind/src/VuFind/Search/Summon/Results.php
index 7fb5ea9ffec..87a769d115c 100644
--- a/module/VuFind/src/VuFind/Search/Summon/Results.php
+++ b/module/VuFind/src/VuFind/Search/Summon/Results.php
@@ -107,27 +107,6 @@ class Results extends BaseResults
         $this->results = $collection->getRecords();
     }
 
-    /**
-     * Method to retrieve a record by ID.  Returns a record driver object.
-     *
-     * @param string $id Unique identifier of record
-     *
-     * @throws RecordMissingException
-     * @return \VuFind\RecordDriver\Base
-     */
-    public function getRecord($id)
-    {
-        $collection = $this->getSearchService()->retrieve('Summon', $id);
-
-        if (count($collection) == 0) {
-            throw new RecordMissingException(
-                'Record ' . $id . ' does not exist.'
-            );
-        }
-
-        return current($collection->getRecords());
-    }
-
     /**
      * Create search backend parameters for advanced features.
      *
diff --git a/module/VuFind/src/VuFind/Search/Tags/Results.php b/module/VuFind/src/VuFind/Search/Tags/Results.php
index 0d9c5c0cb68..8cbdeed777e 100644
--- a/module/VuFind/src/VuFind/Search/Tags/Results.php
+++ b/module/VuFind/src/VuFind/Search/Tags/Results.php
@@ -77,20 +77,6 @@ class Results extends BaseResults
             ->loadBatch($recordsToRequest);
     }
 
-    /**
-     * Method to retrieve a record by ID.  Returns a record driver object.
-     *
-     * @param string $id Unique identifier of record
-     *
-     * @return \VuFind\RecordDriver\Base
-     */
-    public function getRecord($id)
-    {
-        throw new \Exception(
-            'getRecord not supported by VuFind\Search\Tags\Results'
-        );
-    }
-
     /**
      * Returns the stored list of facets for the last search
      *
diff --git a/module/VuFind/src/VuFind/Search/WorldCat/Results.php b/module/VuFind/src/VuFind/Search/WorldCat/Results.php
index 3546555e9a2..349f5403f54 100644
--- a/module/VuFind/src/VuFind/Search/WorldCat/Results.php
+++ b/module/VuFind/src/VuFind/Search/WorldCat/Results.php
@@ -60,27 +60,6 @@ class Results extends \VuFind\Search\Base\Results
         $this->results = $collection->getRecords();
     }
 
-    /**
-     * Method to retrieve a record by ID.  Returns a record driver object.
-     *
-     * @param string $id Unique identifier of record
-     *
-     * @throws RecordMissingException
-     * @return \VuFind\RecordDriver\Base
-     */
-    public function getRecord($id)
-    {
-        $collection = $this->getSearchService()->retrieve('WorldCat', $id);
-
-        if (count($collection) == 0) {
-            throw new RecordMissingException(
-                'Record ' . $id . ' does not exist.'
-            );
-        }
-
-        return current($collection->getRecords());
-    }
-
     /**
      * Returns the stored list of facets for the last search
      *
diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php
index c17e15286bc..e7930938bec 100644
--- a/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php
+++ b/module/VuFindSearch/src/VuFindSearch/Backend/Solr/Backend.php
@@ -28,6 +28,7 @@
 namespace VuFindSearch\Backend\Solr;
 
 use VuFindSearch\Query\AbstractQuery;
+use VuFindSearch\Query\Query;
 
 use VuFindSearch\ParamBag;
 
@@ -38,6 +39,7 @@ use VuFindSearch\Backend\Solr\Response\Json\Terms;
 
 use VuFindSearch\Backend\BackendInterface;
 use VuFindSearch\Feature\MoreLikeThis;
+use VuFindSearch\Feature\RetrieveBatchInterface;
 
 use Zend\Log\LoggerInterface;
 
@@ -55,7 +57,7 @@ use VuFindSearch\Exception\InvalidArgumentException;
  * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
  * @link     http://vufind.org
  */
-class Backend implements BackendInterface, MoreLikeThis
+class Backend implements BackendInterface, MoreLikeThis, RetrieveBatchInterface
 {
     /**
      * Record collection factory.
@@ -215,6 +217,44 @@ class Backend implements BackendInterface, MoreLikeThis
         return $collection;
     }
 
+    /**
+     * Retrieve a batch of documents.
+     *
+     * @param array    $ids    Array of document identifiers
+     * @param ParamBag $params Search backend parameters
+     *
+     * @return \VuFindSearch\Response\RecordCollectionInterface
+     */
+    public function retrieveBatch($ids, ParamBag $params = null)
+    {
+        // Load 100 records at a time; this is a good number to avoid memory
+        // problems while still covering a lot of ground.
+        $pageSize = 100;
+
+        // Callback function for formatting IDs:
+        $formatIds = function ($i) {
+            return '"' . addcslashes($i, '"') . '"';
+        };
+
+        // Retrieve records a page at a time:
+        $results = false;
+        while (count($ids) > 0) {
+            $currentPage = array_splice($ids, 0, $pageSize, array());
+            $currentPage = array_map($formatIds, $currentPage);
+            $query = new Query('id:(' . implode(' OR ', $currentPage) . ')');
+            $next = $this->search($query, 0, $pageSize);
+            if (!$results) {
+                $results = $next;
+            } else {
+                foreach ($next->getRecords() as $record) {
+                    $results->add($record);
+                }
+            }
+        }
+
+        return $results;
+    }
+
     /**
      * Return similar records.
      *
diff --git a/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php b/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php
new file mode 100644
index 00000000000..75704dda17e
--- /dev/null
+++ b/module/VuFindSearch/src/VuFindSearch/Feature/RetrieveBatchInterface.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * Optional backend feature: retrieve batch of records.
+ *
+ * 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\Feature;
+
+use VuFindSearch\Query\AbstractQuery;
+use VuFindSearch\Query\QueryGroup;
+use VuFindSearch\Query\Query;
+
+use VuFindSearch\ParamBag;
+
+/**
+ * Optional backend feature: retrieve batch of records.
+ *
+ * @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
+ */
+interface RetrieveBatchInterface
+{
+    /**
+     * Retrieve a batch of documents.
+     *
+     * @param array    $ids    Array of document identifiers
+     * @param ParamBag $params Search backend parameters
+     *
+     * @return \VuFindSearch\Response\RecordCollectionInterface
+     */
+    public function retrieveBatch($ids, ParamBag $params = null);
+}
\ No newline at end of file
diff --git a/module/VuFindSearch/src/VuFindSearch/Service.php b/module/VuFindSearch/src/VuFindSearch/Service.php
index f4ae33d5b28..99bd058f218 100644
--- a/module/VuFindSearch/src/VuFindSearch/Service.php
+++ b/module/VuFindSearch/src/VuFindSearch/Service.php
@@ -30,6 +30,7 @@
 namespace VuFindSearch;
 
 use VuFindSearch\Backend\BackendInterface;
+use VuFindSearch\Feature\RetrieveBatchInterface;
 
 use Zend\Log\LoggerInterface;
 use Zend\EventManager\EventManagerInterface;
@@ -127,6 +128,46 @@ class Service
         return $response;
     }
 
+    /**
+     * Retrieve a batch of records.
+     *
+     * @param string   $backend Search backend identifier
+     * @param array    $ids     Record identifier
+     * @param ParamBag $params  Search backend parameters
+     *
+     * @return ResponseInterface
+     */
+    public function retrieveBatch($backend, $ids, ParamBag $params = null)
+    {
+        $params  = $params ?: new ParamBag();
+        $context = __FUNCTION__;
+        $args = compact('backend', 'id', 'params', 'context');
+        $backend = $this->resolve($backend, $args);
+        $args['backend_instance'] = $backend;
+
+        $this->triggerPre($backend, $args);
+
+        // If the backend implements the RetrieveBatchInterface, we can load
+        // all the records at once; otherwise, we need to load them one at a
+        // time and aggregate them:
+        if ($backend instanceof RetrieveBatchInterface) {
+            $response = $backend->retrieveBatch($ids, $params);
+        } else {
+            $response = false;
+            foreach ($ids as $id) {
+                $next = $backend->retrieve($id, $params);
+                if (!$response) {
+                    $response = $next;
+                } else {
+                    $response->add($next->first());
+                }
+            }
+        }
+
+        $this->triggerPost($response, $args);
+        return $response;
+    }
+
     /**
      * Return similar records.
      *
-- 
GitLab