Webiant Logo Webiant Logo
  1. No results found.

    Try your search with a different keyword or use * as a wildcard.

LocalizedEntityService.cs

using System.Linq.Expressions;
using System.Reflection;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Domain.Localization;
using Nop.Data;


namespace Nop.Services.Localization;

/// 
/// Provides information about localizable entities
/// 
public partial class LocalizedEntityService : ILocalizedEntityService
{
    #region Fields

    protected readonly IRepository _localizedPropertyRepository;
    protected readonly IStaticCacheManager _staticCacheManager;
    protected readonly LocalizationSettings _localizationSettings;

    #endregion

    #region Ctor

    public LocalizedEntityService(IRepository localizedPropertyRepository,
        IStaticCacheManager staticCacheManager,
        LocalizationSettings localizationSettings)
    {
        _localizedPropertyRepository = localizedPropertyRepository;
        _staticCacheManager = staticCacheManager;
        _localizationSettings = localizationSettings;
    }

    #endregion

    #region Utilities

    /// 
    /// Gets localized properties
    /// 
    /// Entity identifier
    /// Locale key group
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the localized properties
    /// 
    protected virtual async Task> GetLocalizedPropertiesAsync(int entityId, string localeKeyGroup)
    {
        if (entityId == 0 || string.IsNullOrEmpty(localeKeyGroup))
            return new List();

        var query = from lp in _localizedPropertyRepository.Table
            orderby lp.Id
            where lp.EntityId == entityId &&
                  lp.LocaleKeyGroup == localeKeyGroup
            select lp;

        var props = await query.ToListAsync();

        return props;
    }

    /// 
    /// Gets all cached localized properties
    /// 
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the cached localized properties
    /// 
    protected virtual async Task> GetAllLocalizedPropertiesAsync()
    {
        return await _localizedPropertyRepository.GetAllAsync(query =>
        {
            return from lp in query
                select lp;
        }, cache => default);
    }

    /// 
    /// Gets all cached localized properties by language
    /// 
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the uncached localized properties
    /// 
    protected virtual async Task> GetAllLocalizedPropertiesAsync(int languageId)
    {
        // do not cache here
        return await _localizedPropertyRepository.GetAllAsync(query => query.Where(lp => lp.LanguageId == languageId));
    }

    /// 
    /// Deletes a localized property
    /// 
    /// Localized property
    /// A task that represents the asynchronous operation
    protected virtual async Task DeleteLocalizedPropertyAsync(LocalizedProperty localizedProperty)
    {
        await _localizedPropertyRepository.DeleteAsync(localizedProperty);
    }

    /// 
    /// Inserts a localized property
    /// 
    /// Localized property
    /// A task that represents the asynchronous operation
    protected virtual async Task InsertLocalizedPropertyAsync(LocalizedProperty localizedProperty)
    {
        await _localizedPropertyRepository.InsertAsync(localizedProperty);
    }

    /// 
    /// Updates the localized property
    /// 
    /// Localized property
    /// A task that represents the asynchronous operation
    protected virtual async Task UpdateLocalizedPropertyAsync(LocalizedProperty localizedProperty)
    {
        await _localizedPropertyRepository.UpdateAsync(localizedProperty);
    }

    #endregion

    #region Methods

    /// 
    /// Find localized properties
    /// 
    /// Entity identifier
    /// Locale key group
    /// Locale key
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the found localized properties
    /// 
    public virtual async Task> GetEntityLocalizedPropertiesAsync(int entityId, string localeKeyGroup, string localeKey)
    {
        var key = _staticCacheManager.PrepareKeyForDefaultCache(NopLocalizationDefaults.LocalizedPropertiesCacheKey,
            entityId, localeKeyGroup, localeKey);

        return await _staticCacheManager.GetAsync(key, async () =>
        {
            var source = _localizationSettings.LoadAllLocalizedPropertiesOnStartup
                //load all records (we know they are cached)
                ? (await GetAllLocalizedPropertiesAsync()).AsQueryable()
                //gradual loading
                : _localizedPropertyRepository.Table;

            var query = from lp in source
                where lp.EntityId == entityId &&
                      lp.LocaleKeyGroup == localeKeyGroup &&
                      lp.LocaleKey == localeKey
                select lp;

            return await query.ToListAsync();
        });
    }

