diff --git a/src/Smartstore.Web/Controllers/SearchController.cs b/src/Smartstore.Web/Controllers/SearchController.cs index 23a2f5c4b5..93c35065dd 100644 --- a/src/Smartstore.Web/Controllers/SearchController.cs +++ b/src/Smartstore.Web/Controllers/SearchController.cs @@ -20,6 +20,7 @@ public class SearchController : PublicController private readonly CatalogSettings _catalogSettings; private readonly Lazy _productService; private readonly ProductUrlHelper _productUrlHelper; + //private SearchResultModel _searchResultModel; public SearchController( CatalogHelper catalogHelper, @@ -29,7 +30,9 @@ public SearchController( SearchSettings searchSettings, CatalogSettings catalogSettings, Lazy productService, - ProductUrlHelper productUrlHelper) + ProductUrlHelper productUrlHelper + //SearchResultModel search + ) { _catalogHelper = catalogHelper; _catalogSearchService = catalogSearchService; @@ -39,6 +42,15 @@ public SearchController( _catalogSettings = catalogSettings; _productService = productService; _productUrlHelper = productUrlHelper; + + //if(search == null) + //{ + //} + //else + //{ + // _searchResultModel = search; + //} + //_searchResultModel = new SearchResultModel(); } [HttpPost] @@ -98,90 +110,34 @@ await _localizedEntityService.PrefetchLocalizedPropertiesAsync( [LocalizedRoute("/search", Name = "Search")] public async Task Search(CatalogSearchQuery query) { - CatalogSearchResult result = null; - var model = new SearchResultModel(query); var term = query?.DefaultTerm; - if (term == null || term.Length < _searchSettings.InstantSearchTermMinLength) - { - model.SearchResult = new CatalogSearchResult(query); - model.TopProducts = ProductSummaryModel.Empty; - model.Error = T("Search.SearchTermMinimumLengthIsNCharacters", _searchSettings.InstantSearchTermMinLength); - return View(model); - } - - var customer = Services.WorkContext.CurrentCustomer; - if (!customer.IsSystemAccount) - { - customer.GenericAttributes.LastContinueShoppingPage = HttpContext.Request.RawUrl(); - } - - try + if (_searchSettings.SearchProductByIdentificationNumber) { - if (_searchSettings.SearchProductByIdentificationNumber) + var (product, attributeCombination) = await _productService.Value.GetProductByCodeAsync(term); + if (product != null) { - var (product, attributeCombination) = await _productService.Value.GetProductByCodeAsync(term); - if (product != null) - { if (attributeCombination != null) { return Redirect(await _productUrlHelper.GetProductPathAsync( - product.Id, - await product.GetActiveSlugAsync(), + product.Id, + await product.GetActiveSlugAsync(), attributeCombination.AttributeSelection)); } return RedirectToRoute("Product", new { SeName = await product.GetActiveSlugAsync() }); - } } - - result = await _catalogSearchService.SearchAsync(query); - } - catch (Exception ex) - { - model.Error = ex.ToString(); - result = new CatalogSearchResult(query); } + return View(await GetSearchResultModel(query)); + } - if (result.TotalHitsCount == 0 && result.SpellCheckerSuggestions.Length > 0) - { - // No matches, but spell checker made a suggestion. - // We implicitly search again with the first suggested term. - var oldSuggestions = result.SpellCheckerSuggestions; - - query.DefaultTerm = oldSuggestions[0]; - result = await _catalogSearchService.SearchAsync(query); - - if (result.TotalHitsCount > 0) - { - model.AttemptedTerm = term; - // Restore the original suggestions. - result.SpellCheckerSuggestions = oldSuggestions.Where(x => x != query.DefaultTerm).ToArray(); - } - else - { - query.DefaultTerm = term; - } - } - - model.SearchResult = result; - model.Term = query.DefaultTerm; - model.TotalProductsCount = result.TotalHitsCount; - - var productSummaryViewMode = query.CustomData.Get("ViewMode") is string viewMode && viewMode.EqualsNoCase("list") - ? ProductSummaryViewMode.List - : ProductSummaryViewMode.Grid; - - var mappingSettings = _catalogHelper.GetBestFitProductSummaryMappingSettings(productSummaryViewMode); - var summaryModel = await _catalogHelper.MapProductSummaryModelAsync(result, mappingSettings); - - // Prepare paging/sorting/mode stuff. - _catalogHelper.MapListActions(summaryModel, null, _catalogSettings.DefaultPageSizeOptions); - - // Add product hits. - model.TopProducts = summaryModel; - - return View(model); + public async Task GetSearchResultModel(CatalogSearchQuery query) + { + CatalogSearchResult result = null; + var model = new SearchResultModel(query); + + var service = new SearchControllerService(_catalogSearchService, _searchSettings, _catalogSettings, _catalogHelper); + return await service.GetSearchResultService(model, result, query); } } } diff --git a/src/Smartstore.Web/Controllers/SearchControllerService.cs b/src/Smartstore.Web/Controllers/SearchControllerService.cs new file mode 100644 index 0000000000..4de4369219 --- /dev/null +++ b/src/Smartstore.Web/Controllers/SearchControllerService.cs @@ -0,0 +1,99 @@ +using Smartstore.Core.Catalog; +using Smartstore.Core.Catalog.Search; +using Smartstore.Web.Models.Catalog; +using Smartstore.Web.Models.Search; + +namespace Smartstore.Web.Controllers +{ + public class SearchControllerService: PublicController + { + private readonly CatalogHelper _catalogHelper; + private readonly SearchSettings _searchSettings; + private readonly ICatalogSearchService _catalogSearchService; + private readonly CatalogSettings _catalogSettings; + + + public SearchControllerService(ICatalogSearchService catalogSearchService, + SearchSettings searchSettings, + CatalogSettings catalogSettings, + CatalogHelper catalogHelper) + { + _catalogSearchService = catalogSearchService; + _searchSettings = searchSettings; + _catalogSettings = catalogSettings; + _catalogHelper = catalogHelper; + } + public async Task GetSearchResultService(SearchResultModel model, CatalogSearchResult result, CatalogSearchQuery query) + { + var term = query?.DefaultTerm; + + if (term == null || term.Length < _searchSettings.InstantSearchTermMinLength) + { + model.SearchResult = new CatalogSearchResult(query); + model.TopProducts = ProductSummaryModel.Empty; + model.Error = T("Search.SearchTermMinimumLengthIsNCharacters", _searchSettings.InstantSearchTermMinLength); + return model; + } + + CustomerCheck(); + + try + { + result = await _catalogSearchService.SearchAsync(query); + } + catch (Exception ex) + { + model.Error = ex.ToString(); + result = new CatalogSearchResult(query); + } + + if (result.TotalHitsCount == 0 && result.SpellCheckerSuggestions.Length > 0) + { + // No matches, but spell checker made a suggestion. + // We implicitly search again with the first suggested term. + var oldSuggestions = result.SpellCheckerSuggestions; + + query.DefaultTerm = oldSuggestions[0]; + result = await _catalogSearchService.SearchAsync(query); + + if (result.TotalHitsCount > 0) + { + model.AttemptedTerm = term; + // Restore the original suggestions. + result.SpellCheckerSuggestions = oldSuggestions.Where(x => x != query.DefaultTerm).ToArray(); + } + else + { + query.DefaultTerm = term; + } + } + + model.SearchResult = result; + model.Term = query.DefaultTerm; + model.TotalProductsCount = result.TotalHitsCount; + + var productSummaryViewMode = query.CustomData.Get("ViewMode") is string viewMode && viewMode.EqualsNoCase("list") + ? ProductSummaryViewMode.List + : ProductSummaryViewMode.Grid; + + var mappingSettings = _catalogHelper.GetBestFitProductSummaryMappingSettings(productSummaryViewMode); + var summaryModel = await _catalogHelper.MapProductSummaryModelAsync(result, mappingSettings); + + // Prepare paging/sorting/mode stuff. + _catalogHelper.MapListActions(summaryModel, null, _catalogSettings.DefaultPageSizeOptions); + + // Add product hits. + model.TopProducts = summaryModel; + return model; + } + + public void CustomerCheck() + { + var customer = Services.WorkContext.CurrentCustomer; + if (!customer.IsSystemAccount) + { + customer.GenericAttributes.LastContinueShoppingPage = HttpContext.Request.RawUrl(); + } + } + } +} diff --git a/test/Smartstore.Web.Tests/Common/Controllers/SearchControllerTests.cs b/test/Smartstore.Web.Tests/Common/Controllers/SearchControllerTests.cs new file mode 100644 index 0000000000..8d5c737249 --- /dev/null +++ b/test/Smartstore.Web.Tests/Common/Controllers/SearchControllerTests.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using NUnit.Framework; +using Smartstore.Core.Catalog.Search; +using Smartstore.Test.Common; +using Smartstore.Web.Controllers; +using Smartstore.Web.Models.Search; +using Microsoft.AspNetCore.Mvc; +using Smartstore.Web.Models.Catalog; +using Smartstore.Collections; +using Moq; +using Smartstore.Core.Catalog.Products; +using Smartstore.Core.Search.Facets; +using Smartstore.Core.Search; + +namespace Smartstore.Web.Tests.Common.Controllers; + +[TestFixture] +public class SearchControllerTests +{ + [Test] + public async Task Term_Not_Found() + { + SearchSettings settings = new SearchSettings(); + settings.InstantSearchTermMinLength = 2; + + SearchController controller = new(null, null, null, null, settings, null, null, null); + CatalogSearchQuery query = new CatalogSearchQuery(); + query.DefaultTerm = "a"; + var actual = await controller.GetSearchResultModel(query); + + SearchResultModel expected_result = new SearchResultModel(query); + expected_result.SearchResult = new CatalogSearchResult(query); + expected_result.TopProducts = ProductSummaryModel.Empty; + expected_result.Error = "Search.SearchTermMinimumLengthIsNCharacters"; + + //need to mock db for this assert + Assert.AreEqual(expected_result.SearchResult.TotalHitsCount, actual.SearchResult.TotalHitsCount); + Assert.AreEqual(expected_result.TopProducts, actual.TopProducts); + Assert.AreEqual(expected_result.Error, actual.Error); + } + + [Test] + public async Task Search_Item() + { + SearchSettings settings = new SearchSettings(); + settings.InstantSearchTermMinLength = 2; + CatalogSearchQuery query = new CatalogSearchQuery(); + query.DefaultTerm = "baseball"; + + var catalogSerchResult = new CatalogSearchResult(null, query, null, 1, null, null, null); + + var searchServiceMock = new Mock(); + searchServiceMock.Setup(x => x.SearchAsync(query, false)).ReturnsAsync(catalogSerchResult); + var searchServiceMockObject = searchServiceMock.Object; + + SearchController controller = new(null, null, null, null, settings, null, null, null); + var actual = await controller.GetSearchResultModel(query); + + //need to mock db for this assert + Assert.AreEqual(1, actual.TopProducts.Items.Count); + } +}