Implemented category feature
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
using LiteCharms.Features.MidrandBooks.Abstractions;
|
||||
using LiteCharms.Features.MidrandBooks.Categories.Models;
|
||||
using LiteCharms.Features.MidrandBooks.Extensions;
|
||||
using LiteCharms.Features.MidrandBooks.Postgres;
|
||||
|
||||
namespace LiteCharms.Features.MidrandBooks.Categories;
|
||||
|
||||
public sealed class CategoryService(IDbContextFactory<MidrandBooksDbContext> contextFactory) : IService
|
||||
{
|
||||
public async ValueTask<Result> UpdateCategoryStatusAsync(long categoryId, bool enabled, bool isMain, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var rowsUpdated = await context.Categories
|
||||
.Where(c => c.Id == categoryId && c.Enabled)
|
||||
.ExecuteUpdateAsync(setters => setters
|
||||
.SetProperty(c => c.Enabled, enabled)
|
||||
.SetProperty(c => c.IsMain, isMain), cancellationToken);
|
||||
|
||||
return rowsUpdated > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail(new Error($"Failed to update category"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result<Category>> GetCategoryAsync(long categoryId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var category = await context.Categories.AsNoTracking().FirstOrDefaultAsync(c => c.Id == categoryId, cancellationToken);
|
||||
|
||||
return category is not null
|
||||
? Result.Ok(category.ToModel())
|
||||
: Result.Fail<Category>("Failed to create new category");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Category>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result<Category[]>> GetCategoriesAsync(bool? isMain = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
var query = context.Categories.AsNoTracking()
|
||||
.OrderByDescending(o => o.IsMain)
|
||||
.ThenByDescending(o => o.Id)
|
||||
.ThenBy(o => o.IsMain)
|
||||
.AsQueryable();
|
||||
|
||||
query = isMain is null
|
||||
? query.Where(c => c.Enabled).AsQueryable()
|
||||
: query.Where(c => c.Enabled && c.IsMain == isMain.Value);
|
||||
|
||||
var categories = await query.ToListAsync(cancellationToken);
|
||||
|
||||
return categories?.Count > 0
|
||||
? Result.Ok(categories.Select(c => c.ToModel()).ToArray())
|
||||
: Result.Fail<Category[]>("No categories found");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail<Category[]>(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result> CreateCategoriesAsync(CancellationToken cancellationToken = default, params string[] categories)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
foreach (var category in categories)
|
||||
{
|
||||
if (await context.Categories.AnyAsync(c => EF.Functions.ILike(c.Name!, category!), cancellationToken))
|
||||
continue;
|
||||
|
||||
context.Categories.Add(new Entities.Category
|
||||
{
|
||||
Name = category.Humanize(LetterCasing.Title),
|
||||
IsMain = false,
|
||||
Enabled = true,
|
||||
});
|
||||
}
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok()
|
||||
: Result.Fail("Failed to add any category in the list");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask<Result<long>> CreateCategoryAsync(string category, bool isMain, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
await using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
|
||||
|
||||
if (await context.Categories.AnyAsync(c => EF.Functions.ILike(c.Name!, category!), cancellationToken))
|
||||
return Result.Fail($"Category '{category}' already exists");
|
||||
|
||||
var newCategory = context.Categories.Add(new Entities.Category
|
||||
{
|
||||
Name = StringHumanizeExtensions.Humanize(category, LetterCasing.Title),
|
||||
IsMain = isMain,
|
||||
Enabled = true,
|
||||
});
|
||||
|
||||
return await context.SaveChangesAsync(cancellationToken) > 0
|
||||
? Result.Ok(newCategory.Entity.Id)
|
||||
: Result.Fail("Failed to create new category");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace LiteCharms.Features.MidrandBooks.Categories.Entities;
|
||||
|
||||
[EntityTypeConfiguration<CategoryConfiguration, Category>]
|
||||
public sealed class Category : Models.Category;
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace LiteCharms.Features.MidrandBooks.Categories.Entities;
|
||||
|
||||
public sealed class CategoryConfiguration : IEntityTypeConfiguration<Category>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<Category> builder)
|
||||
{
|
||||
builder.ToTable("Categories");
|
||||
|
||||
builder.HasKey(c => c.Id);
|
||||
builder.Property(c => c.Name).IsRequired().HasMaxLength(15);
|
||||
builder.Property(c => c.IsMain).HasDefaultValue(false);
|
||||
builder.Property(c => c.Enabled).HasDefaultValue(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace LiteCharms.Features.MidrandBooks.Categories.Models;
|
||||
|
||||
public class Category
|
||||
{
|
||||
public long Id { get; set; }
|
||||
|
||||
public string? Name { get; set; }
|
||||
|
||||
public bool IsMain { get; set; }
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user