diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php index a1a53bc0fe..b186248d12 100644 --- a/app/Http/Controllers/SearchController.php +++ b/app/Http/Controllers/SearchController.php @@ -67,14 +67,10 @@ class SearchController extends Controller // parse search terms: $searcher->parseQuery($fullQuery); $query = $searcher->getWordsAsString(); - - - //also get modifiers to display: - - + $modifiers = $searcher->getModifiers(); $subTitle = (string)trans('breadcrumbs.search_result', ['query' => $query]); - return view('search.index', compact('query', 'fullQuery', 'subTitle')); + return view('search.index', compact('query','modifiers', 'fullQuery', 'subTitle')); } /** @@ -92,9 +88,10 @@ class SearchController extends Controller $searcher->parseQuery($fullQuery); $searcher->setLimit((int)config('firefly.search_result_limit')); $transactions = $searcher->searchTransactions(); + $searchTime = $searcher->searchTime(); // in seconds try { - $html = view('search.search', compact('transactions'))->render(); + $html = view('search.search', compact('transactions','searchTime'))->render(); // @codeCoverageIgnoreStart } catch (Throwable $e) { Log::error(sprintf('Cannot render search.search: %s', $e->getMessage())); diff --git a/app/Repositories/Account/AccountRepository.php b/app/Repositories/Account/AccountRepository.php index ea233c95c8..89848b5fe0 100644 --- a/app/Repositories/Account/AccountRepository.php +++ b/app/Repositories/Account/AccountRepository.php @@ -556,6 +556,25 @@ class AccountRepository implements AccountRepositoryInterface return $result; } + /** + * @param string $query + * @param array $types + * + * @return Collection + */ + public function searchAccount(string $query, array $types): Collection + { + $dbQuery = $this->user->accounts(); + $search = sprintf('%%%s%%', $query); + if (\count($types) > 0) { + $dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id'); + $dbQuery->whereIn('account_types.type', $types); + } + $dbQuery->where('name', 'LIKE', $search); + + return $dbQuery->get(['accounts.*']); + } + /** * @param User $user */ diff --git a/app/Repositories/Account/AccountRepositoryInterface.php b/app/Repositories/Account/AccountRepositoryInterface.php index 00722f91ec..7d0382ca10 100644 --- a/app/Repositories/Account/AccountRepositoryInterface.php +++ b/app/Repositories/Account/AccountRepositoryInterface.php @@ -36,6 +36,7 @@ use Illuminate\Support\Collection; */ interface AccountRepositoryInterface { + /** * Moved here from account CRUD. * @@ -247,6 +248,14 @@ interface AccountRepositoryInterface */ public function oldestJournalDate(Account $account): ?Carbon; + /** + * @param string $query + * @param array $types + * + * @return Collection + */ + public function searchAccount(string $query, array $types): Collection; + /** * @param User $user */ diff --git a/app/Repositories/Bill/BillRepository.php b/app/Repositories/Bill/BillRepository.php index 5b2d78cf74..f7888eb4ce 100644 --- a/app/Repositories/Bill/BillRepository.php +++ b/app/Repositories/Bill/BillRepository.php @@ -623,6 +623,18 @@ class BillRepository implements BillRepositoryInterface return $start; } + /** + * @param string $query + * + * @return Collection + */ + public function searchBill(string $query): Collection + { + $query = sprintf('%%%s%%', $query); + + return $this->user->bills()->where('name', 'LIKE', $query)->get(); + } + /** * @param User $user */ diff --git a/app/Repositories/Bill/BillRepositoryInterface.php b/app/Repositories/Bill/BillRepositoryInterface.php index 2046fe4c90..e90abc44e8 100644 --- a/app/Repositories/Bill/BillRepositoryInterface.php +++ b/app/Repositories/Bill/BillRepositoryInterface.php @@ -33,7 +33,6 @@ use Illuminate\Support\Collection; */ interface BillRepositoryInterface { - /** * @param Bill $bill * @@ -236,6 +235,13 @@ interface BillRepositoryInterface */ public function nextExpectedMatch(Bill $bill, Carbon $date): Carbon; + /** + * @param string $query + * + * @return Collection + */ + public function searchBill(string $query): Collection; + /** * @param User $user */ diff --git a/app/Repositories/Budget/BudgetRepository.php b/app/Repositories/Budget/BudgetRepository.php index 0b61d860d9..e079b4edaa 100644 --- a/app/Repositories/Budget/BudgetRepository.php +++ b/app/Repositories/Budget/BudgetRepository.php @@ -635,6 +635,20 @@ class BudgetRepository implements BudgetRepositoryInterface return $result; } + /** + * @param string $query + * + * @return Collection + */ + public function searchBudget(string $query): Collection + { + $query = sprintf('%%%s%%', $query); + + return $this->user->budgets()->where('name', 'LIKE', $query)->get(); + } + + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * @param TransactionCurrency $currency * @param Carbon $start @@ -662,8 +676,6 @@ class BudgetRepository implements BudgetRepositoryInterface return $availableBudget; } - /** @noinspection MoreThanThreeArgumentsInspection */ - /** * @param Budget $budget * @param int $order @@ -903,6 +915,8 @@ class BudgetRepository implements BudgetRepositoryInterface return $limit; } + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * @param Budget $budget * @param array $data @@ -922,8 +936,6 @@ class BudgetRepository implements BudgetRepositoryInterface return $budget; } - /** @noinspection MoreThanThreeArgumentsInspection */ - /** * @param AvailableBudget $availableBudget * @param array $data diff --git a/app/Repositories/Budget/BudgetRepositoryInterface.php b/app/Repositories/Budget/BudgetRepositoryInterface.php index 3100064f8f..d61c5ed5ee 100644 --- a/app/Repositories/Budget/BudgetRepositoryInterface.php +++ b/app/Repositories/Budget/BudgetRepositoryInterface.php @@ -169,8 +169,6 @@ interface BudgetRepositoryInterface */ public function getBudgets(): Collection; - /** @noinspection MoreThanThreeArgumentsInspection */ - /** * Get all budgets with these ID's. * @@ -180,6 +178,8 @@ interface BudgetRepositoryInterface */ public function getByIds(array $budgetIds): Collection; + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * @return Collection */ @@ -194,6 +194,13 @@ interface BudgetRepositoryInterface */ public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array; + /** + * @param string $query + * + * @return Collection + */ + public function searchBudget(string $query): Collection; + /** * @param TransactionCurrency $currency * @param Carbon $start diff --git a/app/Repositories/Category/CategoryRepository.php b/app/Repositories/Category/CategoryRepository.php index 4d09089b51..24f695623b 100644 --- a/app/Repositories/Category/CategoryRepository.php +++ b/app/Repositories/Category/CategoryRepository.php @@ -531,6 +531,18 @@ class CategoryRepository implements CategoryRepositoryInterface /** @noinspection MoreThanThreeArgumentsInspection */ + /** + * @param string $query + * + * @return Collection + */ + public function searchCategory(string $query): Collection + { + $query = sprintf('%%%s%%', $query); + + return $this->user->categories()->where('name', 'LIKE', $query)->get(); + } + /** * @param User $user */ diff --git a/app/Repositories/Category/CategoryRepositoryInterface.php b/app/Repositories/Category/CategoryRepositoryInterface.php index 65f0c79b85..edd4a33597 100644 --- a/app/Repositories/Category/CategoryRepositoryInterface.php +++ b/app/Repositories/Category/CategoryRepositoryInterface.php @@ -40,7 +40,6 @@ interface CategoryRepositoryInterface */ public function destroy(Category $category): bool; - /** @noinspection MoreThanThreeArgumentsInspection */ /** * @param Collection $categories * @param Collection $accounts @@ -52,6 +51,7 @@ interface CategoryRepositoryInterface public function earnedInPeriod(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): string; /** @noinspection MoreThanThreeArgumentsInspection */ + /** * @param Collection $categories * @param Collection $accounts @@ -62,6 +62,8 @@ interface CategoryRepositoryInterface */ public function earnedInPeriodCollection(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): Collection; + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * A very cryptic method name that means: * @@ -119,8 +121,6 @@ interface CategoryRepositoryInterface */ public function getByIds(array $categoryIds): Collection; - /** @noinspection MoreThanThreeArgumentsInspection */ - /** * Returns a list of all the categories belonging to a user. * @@ -128,6 +128,8 @@ interface CategoryRepositoryInterface */ public function getCategories(): Collection; + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * Return most recent transaction(journal) date or null when never used before. * @@ -138,8 +140,6 @@ interface CategoryRepositoryInterface */ public function lastUseDate(Category $category, Collection $accounts): ?Carbon; - /** @noinspection MoreThanThreeArgumentsInspection */ - /** * @param Collection $categories * @param Collection $accounts @@ -150,6 +150,8 @@ interface CategoryRepositoryInterface */ public function periodExpenses(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * @param Collection $accounts * @param Carbon $start @@ -169,8 +171,6 @@ interface CategoryRepositoryInterface */ public function periodIncome(Collection $categories, Collection $accounts, Carbon $start, Carbon $end): array; - /** @noinspection MoreThanThreeArgumentsInspection */ - /** * @param Collection $accounts * @param Carbon $start @@ -182,6 +182,15 @@ interface CategoryRepositoryInterface /** @noinspection MoreThanThreeArgumentsInspection */ + /** + * @param string $query + * + * @return Collection + */ + public function searchCategory(string $query): Collection; + + /** @noinspection MoreThanThreeArgumentsInspection */ + /** * @param User $user */ diff --git a/app/Support/Search/Search.php b/app/Support/Search/Search.php index 6c73a3d7c4..b0c8ed1909 100644 --- a/app/Support/Search/Search.php +++ b/app/Support/Search/Search.php @@ -26,6 +26,11 @@ use Carbon\Carbon; use FireflyIII\Helpers\Collector\TransactionCollectorInterface; use FireflyIII\Helpers\Filter\DoubleTransactionFilter; use FireflyIII\Helpers\Filter\InternalTransferFilter; +use FireflyIII\Models\AccountType; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; +use FireflyIII\Repositories\Bill\BillRepositoryInterface; +use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; +use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\User; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Support\Collection; @@ -36,12 +41,22 @@ use Log; */ class Search implements SearchInterface { + /** @var AccountRepositoryInterface */ + private $accountRepository; + /** @var BillRepositoryInterface */ + private $billRepository; + /** @var BudgetRepositoryInterface */ + private $budgetRepository; + /** @var CategoryRepositoryInterface */ + private $categoryRepository; /** @var int */ private $limit = 100; /** @var Collection */ private $modifiers; /** @var string */ private $originalQuery = ''; + /** @var float */ + private $startTime; /** @var User */ private $user; /** @var array */ @@ -54,8 +69,13 @@ class Search implements SearchInterface */ public function __construct() { - $this->modifiers = new Collection; - $this->validModifiers = (array)config('firefly.search_modifiers'); + $this->modifiers = new Collection; + $this->validModifiers = (array)config('firefly.search_modifiers'); + $this->startTime = microtime(true); + $this->accountRepository = app(AccountRepositoryInterface::class); + $this->categoryRepository = app(CategoryRepositoryInterface::class); + $this->budgetRepository = app(BudgetRepositoryInterface::class); + $this->billRepository = app(BillRepositoryInterface::class); if ('testing' === config('app.env')) { Log::warning(sprintf('%s should not be instantiated in the TEST environment!', \get_class($this))); @@ -112,6 +132,14 @@ class Search implements SearchInterface } } + /** + * @return float + */ + public function searchTime(): float + { + return microtime(true) - $this->startTime; + } + /** * @return LengthAwarePaginator */ @@ -128,8 +156,6 @@ class Search implements SearchInterface $collector->withOpposingAccount()->withCategoryInformation()->withBudgetInformation(); } - - $collector->setSearchWords($this->words); $collector->removeFilter(InternalTransferFilter::class); $collector->addFilter(DoubleTransactionFilter::class); @@ -155,6 +181,10 @@ class Search implements SearchInterface public function setUser(User $user): void { $this->user = $user; + $this->accountRepository->setUser($user); + $this->billRepository->setUser($user); + $this->categoryRepository->setUser($user); + $this->budgetRepository->setUser($user); } /** @@ -167,8 +197,6 @@ class Search implements SearchInterface { /* * TODO: - * 'source', 'destination', - * 'category','budget', * 'bill', */ @@ -176,6 +204,40 @@ class Search implements SearchInterface switch ($modifier['type']) { default: die(sprintf('unsupported modifier: "%s"', $modifier['type'])); + case 'source': + // source can only be asset, liability or revenue account: + $searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::REVENUE]; + $accounts = $this->accountRepository->searchAccount($modifier['value'], $searchTypes); + if ($accounts->count() > 0) { + $collector->setAccounts($accounts); + } + break; + case 'destination': + // source can only be asset, liability or expense account: + $searchTypes = [AccountType::ASSET, AccountType::MORTGAGE, AccountType::LOAN, AccountType::DEBT, AccountType::EXPENSE]; + $accounts = $this->accountRepository->searchAccount($modifier['value'], $searchTypes); + if ($accounts->count() > 0) { + $collector->setOpposingAccounts($accounts); + } + break; + case 'category': + $result = $this->categoryRepository->searchCategory($modifier['value']); + if ($result->count() > 0) { + $collector->setCategories($result); + } + break; + case 'bill': + $result = $this->billRepository->searchBill($modifier['value']); + if ($result->count() > 0) { + $collector->setBills($result); + } + break; + case 'budget': + $result = $this->budgetRepository->searchBudget($modifier['value']); + if ($result->count() > 0) { + $collector->setBudgets($result); + } + break; case 'amount_is': case 'amount': $amount = app('steam')->positive((string)$modifier['value']); diff --git a/app/Support/Search/SearchInterface.php b/app/Support/Search/SearchInterface.php index 269c592a44..df8876d5ae 100644 --- a/app/Support/Search/SearchInterface.php +++ b/app/Support/Search/SearchInterface.php @@ -31,6 +31,11 @@ use Illuminate\Support\Collection; */ interface SearchInterface { + /** + * @return Collection + */ + public function getModifiers(): Collection; + /** * @return string */ @@ -46,6 +51,11 @@ interface SearchInterface */ public function parseQuery(string $query); + /** + * @return float + */ + public function searchTime(): float; + /** * @return LengthAwarePaginator */ diff --git a/public/v1/js/ff/search/index.js b/public/v1/js/ff/search/index.js index c7ccc645ef..c178767735 100644 --- a/public/v1/js/ff/search/index.js +++ b/public/v1/js/ff/search/index.js @@ -39,8 +39,6 @@ function searchFailure() { function presentSearchResults(data) { $('.search_ongoing').hide(); - $('p.search_count').show(); - $('span.search_count').text(data.count); $('.search_box').find('.overlay').remove(); $('.search_results').html(data.html).show(); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 7146a141d9..2d4da6337d 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -217,7 +217,27 @@ return [ // search 'search' => 'Search', 'search_query' => 'Query', - 'search_found_transactions' => 'Number of transactions found:', + 'search_found_transactions' => 'Firefly III found :count transaction(s) in :time seconds.', + 'search_for_query' => 'Firefly III is searching for transactions with all of these words in them: :query', + 'search_modifier_amount_is' => 'Amount is exactly :value', + 'search_modifier_amount' => 'Amount is exactly :value', + 'search_modifier_amount_max' => 'Amount is at most :value', + 'search_modifier_amount_min' => 'Amount is at least :value', + 'search_modifier_amount_less' => 'Amount is less than :value', + 'search_modifier_amount_more' => 'Amount is more than :value', + 'search_modifier_source' => 'Source account is :value', + 'search_modifier_destination' => 'Destination account is :value', + 'search_modifier_category' => 'Category is :value', + 'search_modifier_budget' => 'Budget is :value', + 'search_modifier_bill' => 'Bill is :value', + 'search_modifier_type' => 'Transaction type is :value', + 'search_modifier_date' => 'Transaction date is :value', + 'search_modifier_date_before' => 'Transaction date is before :value', + 'search_modifier_date_after' => 'Transaction date is after :value', + 'search_modifier_on' => 'Transaction date is :value', + 'search_modifier_before' => 'Transaction date is before :value', + 'search_modifier_after' => 'Transaction date is after :value', + 'modifiers_applies_are' => 'The following modifiers are applied to the search as well:', 'general_search_error' => 'An error occured while searching. Please check the log files for more information.', 'search_box' => 'Search', 'search_box_intro' => 'Welcome to the search function of Firefly III. Enter your search query in the box. Make sure you check out the help file because the search is pretty advanced.', diff --git a/resources/views/v1/search/index.twig b/resources/views/v1/search/index.twig index b9f5fe9ebd..90b174b21e 100644 --- a/resources/views/v1/search/index.twig +++ b/resources/views/v1/search/index.twig @@ -20,7 +20,8 @@
+ {{ trans('firefly.search_for_query', {query: query})|raw}} +
+ {% if modifiers|length > 0 %} +{{ trans('firefly.modifiers_applies_are') }}
+{{ 'search_searching'|_ }}
- - + {# loading indicator #}