[Developer] Proxy searcher

TickTackk

Well-known member
Currently the XF\Searcher\Thread only allows to search the threads based on the thread criteria (as it should) but after implementing the following changes to \XF\Searcher\AbstractSearcher to implement "proxy searcher" feature:
Diff:
diff --git a/upload/src/XF/Searcher/AbstractSearcher.php b/upload/src/XF/Searcher/AbstractSearcher.php
--- a/upload/src/XF/Searcher/AbstractSearcher.php
+++ b/upload/src/XF/Searcher/AbstractSearcher.php
@@ -29,12 +29,37 @@ abstract class AbstractSearcher
 
     protected $order = [];
 
+    /**
+     * These are in <relation name> and <searcher class> format
+     *
+     * @var array<string, string>
+     */
+    protected $proxySearcherMaps = [];
+
+    /**
+     * These are in in <relation name> and <searcher> format
+     *
+     * @var array<string, AbstractSearcher>
+     */
+    protected $proxySearchers = [];
+
+    /**
+     * @var Finder|null
+     */
+    protected $parentFinder = null;
+
+    /**
+     * @var string|null
+     */
+    protected $parentFinderRelationPath = null;
+
     public function __construct(Manager $em, array $criteria = null)
     {
         $this->em = $em;
         $this->structure = $em->getEntityStructure($this->getEntityType());
         $this->orderOptions = $this->getDefaultOrderOptions();
 
+        $this->initProxySearchers();
         $this->init();
 
         if ($criteria)
@@ -52,12 +77,29 @@ abstract class AbstractSearcher
      */
     abstract protected function getDefaultOrderOptions();
 
+    protected function initProxySearchers()
+    {
+        foreach ($this->proxySearcherMaps AS $relation => $identifier)
+        {
+            $this->proxySearchers[$relation] = \XF::app()->searcher($identifier, []);
+        }
+    }
+
     protected function init()
     {
     }
 
     public function setCriteria(array $criteria)
     {
+        foreach ($this->proxySearcherMaps AS $relation => $null)
+        {
+            if (!empty($criteria[$relation]))
+            {
+                $this->proxySearchers[$relation]->setCriteria($criteria[$relation]);
+                unset($criteria[$relation]);
+            }
+        }
+
         $this->rawCriteria = $criteria;
         $this->filteredCriteria = $this->filterCriteria($criteria);
     }
@@ -181,6 +223,20 @@ abstract class AbstractSearcher
         return $this->order;
     }
 
+    /**
+     * @param Finder $parentFinder
+     * @param string $parentFinderRelationPath
+     *
+     * @return $this
+     */
+    public function setParentFinder(Finder $parentFinder, $parentFinderRelationPath) : self
+    {
+        $this->parentFinder = $parentFinder;
+        $this->parentFinderRelationPath = $parentFinderRelationPath;
+
+        return $this;
+    }
+
     protected function filterCriteria(array $criteria, $relation = null)
     {
         if ($relation === null)
@@ -363,6 +419,7 @@ abstract class AbstractSearcher
     public function getFinder()
     {
         $finder = $this->em->getFinder($this->getEntityType());
+        $this->applyParentFinder($finder);
         $this->applyDefaultFinderLimits($finder);
         $this->applyCriteria($finder, $this->filteredCriteria);
 
@@ -371,9 +428,41 @@ abstract class AbstractSearcher
             $finder->setDefaultOrder($this->order);
         }
 
+        foreach ($this->proxySearchers AS $relation => $proxySearcher)
+        {
+            // getFinder() needs to be called in order to be apply the conditions
+            $proxySearcher->setParentFinder($finder, $relation)->getFinder();
+        }
+
         return $finder;
     }
 
+    /**
+     * @return Finder|null
+     */
+    public function getParentFinder()
+    {
+        return $this->parentFinder;
+    }
+
+    /**
+     * @return string|null
+     */
+    public function getParentFinderRelationPath()
+    {
+        return $this->parentFinderRelationPath;
+    }
+
+    protected function applyParentFinder(Finder $finder)
+    {
+        $parentFinder = $this->getParentFinder();
+        $relationPath = $this->getParentFinderRelationPath();
+        if ($parentFinder !== null && $relationPath !== null)
+        {
+            $finder->setParentFinder($parentFinder, $relationPath);
+        }
+    }
+
     protected function applyDefaultFinderLimits(\XF\Mvc\Entity\Finder $finder)
     {
     }

Developers would be able to do something like this:
PHP:
$contentSearcher = \XF::app()->searcher('Iam\Running:OutOfIdeas');
$contentSearcher->setCriteria([
    'some_column' => 'naaah',
    'Starter' => ['secondary_group_ids' => [69]],
    'LastReplier' => ['secondary_group_ids' => [420]]
]);
$contentSearcher->getFinder();
and XF:User searcher will automagically apply its condition checks on the finder we get from $contentSearcher automagically.

pls accept this pr because it took me more time to explain what the diff will do than actually figuring everything out :'(
 
Upvote 2
Top