    /// 
    /// Find localized value
    /// 
    /// Language identifier
    /// Entity identifier
    /// Locale key group
    /// Locale key
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the found localized value
    /// 
    public virtual async Task GetLocalizedValueAsync(int languageId, int entityId, string localeKeyGroup, string localeKey)
    {
        if (_localizationSettings.LoadAllLocalizedPropertiesOnStartup)
        {
            //value tuples aren't json-serializable by default, so we use a string key
            static string formatKey(string keyGroup, string key, int id) => $"{keyGroup}:{key}:{id}";

            var localizedValues = await _staticCacheManager.GetAsync(
                _staticCacheManager.PrepareKeyForDefaultCache(NopLocalizationDefaults.LocalizedPropertyLookupCacheKey, languageId),
                async () => (await GetAllLocalizedPropertiesAsync(languageId))
                    .GroupBy(p => formatKey(p.LocaleKeyGroup, p.LocaleKey, p.EntityId))
                    .ToDictionary(g => g.Key, g => g.First().LocaleValue));

            return localizedValues.TryGetValue(formatKey(localeKeyGroup, localeKey, entityId), out var localeValue)
                ? localeValue
                : string.Empty;
        }

        //gradual loading
        var key = _staticCacheManager.PrepareKeyForDefaultCache(NopLocalizationDefaults.LocalizedPropertyCacheKey
            , languageId, entityId, localeKeyGroup, localeKey);

        return await _staticCacheManager.GetAsync(key, async () =>
        {
            var query = from lp in _localizedPropertyRepository.Table
                where lp.LanguageId == languageId &&
                      lp.EntityId == entityId &&
                      lp.LocaleKeyGroup == localeKeyGroup &&
                      lp.LocaleKey == localeKey
                select lp.LocaleValue;

            //little hack here. nulls aren't cacheable so set it to ""
            return await query.FirstOrDefaultAsync() ?? string.Empty;
        });
    }

    /// 
    /// Save localized value
    /// 
    /// Type
    /// Entity
    /// Key selector
    /// Locale value
    /// Language ID
    /// A task that represents the asynchronous operation
    public virtual async Task SaveLocalizedValueAsync(T entity,
        Expression> keySelector,
        string localeValue,
        int languageId) where T : BaseEntity, ILocalizedEntity
    {
        await SaveLocalizedValueAsync(entity, keySelector, localeValue, languageId);
    }

    /// 
    /// Save localized value
    /// 
    /// Type
    /// Property type
    /// Entity
    /// Key selector
    /// Locale value
    /// Language ID
    /// A task that represents the asynchronous operation
    public virtual async Task SaveLocalizedValueAsync(T entity,
        Expression> keySelector,
        TPropType localeValue,
        int languageId) where T : BaseEntity, ILocalizedEntity
    {
        ArgumentNullException.ThrowIfNull(entity);

        if (languageId == 0)
            throw new ArgumentOutOfRangeException(nameof(languageId), "Language ID should not be 0");

        if (keySelector.Body is not MemberExpression member)
        {
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a method, not a property.",
                keySelector));
        }

        var propInfo = member.Member as PropertyInfo ?? throw new ArgumentException(string.Format(
            "Expression '{0}' refers to a field, not a property.",
            keySelector));

        //load localized value (check whether it's a cacheable entity. In such cases we load its original entity type)
        var localeKeyGroup = entity.GetType().Name;
        var localeKey = propInfo.Name;

        var props = await GetLocalizedPropertiesAsync(entity.Id, localeKeyGroup);
        var prop = props.FirstOrDefault(lp => lp.LanguageId == languageId &&
                                              lp.LocaleKey.Equals(localeKey, StringComparison.InvariantCultureIgnoreCase)); //should be culture invariant

        var localeValueStr = CommonHelper.To(localeValue);

        if (prop != null)
        {
            if (string.IsNullOrWhiteSpace(localeValueStr))
            {
                //delete
                await DeleteLocalizedPropertyAsync(prop);
            }
            else
            {
                //update
                prop.LocaleValue = localeValueStr;
                await UpdateLocalizedPropertyAsync(prop);
            }
        }
        else
        {
            if (string.IsNullOrWhiteSpace(localeValueStr))
                return;

            //insert
            prop = new LocalizedProperty
            {
                EntityId = entity.Id,
                LanguageId = languageId,
                LocaleKey = localeKey,
                LocaleKeyGroup = localeKeyGroup,
                LocaleValue = localeValueStr
            };
            await InsertLocalizedPropertyAsync(prop);
        }
    }

    #endregion
}