Skip to content

Commit

Permalink
Prepared seo friendly name for filtering by specification attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
KrzysztofPajak committed Apr 24, 2020
1 parent 1fe8f03 commit 0281188
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 28 deletions.
10 changes: 10 additions & 0 deletions Grand.Core/Domain/Catalog/SpecificationAttributeOptionFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public class SpecificationAttributeOptionFilter
/// </summary>
public string SpecificationAttributeName { get; set; }

/// <summary>
/// Gets or sets the specification attribute sename
/// </summary>
public string SpecificationAttributeSeName { get; set; }

/// <summary>
/// Gets or sets the specification attribute option color (RGB)
/// </summary>
Expand All @@ -35,6 +40,11 @@ public class SpecificationAttributeOptionFilter
/// </summary>
public string SpecificationAttributeOptionName { get; set; }

/// <summary>
/// Gets or sets the specification attribute option sename
/// </summary>
public string SpecificationAttributeOptionSeName { get; set; }

/// <summary>
/// Gets or sets the specification attribute option display order
/// </summary>
Expand Down
7 changes: 7 additions & 0 deletions Grand.Services/Catalog/ISpecificationAttributeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ public partial interface ISpecificationAttributeService
/// <returns>Specification attribute</returns>
Task<SpecificationAttribute> GetSpecificationAttributeById(string specificationAttributeId);

/// <summary>
/// Gets a specification attribute by sename
/// </summary>
/// <param name="sename">Sename</param>
/// <returns>Specification attribute</returns>
Task<SpecificationAttribute> GetSpecificationAttributeBySeName(string sename);

