mirror of
https://github.com/flarum/framework.git
synced 2024-11-26 02:10:09 +08:00
Simple Flarum Search Extender and tests (#2483)
This commit is contained in:
parent
458ae2bfbe
commit
4661de4ddc
|
@ -12,6 +12,9 @@ namespace Flarum\Discussion\Event;
|
||||||
use Flarum\Discussion\Search\DiscussionSearch;
|
use Flarum\Discussion\Search\DiscussionSearch;
|
||||||
use Flarum\Search\SearchCriteria;
|
use Flarum\Search\SearchCriteria;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated beta 16, remove beta 17
|
||||||
|
*/
|
||||||
class Searching
|
class Searching
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,21 +11,16 @@ namespace Flarum\Discussion\Search;
|
||||||
|
|
||||||
use Flarum\Discussion\DiscussionRepository;
|
use Flarum\Discussion\DiscussionRepository;
|
||||||
use Flarum\Discussion\Event\Searching;
|
use Flarum\Discussion\Event\Searching;
|
||||||
use Flarum\Search\ApplySearchParametersTrait;
|
use Flarum\Search\AbstractSearch;
|
||||||
|
use Flarum\Search\AbstractSearcher;
|
||||||
use Flarum\Search\GambitManager;
|
use Flarum\Search\GambitManager;
|
||||||
use Flarum\Search\SearchCriteria;
|
use Flarum\Search\SearchCriteria;
|
||||||
use Flarum\Search\SearchResults;
|
use Flarum\User\User;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
class DiscussionSearcher
|
class DiscussionSearcher extends AbstractSearcher
|
||||||
{
|
{
|
||||||
use ApplySearchParametersTrait;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var GambitManager
|
|
||||||
*/
|
|
||||||
protected $gambits;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var DiscussionRepository
|
* @var DiscussionRepository
|
||||||
*/
|
*/
|
||||||
|
@ -37,53 +32,36 @@ class DiscussionSearcher
|
||||||
protected $events;
|
protected $events;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param GambitManager $gambits
|
|
||||||
* @param DiscussionRepository $discussions
|
* @param DiscussionRepository $discussions
|
||||||
* @param Dispatcher $events
|
* @param Dispatcher $events
|
||||||
|
* @param GambitManager $gambits
|
||||||
|
* @param array $searchMutators
|
||||||
*/
|
*/
|
||||||
public function __construct(GambitManager $gambits, DiscussionRepository $discussions, Dispatcher $events)
|
public function __construct(DiscussionRepository $discussions, Dispatcher $events, GambitManager $gambits, array $searchMutators)
|
||||||
{
|
{
|
||||||
$this->gambits = $gambits;
|
parent::__construct($gambits, $searchMutators);
|
||||||
|
|
||||||
$this->discussions = $discussions;
|
$this->discussions = $discussions;
|
||||||
$this->events = $events;
|
$this->events = $events;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function getQuery(User $actor): Builder
|
||||||
* @param SearchCriteria $criteria
|
|
||||||
* @param int|null $limit
|
|
||||||
* @param int $offset
|
|
||||||
*
|
|
||||||
* @return SearchResults
|
|
||||||
*/
|
|
||||||
public function search(SearchCriteria $criteria, $limit = null, $offset = 0)
|
|
||||||
{
|
{
|
||||||
$actor = $criteria->actor;
|
return $this->discussions->query()->select('discussions.*')->whereVisibleTo($actor);
|
||||||
|
}
|
||||||
|
|
||||||
$query = $this->discussions->query()->select('discussions.*')->whereVisibleTo($actor);
|
protected function getSearch(Builder $query, User $actor): AbstractSearch
|
||||||
|
{
|
||||||
|
return new DiscussionSearch($query->getQuery(), $actor);
|
||||||
|
}
|
||||||
|
|
||||||
// Construct an object which represents this search for discussions.
|
/**
|
||||||
// Apply gambits to it, sort, and paging criteria. Also give extensions
|
* @deprecated along with the Searching event, remove in Beta 17.
|
||||||
// an opportunity to modify it.
|
*/
|
||||||
$search = new DiscussionSearch($query->getQuery(), $actor);
|
protected function mutateSearch(AbstractSearch $search, SearchCriteria $criteria)
|
||||||
|
{
|
||||||
$this->gambits->apply($search, $criteria->query);
|
parent::mutateSearch($search, $criteria);
|
||||||
$this->applySort($search, $criteria->sort);
|
|
||||||
$this->applyOffset($search, $offset);
|
|
||||||
$this->applyLimit($search, $limit + 1);
|
|
||||||
|
|
||||||
$this->events->dispatch(new Searching($search, $criteria));
|
$this->events->dispatch(new Searching($search, $criteria));
|
||||||
|
|
||||||
// Execute the search query and retrieve the results. We get one more
|
|
||||||
// results than the user asked for, so that we can say if there are more
|
|
||||||
// results. If there are, we will get rid of that extra result.
|
|
||||||
$discussions = $query->get();
|
|
||||||
|
|
||||||
$areMoreResults = $limit > 0 && $discussions->count() > $limit;
|
|
||||||
|
|
||||||
if ($areMoreResults) {
|
|
||||||
$discussions->pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SearchResults($discussions, $areMoreResults);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ namespace Flarum\Event;
|
||||||
|
|
||||||
use Flarum\Search\GambitManager;
|
use Flarum\Search\GambitManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated beta 16, removed in beta 17
|
||||||
|
*/
|
||||||
abstract class AbstractConfigureGambits
|
abstract class AbstractConfigureGambits
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
namespace Flarum\Event;
|
namespace Flarum\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated beta 16, removed in beta 17
|
||||||
|
*/
|
||||||
class ConfigureDiscussionGambits extends AbstractConfigureGambits
|
class ConfigureDiscussionGambits extends AbstractConfigureGambits
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
namespace Flarum\Event;
|
namespace Flarum\Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated beta 16, removed in beta 17
|
||||||
|
*/
|
||||||
class ConfigureUserGambits extends AbstractConfigureGambits
|
class ConfigureUserGambits extends AbstractConfigureGambits
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
99
framework/core/src/Extend/SimpleFlarumSearch.php
Normal file
99
framework/core/src/Extend/SimpleFlarumSearch.php
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Extend;
|
||||||
|
|
||||||
|
use Flarum\Extension\Extension;
|
||||||
|
use Illuminate\Contracts\Container\Container;
|
||||||
|
|
||||||
|
class SimpleFlarumSearch implements ExtenderInterface
|
||||||
|
{
|
||||||
|
private $fullTextGambit;
|
||||||
|
private $gambits = [];
|
||||||
|
private $searcher;
|
||||||
|
private $searchMutators = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $searcherClass: The ::class attribute of the Searcher you are modifying.
|
||||||
|
* This searcher must extend \Flarum\Search\AbstractSearcher.
|
||||||
|
*/
|
||||||
|
public function __construct($searcherClass)
|
||||||
|
{
|
||||||
|
$this->searcher = $searcherClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a gambit to this searcher. Gambits are used to filter search queries.
|
||||||
|
*
|
||||||
|
* @param string $gambitClass: The ::class attribute of the gambit you are adding.
|
||||||
|
* This gambit must extend \Flarum\Search\AbstractRegexGambit
|
||||||
|
*/
|
||||||
|
public function addGambit($gambitClass)
|
||||||
|
{
|
||||||
|
$this->gambits[] = $gambitClass;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the full text gambit for this searcher. The full text gambit actually executes the search.
|
||||||
|
*
|
||||||
|
* @param string $gambitClass: The ::class attribute of the full test gambit you are adding.
|
||||||
|
* This gambit must implement \Flarum\Search\GambitInterface
|
||||||
|
*/
|
||||||
|
public function setFullTextGambit($gambitClass)
|
||||||
|
{
|
||||||
|
$this->fullTextGambit = $gambitClass;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a callback through which to run all search queries after gambits have been applied.
|
||||||
|
*
|
||||||
|
* @param callable|string $callback
|
||||||
|
*
|
||||||
|
* The callback can be a closure or an invokable class, and should accept:
|
||||||
|
* - Flarum\Search\AbstractSearch $search
|
||||||
|
* - Flarum\Search\SearchCriteria $criteria
|
||||||
|
*/
|
||||||
|
public function addSearchMutator($callback)
|
||||||
|
{
|
||||||
|
$this->searchMutators[] = $callback;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function extend(Container $container, Extension $extension = null)
|
||||||
|
{
|
||||||
|
if (! is_null($this->fullTextGambit)) {
|
||||||
|
$container->resolving('flarum.simple_search.fulltext_gambits', function ($oldFulltextGambits) {
|
||||||
|
$oldFulltextGambits[$this->searcher] = $this->fullTextGambit;
|
||||||
|
|
||||||
|
return $oldFulltextGambits;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$container->extend('flarum.simple_search.gambits', function ($oldGambits) {
|
||||||
|
foreach ($this->gambits as $gambit) {
|
||||||
|
$oldGambits[$this->searcher][] = $gambit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oldGambits;
|
||||||
|
});
|
||||||
|
|
||||||
|
$container->extend('flarum.simple_search.search_mutators', function ($oldMutators) {
|
||||||
|
foreach ($this->searchMutators as $mutator) {
|
||||||
|
$oldMutators[$this->searcher][] = $mutator;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $oldMutators;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
81
framework/core/src/Search/AbstractSearcher.php
Normal file
81
framework/core/src/Search/AbstractSearcher.php
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Search;
|
||||||
|
|
||||||
|
use Flarum\User\User;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
abstract class AbstractSearcher
|
||||||
|
{
|
||||||
|
use ApplySearchParametersTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var GambitManager
|
||||||
|
*/
|
||||||
|
protected $gambits;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $searchMutators;
|
||||||
|
|
||||||
|
public function __construct(GambitManager $gambits, array $searchMutators)
|
||||||
|
{
|
||||||
|
$this->gambits = $gambits;
|
||||||
|
$this->searchMutators = $searchMutators;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract protected function getQuery(User $actor): Builder;
|
||||||
|
|
||||||
|
abstract protected function getSearch(Builder $query, User $actor): AbstractSearch;
|
||||||
|
|
||||||
|
protected function mutateSearch(AbstractSearch $search, SearchCriteria $criteria)
|
||||||
|
{
|
||||||
|
foreach ($this->searchMutators as $mutator) {
|
||||||
|
$mutator($search, $criteria);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param SearchCriteria $criteria
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int $offset
|
||||||
|
*
|
||||||
|
* @return SearchResults
|
||||||
|
*/
|
||||||
|
public function search(SearchCriteria $criteria, $limit = null, $offset = 0, array $load = [])
|
||||||
|
{
|
||||||
|
$actor = $criteria->actor;
|
||||||
|
|
||||||
|
$query = $this->getQuery($actor);
|
||||||
|
|
||||||
|
$search = $this->getSearch($query, $actor);
|
||||||
|
|
||||||
|
$this->gambits->apply($search, $criteria->query);
|
||||||
|
$this->applySort($search, $criteria->sort);
|
||||||
|
$this->applyOffset($search, $offset);
|
||||||
|
$this->applyLimit($search, $limit + 1);
|
||||||
|
|
||||||
|
$this->mutateSearch($search, $criteria);
|
||||||
|
|
||||||
|
// Execute the search query and retrieve the results. We get one more
|
||||||
|
// results than the user asked for, so that we can say if there are more
|
||||||
|
// results. If there are, we will get rid of that extra result.
|
||||||
|
$results = $query->get();
|
||||||
|
|
||||||
|
if ($areMoreResults = $limit > 0 && $results->count() > $limit) {
|
||||||
|
$results->pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
$results->load($load);
|
||||||
|
|
||||||
|
return new SearchResults($results, $areMoreResults);
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,6 @@
|
||||||
|
|
||||||
namespace Flarum\Search;
|
namespace Flarum\Search;
|
||||||
|
|
||||||
use Illuminate\Contracts\Container\Container;
|
|
||||||
use LogicException;
|
use LogicException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,33 +22,36 @@ class GambitManager
|
||||||
protected $gambits = [];
|
protected $gambits = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string
|
* @var GambitInterface
|
||||||
*/
|
*/
|
||||||
protected $fulltextGambit;
|
protected $fulltextGambit;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Container
|
|
||||||
*/
|
|
||||||
protected $container;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Container $container
|
|
||||||
*/
|
|
||||||
public function __construct(Container $container)
|
|
||||||
{
|
|
||||||
$this->container = $container;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a gambit.
|
* Add a gambit.
|
||||||
*
|
*
|
||||||
* @param string $gambit
|
* @param GambitInterface $gambit
|
||||||
*/
|
*/
|
||||||
public function add($gambit)
|
public function add($gambit)
|
||||||
{
|
{
|
||||||
$this->gambits[] = $gambit;
|
$this->gambits[] = $gambit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Do not use. Added temporarily to provide support for ConfigureUserGambits and ConfigureDiscussionGambits until they are removed in beta 17.
|
||||||
|
*/
|
||||||
|
public function getFullTextGambit()
|
||||||
|
{
|
||||||
|
return $this->fulltextGambit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Do not use. Added temporarily to provide support for ConfigureUserGambits and ConfigureDiscussionGambits until they are removed in beta 17.
|
||||||
|
*/
|
||||||
|
public function getGambits()
|
||||||
|
{
|
||||||
|
return $this->gambits;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply gambits to a search, given a search query.
|
* Apply gambits to a search, given a search query.
|
||||||
*
|
*
|
||||||
|
@ -68,7 +70,7 @@ class GambitManager
|
||||||
/**
|
/**
|
||||||
* Set the gambit to handle fulltext searching.
|
* Set the gambit to handle fulltext searching.
|
||||||
*
|
*
|
||||||
* @param string $gambit
|
* @param GambitInterface $gambit
|
||||||
*/
|
*/
|
||||||
public function setFulltextGambit($gambit)
|
public function setFulltextGambit($gambit)
|
||||||
{
|
{
|
||||||
|
@ -99,10 +101,8 @@ class GambitManager
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$gambits = array_map([$this->container, 'make'], $this->gambits);
|
|
||||||
|
|
||||||
foreach ($bits as $k => $bit) {
|
foreach ($bits as $k => $bit) {
|
||||||
foreach ($gambits as $gambit) {
|
foreach ($this->gambits as $gambit) {
|
||||||
if (! $gambit instanceof GambitInterface) {
|
if (! $gambit instanceof GambitInterface) {
|
||||||
throw new LogicException(
|
throw new LogicException(
|
||||||
'Gambit '.get_class($gambit).' does not implement '.GambitInterface::class
|
'Gambit '.get_class($gambit).' does not implement '.GambitInterface::class
|
||||||
|
@ -130,9 +130,7 @@ class GambitManager
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$gambit = $this->container->make($this->fulltextGambit);
|
$search->addActiveGambit($this->fulltextGambit);
|
||||||
|
$this->fulltextGambit->apply($search, $query);
|
||||||
$search->addActiveGambit($gambit);
|
|
||||||
$gambit->apply($search, $query);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,63 +18,108 @@ use Flarum\Discussion\Search\Gambit\UnreadGambit;
|
||||||
use Flarum\Event\ConfigureDiscussionGambits;
|
use Flarum\Event\ConfigureDiscussionGambits;
|
||||||
use Flarum\Event\ConfigureUserGambits;
|
use Flarum\Event\ConfigureUserGambits;
|
||||||
use Flarum\Foundation\AbstractServiceProvider;
|
use Flarum\Foundation\AbstractServiceProvider;
|
||||||
|
use Flarum\Foundation\ContainerUtil;
|
||||||
use Flarum\User\Search\Gambit\EmailGambit;
|
use Flarum\User\Search\Gambit\EmailGambit;
|
||||||
use Flarum\User\Search\Gambit\FulltextGambit as UserFulltextGambit;
|
use Flarum\User\Search\Gambit\FulltextGambit as UserFulltextGambit;
|
||||||
use Flarum\User\Search\Gambit\GroupGambit;
|
use Flarum\User\Search\Gambit\GroupGambit;
|
||||||
use Flarum\User\Search\UserSearcher;
|
use Flarum\User\Search\UserSearcher;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
class SearchServiceProvider extends AbstractServiceProvider
|
class SearchServiceProvider extends AbstractServiceProvider
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Register the service provider.
|
* @inheritDoc
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
*/
|
||||||
public function register()
|
public function register()
|
||||||
{
|
{
|
||||||
$this->registerDiscussionGambits();
|
$this->app->singleton('flarum.simple_search.fulltext_gambits', function () {
|
||||||
|
return [
|
||||||
|
DiscussionSearcher::class => DiscussionFulltextGambit::class,
|
||||||
|
UserSearcher::class => UserFulltextGambit::class
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
$this->registerUserGambits();
|
$this->app->singleton('flarum.simple_search.gambits', function () {
|
||||||
|
return [
|
||||||
|
DiscussionSearcher::class => [
|
||||||
|
AuthorGambit::class,
|
||||||
|
CreatedGambit::class,
|
||||||
|
HiddenGambit::class,
|
||||||
|
UnreadGambit::class
|
||||||
|
],
|
||||||
|
UserSearcher::class => [
|
||||||
|
EmailGambit::class,
|
||||||
|
GroupGambit::class
|
||||||
|
]
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app->singleton('flarum.simple_search.search_mutators', function () {
|
||||||
|
return [];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerUserGambits()
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function boot()
|
||||||
{
|
{
|
||||||
$this->app->when(UserSearcher::class)
|
// The rest of these we can resolve in the when->needs->give callback,
|
||||||
->needs(GambitManager::class)
|
// but we need to resolve at least one regardless so we know which
|
||||||
->give(function (Container $app) {
|
// searchers we need to register gambits for.
|
||||||
$gambits = new GambitManager($app);
|
$fullTextGambits = $this->app->make('flarum.simple_search.fulltext_gambits');
|
||||||
|
|
||||||
$gambits->setFulltextGambit(UserFulltextGambit::class);
|
foreach ($fullTextGambits as $searcher => $fullTextGambitClass) {
|
||||||
$gambits->add(EmailGambit::class);
|
$this->app
|
||||||
$gambits->add(GroupGambit::class);
|
->when($searcher)
|
||||||
|
->needs(GambitManager::class)
|
||||||
|
->give(function () use ($searcher, $fullTextGambitClass) {
|
||||||
|
$gambitManager = new GambitManager();
|
||||||
|
$gambitManager->setFulltextGambit($this->app->make($fullTextGambitClass));
|
||||||
|
foreach (Arr::get($this->app->make('flarum.simple_search.gambits'), $searcher, []) as $gambit) {
|
||||||
|
$gambitManager->add($this->app->make($gambit));
|
||||||
|
}
|
||||||
|
|
||||||
$app->make('events')->dispatch(
|
// Temporary BC Layer
|
||||||
new ConfigureUserGambits($gambits)
|
// @deprecated beta 16, remove beta 17.
|
||||||
);
|
|
||||||
|
|
||||||
return $gambits;
|
$oldEvents = [
|
||||||
});
|
DiscussionSearcher::class => ConfigureDiscussionGambits::class,
|
||||||
}
|
UserSearcher::class => ConfigureUserGambits::class
|
||||||
|
];
|
||||||
|
|
||||||
public function registerDiscussionGambits()
|
foreach ($oldEvents as $oldSearcher => $event) {
|
||||||
{
|
if ($searcher === $oldSearcher) {
|
||||||
$this->app->when(DiscussionSearcher::class)
|
$tempGambits = new GambitManager;
|
||||||
->needs(GambitManager::class)
|
$this->app->make('events')->dispatch(
|
||||||
->give(function (Container $app) {
|
new $event($tempGambits)
|
||||||
$gambits = new GambitManager($app);
|
);
|
||||||
|
|
||||||
$gambits->setFulltextGambit(DiscussionFulltextGambit::class);
|
if (! is_null($fullTextGambit = $tempGambits->getFullTextGambit())) {
|
||||||
$gambits->add(AuthorGambit::class);
|
$gambitManager->setFullTextGambit($this->app->make($fullTextGambit));
|
||||||
$gambits->add(CreatedGambit::class);
|
}
|
||||||
$gambits->add(HiddenGambit::class);
|
|
||||||
$gambits->add(UnreadGambit::class);
|
|
||||||
|
|
||||||
$app->make('events')->dispatch(
|
foreach ($tempGambits->getGambits() as $gambit) {
|
||||||
new ConfigureDiscussionGambits($gambits)
|
$gambitManager->add($this->app->make($gambit));
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $gambits;
|
// End BC Layer
|
||||||
});
|
|
||||||
|
return $gambitManager;
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->app
|
||||||
|
->when($searcher)
|
||||||
|
->needs('$searchMutators')
|
||||||
|
->give(function () use ($searcher) {
|
||||||
|
$searchMutators = Arr::get($this->app->make('flarum.simple_search.search_mutators'), $searcher, []);
|
||||||
|
|
||||||
|
return array_map(function ($mutator) {
|
||||||
|
return ContainerUtil::wrapCallback($mutator, $this->app);
|
||||||
|
}, $searchMutators);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,9 @@ namespace Flarum\User\Event;
|
||||||
use Flarum\Search\SearchCriteria;
|
use Flarum\Search\SearchCriteria;
|
||||||
use Flarum\User\Search\UserSearch;
|
use Flarum\User\Search\UserSearch;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated beta 16, remove beta 17
|
||||||
|
*/
|
||||||
class Searching
|
class Searching
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,25 +9,26 @@
|
||||||
|
|
||||||
namespace Flarum\User\Search;
|
namespace Flarum\User\Search;
|
||||||
|
|
||||||
use Flarum\Search\ApplySearchParametersTrait;
|
use Flarum\Search\AbstractSearch;
|
||||||
|
use Flarum\Search\AbstractSearcher;
|
||||||
use Flarum\Search\GambitManager;
|
use Flarum\Search\GambitManager;
|
||||||
use Flarum\Search\SearchCriteria;
|
use Flarum\Search\SearchCriteria;
|
||||||
use Flarum\Search\SearchResults;
|
|
||||||
use Flarum\User\Event\Searching;
|
use Flarum\User\Event\Searching;
|
||||||
|
use Flarum\User\User;
|
||||||
use Flarum\User\UserRepository;
|
use Flarum\User\UserRepository;
|
||||||
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Takes a UserSearchCriteria object, performs a search using gambits,
|
* Takes a UserSearchCriteria object, performs a search using gambits,
|
||||||
* and spits out a UserSearchResults object.
|
* and spits out a UserSearchResults object.
|
||||||
*/
|
*/
|
||||||
class UserSearcher
|
class UserSearcher extends AbstractSearcher
|
||||||
{
|
{
|
||||||
use ApplySearchParametersTrait;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var GambitManager
|
* @var Dispatcher
|
||||||
*/
|
*/
|
||||||
protected $gambits;
|
protected $events;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var UserRepository
|
* @var UserRepository
|
||||||
|
@ -35,51 +36,36 @@ class UserSearcher
|
||||||
protected $users;
|
protected $users;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param UserRepository $users
|
||||||
|
* @param Dispatcher $events
|
||||||
* @param GambitManager $gambits
|
* @param GambitManager $gambits
|
||||||
* @param \Flarum\User\UserRepository $users
|
* @param array $searchMutators
|
||||||
*/
|
*/
|
||||||
public function __construct(GambitManager $gambits, UserRepository $users)
|
public function __construct(UserRepository $users, Dispatcher $events, GambitManager $gambits, array $searchMutators)
|
||||||
{
|
{
|
||||||
$this->gambits = $gambits;
|
parent::__construct($gambits, $searchMutators);
|
||||||
|
|
||||||
|
$this->events = $events;
|
||||||
$this->users = $users;
|
$this->users = $users;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function getQuery(User $actor): Builder
|
||||||
* @param SearchCriteria $criteria
|
|
||||||
* @param int|null $limit
|
|
||||||
* @param int $offset
|
|
||||||
* @param array $load An array of relationships to load on the results.
|
|
||||||
* @return SearchResults
|
|
||||||
*/
|
|
||||||
public function search(SearchCriteria $criteria, $limit = null, $offset = 0, array $load = [])
|
|
||||||
{
|
{
|
||||||
$actor = $criteria->actor;
|
return $this->users->query()->whereVisibleTo($actor);
|
||||||
|
}
|
||||||
|
|
||||||
$query = $this->users->query()->whereVisibleTo($actor);
|
protected function getSearch(Builder $query, User $actor): AbstractSearch
|
||||||
|
{
|
||||||
|
return new UserSearch($query->getQuery(), $actor);
|
||||||
|
}
|
||||||
|
|
||||||
// Construct an object which represents this search for users.
|
/**
|
||||||
// Apply gambits to it, sort, and paging criteria. Also give extensions
|
* @deprecated along with the Searching event, remove in Beta 17.
|
||||||
// an opportunity to modify it.
|
*/
|
||||||
$search = new UserSearch($query->getQuery(), $actor);
|
protected function mutateSearch(AbstractSearch $search, SearchCriteria $criteria)
|
||||||
|
{
|
||||||
|
parent::mutateSearch($search, $criteria);
|
||||||
|
|
||||||
$this->gambits->apply($search, $criteria->query);
|
$this->events->dispatch(new Searching($search, $criteria));
|
||||||
$this->applySort($search, $criteria->sort);
|
|
||||||
$this->applyOffset($search, $offset);
|
|
||||||
$this->applyLimit($search, $limit + 1);
|
|
||||||
|
|
||||||
event(new Searching($search, $criteria));
|
|
||||||
|
|
||||||
// Execute the search query and retrieve the results. We get one more
|
|
||||||
// results than the user asked for, so that we can say if there are more
|
|
||||||
// results. If there are, we will get rid of that extra result.
|
|
||||||
$users = $query->get();
|
|
||||||
|
|
||||||
if ($areMoreResults = ($limit > 0 && $users->count() > $limit)) {
|
|
||||||
$users->pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
$users->load($load);
|
|
||||||
|
|
||||||
return new SearchResults($users, $areMoreResults);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,175 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tests\integration\extenders;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Flarum\Discussion\Search\DiscussionSearcher;
|
||||||
|
use Flarum\Extend;
|
||||||
|
use Flarum\Search\AbstractRegexGambit;
|
||||||
|
use Flarum\Search\AbstractSearch;
|
||||||
|
use Flarum\Search\GambitInterface;
|
||||||
|
use Flarum\Search\SearchCriteria;
|
||||||
|
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Tests\integration\TestCase;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class SimpleFlarumSearchTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
|
||||||
|
public function prepDb()
|
||||||
|
{
|
||||||
|
$this->database()->rollBack();
|
||||||
|
|
||||||
|
// We need to insert these outside of a transaction, because FULLTEXT indexing,
|
||||||
|
// which is needed for search, doesn't happen in transactions.
|
||||||
|
// We clean it up explcitly at the end.
|
||||||
|
$this->database()->table('discussions')->insert([
|
||||||
|
['id' => 1, 'title' => 'DISCUSSION 1', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 2, 'title' => 'DISCUSSION 2', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->database()->table('posts')->insert([
|
||||||
|
['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>not in text</p></t>'],
|
||||||
|
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>lightsail in text</p></t>'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
// We need to call these again, since we rolled back the transaction started by `::app()`.
|
||||||
|
$this->database()->beginTransaction();
|
||||||
|
|
||||||
|
$this->populateDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
parent::tearDown();
|
||||||
|
|
||||||
|
$this->database()->table('discussions')->whereIn('id', [1, 2])->delete();
|
||||||
|
$this->database()->table('posts')->whereIn('id', [1, 2])->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchDiscussions($query, $limit = null)
|
||||||
|
{
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$actor = User::find(1);
|
||||||
|
|
||||||
|
$criteria = new SearchCriteria($actor, $query);
|
||||||
|
|
||||||
|
return $this->app()->getContainer()->make(DiscussionSearcher::class)->search($criteria, $limit)->getResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function works_as_expected_with_no_modifications()
|
||||||
|
{
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$searchForAll = json_encode($this->searchDiscussions('in text', 5));
|
||||||
|
$this->assertContains('DISCUSSION 1', $searchForAll);
|
||||||
|
$this->assertContains('DISCUSSION 2', $searchForAll);
|
||||||
|
|
||||||
|
$searchForSecond = json_encode($this->searchDiscussions('lightsail', 5));
|
||||||
|
$this->assertNotContains('DISCUSSION 1', $searchForSecond);
|
||||||
|
$this->assertContains('DISCUSSION 2', $searchForSecond);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_full_text_gambit_has_effect_if_added()
|
||||||
|
{
|
||||||
|
$this->extend((new Extend\SimpleFlarumSearch(DiscussionSearcher::class))->setFullTextGambit(NoResultFullTextGambit::class));
|
||||||
|
|
||||||
|
$this->assertEquals('[]', json_encode($this->searchDiscussions('in text', 5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_filter_gambit_has_effect_if_added()
|
||||||
|
{
|
||||||
|
$this->extend((new Extend\SimpleFlarumSearch(DiscussionSearcher::class))->addGambit(NoResultFilterGambit::class));
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$withResultSearch = json_encode($this->searchDiscussions('noResult:0', 5));
|
||||||
|
$this->assertContains('DISCUSSION 1', $withResultSearch);
|
||||||
|
$this->assertContains('DISCUSSION 2', $withResultSearch);
|
||||||
|
$this->assertEquals('[]', json_encode($this->searchDiscussions('noResult:1', 5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function search_mutator_has_effect_if_added()
|
||||||
|
{
|
||||||
|
$this->extend((new Extend\SimpleFlarumSearch(DiscussionSearcher::class))->addSearchMutator(function ($search, $criteria) {
|
||||||
|
$search->getquery()->whereRaw('1=0');
|
||||||
|
}));
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$this->assertEquals('[]', json_encode($this->searchDiscussions('in text', 5)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function search_mutator_has_effect_if_added_with_invokable_class()
|
||||||
|
{
|
||||||
|
$this->extend((new Extend\SimpleFlarumSearch(DiscussionSearcher::class))->addSearchMutator(CustomSearchMutator::class));
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$this->assertEquals('[]', json_encode($this->searchDiscussions('in text', 5)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoResultFullTextGambit implements GambitInterface
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function apply(AbstractSearch $search, $searchValue)
|
||||||
|
{
|
||||||
|
$search->getQuery()
|
||||||
|
->whereRaw('0=1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NoResultFilterGambit extends AbstractRegexGambit
|
||||||
|
{
|
||||||
|
protected $pattern = 'noResult:(.+)';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function conditions(AbstractSearch $search, array $matches, $negate)
|
||||||
|
{
|
||||||
|
$noResults = trim($matches[1], ' ');
|
||||||
|
if ($noResults == '1') {
|
||||||
|
$search->getQuery()
|
||||||
|
->whereRaw('0=1');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomSearchMutator
|
||||||
|
{
|
||||||
|
public function __invoke($search, $criteria)
|
||||||
|
{
|
||||||
|
$search->getQuery()->whereRaw('1=0');
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user