/// <summary>
/// Gets specification attributes
/// </summary>
Expand Down
58 changes: 54 additions & 4 deletions Grand.Services/Catalog/SpecificationAttributeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Grand.Services.Catalog
public partial class SpecificationAttributeService : ISpecificationAttributeService
{
#region Constants

/// <summary>
/// Key for caching
/// </summary>
Expand All @@ -33,6 +33,28 @@ public partial class SpecificationAttributeService : ISpecificationAttributeServ
/// </summary>
private const string PRODUCTS_PATTERN_KEY = "Grand.product.";


/// <summary>
/// Key for caching
/// </summary>
/// <remarks>
/// {0} : sename
/// </remarks>
private const string SPECIFICATION_BY_SENAME = "Grand.specification.sename-{0}";

/// <summary>
/// Key for caching
/// </summary>
/// <remarks>
/// {0} : specification ID
/// </remarks>
private const string SPECIFICATION_BY_ID_KEY = "Grand.specification.id-{0}";

/// <summary>
/// Key pattern to clear cache
/// </summary>
private const string SPECIFICATION_PATTERN_KEY = "Grand.specification.";

#endregion

#region Fields
Expand Down Expand Up @@ -74,11 +96,29 @@ public SpecificationAttributeService(ICacheManager cacheManager,
/// </summary>
/// <param name="specificationAttributeId">The specification attribute identifier</param>
/// <returns>Specification attribute</returns>
public virtual Task<SpecificationAttribute> GetSpecificationAttributeById(string specificationAttributeId)
public virtual async Task<SpecificationAttribute> GetSpecificationAttributeById(string specificationAttributeId)
{
return _specificationAttributeRepository.GetByIdAsync(specificationAttributeId);
string key = string.Format(SPECIFICATION_BY_ID_KEY, specificationAttributeId);
return await _cacheManager.GetAsync(key, () => _specificationAttributeRepository.GetByIdAsync(specificationAttributeId));
}

/// <summary>
/// Gets a specification attribute by sename
/// </summary>
/// <param name="sename">Sename</param>
/// <returns>Specification attribute</returns>
public virtual async Task<SpecificationAttribute> GetSpecificationAttributeBySeName(string sename)
{
if (string.IsNullOrEmpty(sename))
return await Task.FromResult<SpecificationAttribute>(null);

sename = sename.ToLowerInvariant();

var key = string.Format(SPECIFICATION_BY_SENAME, sename);
return await _cacheManager.GetAsync(key, async () => await _specificationAttributeRepository.Table.Where(x => x.SeName == sename).FirstOrDefaultAsync());
}


/// <summary>
/// Gets specification attributes
/// </summary>
Expand Down Expand Up @@ -109,7 +149,9 @@ public virtual async Task DeleteSpecificationAttribute(SpecificationAttribute sp

await _specificationAttributeRepository.DeleteAsync(specificationAttribute);

//clear cache
await _cacheManager.RemoveByPrefix(PRODUCTS_PATTERN_KEY);
await _cacheManager.RemoveByPrefix(SPECIFICATION_PATTERN_KEY);

//event notification
await _mediator.EntityDeleted(specificationAttribute);
Expand All @@ -126,6 +168,9 @@ public virtual async Task InsertSpecificationAttribute(SpecificationAttribute sp

await _specificationAttributeRepository.InsertAsync(specificationAttribute);

//clear cache
await _cacheManager.RemoveByPrefix(SPECIFICATION_PATTERN_KEY);

//event notification
await _mediator.EntityInserted(specificationAttribute);
}
Expand All @@ -141,6 +186,9 @@ public virtual async Task UpdateSpecificationAttribute(SpecificationAttribute sp

await _specificationAttributeRepository.UpdateAsync(specificationAttribute);

//clear cache
await _cacheManager.RemoveByPrefix(SPECIFICATION_PATTERN_KEY);

//event notification
await _mediator.EntityUpdated(specificationAttribute);
}
Expand Down Expand Up @@ -188,6 +236,8 @@ public virtual async Task DeleteSpecificationAttributeOption(SpecificationAttrib
specificationAttribute.SpecificationAttributeOptions.Remove(sao);
await UpdateSpecificationAttribute(specificationAttribute);

//clear cache
await _cacheManager.RemoveByPrefix(SPECIFICATION_PATTERN_KEY);
await _cacheManager.RemoveByPrefix(PRODUCTS_PATTERN_KEY);

//event notification
Expand All @@ -212,7 +262,7 @@ public virtual async Task DeleteProductSpecificationAttribute(ProductSpecificati
var update = updatebuilder.Pull(p => p.ProductSpecificationAttributes, productSpecificationAttribute);
await _productRepository.Collection.UpdateOneAsync(new BsonDocument("_id", productSpecificationAttribute.ProductId), update);

//cache
//clear cache
await _cacheManager.RemoveAsync(string.Format(PRODUCTS_BY_ID_KEY, productSpecificationAttribute.ProductId));

//event notification
Expand Down
6 changes: 5 additions & 1 deletion Grand.Web/Features/Handlers/Catalog/GetCategoryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Grand.Web.Models.Catalog;
using Grand.Web.Models.Media;
using MediatR;
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
Expand All @@ -34,6 +35,7 @@ public class GetCategoryHandler : IRequestHandler<GetCategory, CategoryModel>
private readonly IPictureService _pictureService;
private readonly ILocalizationService _localizationService;
private readonly ISpecificationAttributeService _specificationAttributeService;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly CatalogSettings _catalogSettings;
private readonly MediaSettings _mediaSettings;

Expand All @@ -47,6 +49,7 @@ public GetCategoryHandler(
IPictureService pictureService,
ILocalizationService localizationService,
ISpecificationAttributeService specificationAttributeService,
IHttpContextAccessor httpContextAccessor,
CatalogSettings catalogSettings,
MediaSettings mediaSettings)
{
Expand All @@ -59,6 +62,7 @@ public GetCategoryHandler(
_pictureService = pictureService;
_localizationService = localizationService;
_specificationAttributeService = specificationAttributeService;
_httpContextAccessor = httpContextAccessor;
_catalogSettings = catalogSettings;
_mediaSettings = mediaSettings;
}
Expand Down Expand Up @@ -199,7 +203,7 @@ public async Task<CategoryModel> Handle(GetCategory request, CancellationToken c
categoryIds.AddRange(await _mediator.Send(new GetChildCategoryIds() { ParentCategoryId = request.Category.Id, Customer = request.Customer, Store = request.Store }));
}
//products
IList<string> alreadyFilteredSpecOptionIds = model.PagingFilteringContext.SpecificationFilter.GetAlreadyFilteredSpecOptionIds(_webHelper);
IList<string> alreadyFilteredSpecOptionIds = await model.PagingFilteringContext.SpecificationFilter.GetAlreadyFilteredSpecOptionIds(_httpContextAccessor, _specificationAttributeService);
var products = (await _mediator.Send(new GetSearchProductsQuery() {
LoadFilterableSpecificationAttributeOptionIds = !_catalogSettings.IgnoreFilterableSpecAttributeOption,
CategoryIds = categoryIds,
Expand Down
6 changes: 1 addition & 5 deletions Grand.Web/Features/Models/Catalog/GetCategory.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Grand.Core.Domain.Catalog;
using Grand.Core.Domain.Catalog;
using Grand.Core.Domain.Customers;
using Grand.Core.Domain.Directory;
using Grand.Core.Domain.Localization;
Expand Down
65 changes: 47 additions & 18 deletions Grand.Web/Models/Catalog/CatalogPagingFilteringModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Grand.Services.Catalog;
using Grand.Services.Localization;
using Grand.Web.Infrastructure.Cache;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Rendering;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -271,19 +272,32 @@ protected virtual string GenerateFilteredSpecQueryParam(IList<string> optionIds)

#region Methods

public virtual List<string> GetAlreadyFilteredSpecOptionIds(IWebHelper webHelper)
public virtual async Task<List<string>> GetAlreadyFilteredSpecOptionIds
(IHttpContextAccessor httpContextAccessor, ISpecificationAttributeService specificationAttributeService)
{
var result = new List<string>();

var alreadyFilteredSpecsStr = webHelper.QueryString<string>(QUERYSTRINGPARAM);
if (String.IsNullOrWhiteSpace(alreadyFilteredSpecsStr))
return result;

foreach (var spec in alreadyFilteredSpecsStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
foreach (var item in httpContextAccessor.HttpContext.Request.Query)
{
if (!result.Contains(spec))
result.Add(spec);
var spec = await specificationAttributeService.GetSpecificationAttributeBySeName(item.Key);
if (spec != null)
{
foreach (var value in item.Value)
{
foreach (var option in value.Split(","))
{
var opt = spec.SpecificationAttributeOptions.FirstOrDefault(x => x.SeName == option.ToLowerInvariant());
if (opt != null)
{
if (!result.Contains(opt.Id))
result.Add(opt.Id);
}

}
}
}
}

return result;
}

Expand All @@ -305,13 +319,14 @@ public virtual async Task PrepareSpecsFilters(IList<string> alreadyFilteredSpecO
var sa = await specificationAttributeService.GetSpecificationAttributeByOptionId(sao);
if (sa != null)
{
_allFilters.Add(new SpecificationAttributeOptionFilter
{
_allFilters.Add(new SpecificationAttributeOptionFilter {
SpecificationAttributeId = sa.Id,
SpecificationAttributeName = sa.GetLocalized(x => x.Name, langId),
SpecificationAttributeSeName = sa.SeName,
SpecificationAttributeDisplayOrder = sa.DisplayOrder,
SpecificationAttributeOptionId = sao,
SpecificationAttributeOptionName = sa.SpecificationAttributeOptions.FirstOrDefault(x => x.Id == sao).GetLocalized(x => x.Name, langId),
SpecificationAttributeOptionSeName = sa.SpecificationAttributeOptions.FirstOrDefault(x => x.Id == sao).SeName,
SpecificationAttributeOptionDisplayOrder = sa.SpecificationAttributeOptions.FirstOrDefault(x => x.Id == sao).DisplayOrder,
SpecificationAttributeOptionColorRgb = sa.SpecificationAttributeOptions.FirstOrDefault(x => x.Id == sao).ColorSquaresRgb,
});
Expand All @@ -330,18 +345,27 @@ public virtual async Task PrepareSpecsFilters(IList<string> alreadyFilteredSpecO

//prepare the model properties
Enabled = true;
var removeFilterUrl = webHelper.ModifyQueryString(webHelper.GetThisPageUrl(true), QUERYSTRINGPARAM, null);
string removeFilterUrl = webHelper.GetThisPageUrl(true);
foreach (var item in allFilters.GroupBy(x => x.SpecificationAttributeSeName))
{
removeFilterUrl = webHelper.ModifyQueryString(removeFilterUrl, item.Key, null);
}
RemoveFilterUrl = ExcludeQueryStringParams(removeFilterUrl, webHelper);

//get already filtered specification options
var alreadyFilteredOptions = allFilters.Where(x => alreadyFilteredSpecOptionIds.Contains(x.SpecificationAttributeOptionId));
AlreadyFilteredItems = alreadyFilteredOptions.Select(x =>
{
var filterUrl = webHelper.ModifyQueryString(webHelper.GetThisPageUrl(true), QUERYSTRINGPARAM, GenerateFilteredSpecQueryParam(alreadyFilteredOptions.Where(y => y.SpecificationAttributeOptionId != x.SpecificationAttributeOptionId).Select(z => z.SpecificationAttributeOptionId).ToList()));
return new SpecificationFilterItem
{
var alreadyFiltered = alreadyFilteredOptions.Where(y => y.SpecificationAttributeId == x.SpecificationAttributeId).Select(z => z.SpecificationAttributeOptionSeName)
.Except(new List<string> { x.SpecificationAttributeOptionSeName }).ToList();

var filterUrl = webHelper.ModifyQueryString(webHelper.GetThisPageUrl(true), x.SpecificationAttributeSeName, GenerateFilteredSpecQueryParam(alreadyFiltered));

return new SpecificationFilterItem {
SpecificationAttributeName = x.SpecificationAttributeName,
SpecificationAttributeSeName = x.SpecificationAttributeSeName,
SpecificationAttributeOptionName = x.SpecificationAttributeOptionName,
SpecificationAttributeOptionSeName = x.SpecificationAttributeOptionSeName,
SpecificationAttributeOptionColorRgb = x.SpecificationAttributeOptionColorRgb,
FilterUrl = ExcludeQueryStringParams(filterUrl, webHelper)
};
Expand All @@ -351,12 +375,15 @@ public virtual async Task PrepareSpecsFilters(IList<string> alreadyFilteredSpecO
NotFilteredItems = allFilters.Except(alreadyFilteredOptions).Select(x =>
{
//filter URL
var alreadyFiltered = alreadyFilteredSpecOptionIds.Concat(new List<string> { x.SpecificationAttributeOptionId });
var filterUrl = webHelper.ModifyQueryString(webHelper.GetThisPageUrl(true), QUERYSTRINGPARAM, GenerateFilteredSpecQueryParam(alreadyFiltered.ToList()));
return new SpecificationFilterItem()
{
var alreadyFiltered = alreadyFilteredOptions.Where(y => y.SpecificationAttributeId == x.SpecificationAttributeId).Select(x => x.SpecificationAttributeOptionSeName)
.Concat(new List<string> { x.SpecificationAttributeOptionSeName });

var filterUrl = webHelper.ModifyQueryString(webHelper.GetThisPageUrl(true), x.SpecificationAttributeSeName, GenerateFilteredSpecQueryParam(alreadyFiltered.ToList()));
return new SpecificationFilterItem() {
SpecificationAttributeName = x.SpecificationAttributeName,
SpecificationAttributeSeName = x.SpecificationAttributeSeName,
SpecificationAttributeOptionName = x.SpecificationAttributeOptionName,
SpecificationAttributeOptionSeName = x.SpecificationAttributeOptionSeName,
SpecificationAttributeOptionColorRgb = x.SpecificationAttributeOptionColorRgb,
FilterUrl = ExcludeQueryStringParams(filterUrl, webHelper)
};
Expand All @@ -377,7 +404,9 @@ public virtual async Task PrepareSpecsFilters(IList<string> alreadyFilteredSpecO
public partial class SpecificationFilterItem : BaseGrandModel
{
public string SpecificationAttributeName { get; set; }
public string SpecificationAttributeSeName { get; set; }
public string SpecificationAttributeOptionName { get; set; }
public string SpecificationAttributeOptionSeName { get; set; }
public string SpecificationAttributeOptionColorRgb { get; set; }
public string FilterUrl { get; set; }
}
Expand Down

0 comments on commit 0281188

Please sign in to comment.