Commit b7d40f9a authored by boncho vylkov's avatar boncho vylkov

add some database tables for product cart demo system,product service and product controller

parent 756a1d87
......@@ -3,12 +3,15 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using MvcCoreTemplate.Data.Common.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public class ApplicationUser : IdentityUser, IAuditInfo, IDeletableEntity
{
public ApplicationUser()
{
this.CreatedOn = DateTime.UtcNow;
this.PlacedOrders = new HashSet<PlacedOrder>();
}
public DateTime CreatedOn { get; set; }
......@@ -18,5 +21,13 @@
public bool IsDeleted { get; set; }
public DateTime? DeletedOn { get; set; }
public Cart Cart { get; set; }
public OrderDetails OrderDetails { get; set; }
public ICollection<PlacedOrder> PlacedOrders { get; set; }
}
}
using MvcCoreTemplate.Data.Common.Models;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MvcCoreTemplate.Data.Models
{
public class Cart : BaseModel<int>
{
public Cart()
{
this.Products = new HashSet<Product>();
}
[Required]
public string ApplicationUserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
//this should be saved anyway
public string Ip { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
}
using MvcCoreTemplate.Data.Common.Models;
using System.ComponentModel.DataAnnotations;
namespace MvcCoreTemplate.Data.Models
{
public class Category : BaseModel<int>
{
[MaxLength(256)]
public string Name { get; set; }
}
}
\ No newline at end of file
using System.ComponentModel.DataAnnotations;
namespace MvcCoreTemplate.Data.Models
{
public class GalleryImage : Image
{
public int Order { get; set; }
[Required]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
}
\ No newline at end of file
using MvcCoreTemplate.Data.Common.Models;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace MvcCoreTemplate.Data.Models
{
public class Image : BaseModel<int>
{
[Required]
[MaxLength(256)]
public string Url { get; set; }
[MaxLength(256)]
//TODO: add index in the migration
public string Title { get; set; }
}
}
\ No newline at end of file
namespace MvcCoreTemplate.Data.Models
{
using MvcCoreTemplate.Data.Common.Models;
public class OrderDetails : BaseModel<int>
{
public string ReceiverPhone { get; set; }
public string ReceiverEmail { get; set; }
public string ReceiverName { get; set; }
public string ShipCountry { get; set; }
public string ShipCity { get; set; }
public string ShipAddress { get; set; }
public string PostCode { get; set; }
//might have user, or might not
public string ApplicationUserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
public int? PlacedOrderId { get; set; }
public virtual PlacedOrder PlacedOrder { get; set; }
}
}
using MvcCoreTemplate.Data.Common.Models;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MvcCoreTemplate.Data.Models
{
public class PlacedOrder : BaseModel<int>
{
public PlacedOrder()
{
this.Products = new HashSet<Product>();
}
public virtual ICollection<Product> Products { get; set; }
//all these would be filled automatically if the user is registered
//public int OrderDetailsId { get; set; }
public OrderDetails OrderDetails { get; set; }
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
}
using MvcCoreTemplate.Data.Common.Models;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MvcCoreTemplate.Data.Models
{
public class Product : BaseModel<int>
{
public Product()
{
this.GalleryImages = new HashSet<GalleryImage>();
}
[MaxLength(512)]
//TODO: add index
public string Name { get; set; }
[MaxLength(2048)]
public string Description { get; set; }
public decimal Price { get; set; }
public decimal? DiscountPercent { get; set; }
public bool? IsInHouse { get; set; }
public int? CategoryId { get; set; }
public virtual Category Category { get; set; }
[Required]
public string ImageUrl { get; set; }
public virtual ICollection<GalleryImage> GalleryImages { get; set; }
}
}
using MvcCoreTemplate.Data.Common.Models;
using System.ComponentModel.DataAnnotations;
namespace MvcCoreTemplate.Data.Models
{
public class ProductOrder : BaseModel<int>
{
[Required]
public int ProductId { get; set; }
public virtual Product Product { get; set; }
public string ApplicationUserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
}
}
namespace MvcCoreTemplate.Data.Models
{
using MvcCoreTemplate.Data.Common.Models;
public class Setting : BaseModel<int>
{
public string Name { get; set; }
public string Value { get; set; }
}
}
......@@ -26,6 +26,24 @@ namespace MvcCoreTemplate.Data
public DbSet<Feature> Features { get; set; }
public DbSet<Setting> Settings { get; set; }
public DbSet<Cart> Carts { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<GalleryImage> GalleryImages { get; set; }
public DbSet<Image> Images { get; set; }
public DbSet<OrderDetails> OrderDetails { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<ProductOrder> ProductOrders { get; set; }
public DbSet<PlacedOrder> PlacedOrders { get; set; }
public override int SaveChanges()
{
this.ApplyAuditInfoRules();
......
......@@ -7,7 +7,19 @@ namespace MvcCoreTemplate.Data.UnitOfWork
public interface IUowData
{
IDeletableEntityRepository<Feature> Features { get; }
IDeletableEntityRepository<Setting> Settings { get; }
IDeletableEntityRepository<Cart> Carts { get; }
IDeletableEntityRepository<Category> Categories { get; }
IDeletableEntityRepository<GalleryImage> GalleryImages { get; }
IDeletableEntityRepository<Image> Images { get; }
IDeletableEntityRepository<OrderDetails> OrderDetails { get; }
IDeletableEntityRepository<Product> Products { get; }
IDeletableEntityRepository<ProductOrder> ProductOrders { get; }
IDeletableEntityRepository<PlacedOrder> PlacedOrders { get; }
Task<int> SaveChangesAsync();
void Dispose();
......
......@@ -20,6 +20,28 @@ namespace MvcCoreTemplate.Data.UnitOfWork
get { return this.GetRepository<Feature>(); }
}
public IDeletableEntityRepository<Setting> Settings
{
get { return this.GetRepository<Setting>(); }
}
public IDeletableEntityRepository<Cart> Carts
{
get { return this.GetRepository<Cart>(); }
}
public IDeletableEntityRepository<Category> Categories => this.GetRepository<Category>();
public IDeletableEntityRepository<GalleryImage> GalleryImages => this.GetRepository<GalleryImage>();
public IDeletableEntityRepository<Image> Images => this.GetRepository<Image>();
public IDeletableEntityRepository<OrderDetails> OrderDetails => this.GetRepository<OrderDetails>();
public IDeletableEntityRepository<Product> Products => this.GetRepository<Product>();
public IDeletableEntityRepository<ProductOrder> ProductOrders => this.GetRepository<ProductOrder>();
public IDeletableEntityRepository<PlacedOrder> PlacedOrders => this.GetRepository<PlacedOrder>();
public UowData(ApplicationDbContext context)
{
this.context = context;
......
namespace MvcCoreTemplate.Services.Data
{
using MvcCoreTemplate.Web.Infrastructure.Models.Products;
using System.Collections.Generic;
public interface IProductService
{
IEnumerable<ProductListViewModel> GetProductsByName(string name, int? skip = 0, int? take = 10);
IEnumerable<ProductListViewModel> GetProductsByCategory(int categoryId, int? skip = 0, int? take = 10);
IEnumerable<ProductListViewModel> GetProductsByCategoryName(string categoryName, int? skip = 0, int? take = 10);
IEnumerable<ProductListViewModel> GetProductsByNameAndToPrice(string name,decimal price, int? skip = 0, int? take = 10);
IEnumerable<ProductListViewModel> GetProductsByNameAndFromPrice(string name, decimal price, int? skip = 0, int? take = 10);
IEnumerable<ProductListViewModel> GetLatestProducts(int? skip = 0, int? take = 10);
ProductDetailsViewModel GetProductDetails(int id);
}
}
......@@ -6,6 +6,7 @@
<ItemGroup>
<ProjectReference Include="..\MvcCoreTemplate.Data.Models\MvcCoreTemplate.Data.Models.csproj" />
<ProjectReference Include="..\MvcCoreTemplate.Data\MvcCoreTemplate.Data.csproj" />
<ProjectReference Include="..\MvcCoreTemplate.Web.Infrastructure\MvcCoreTemplate.Web.Infrastructure.csproj" />
</ItemGroup>
......
using System;
using System.Collections.Generic;
using MvcCoreTemplate.Data.Common.Repositories;
using MvcCoreTemplate.Data.Models;
using MvcCoreTemplate.Web.Infrastructure.Models.Products;
using MvcCoreTemplate.Data.UnitOfWork;
using System.Linq;
using MvcCoreTemplate.Web.Infrastructure.Mapping;
using AutoMapper;
namespace MvcCoreTemplate.Services.Data
{
public class ProductService : IProductService
{
private IUowData data;
public ProductService(IUowData data)
{
this.data = data;
}
public IEnumerable<ProductListViewModel> GetLatestProducts(int? skip = 0, int? take = 10)
{
var products = data.Products.All()
.OrderByDescending(p => p.CreatedOn).Skip(skip.Value).Take(take.Value).To<ProductListViewModel>().ToList();
return products;
}
public ProductDetailsViewModel GetProductDetails(int id)
{
var product = this.data.Products.All().FirstOrDefault(p => p.Id == id);
var productViewModel = Mapper.Map<ProductDetailsViewModel>(product);
return productViewModel;
}
public IEnumerable<ProductListViewModel> GetProductsByCategory(int categoryId, int? skip = 0, int? take = 10)
{
var products = data.Products.All().Where(p => p.CategoryId == categoryId)
.OrderBy(p => p.CreatedOn).Skip(skip.Value).Take(take.Value).To<ProductListViewModel>().ToList();
return products;
}
public IEnumerable<ProductListViewModel> GetProductsByCategoryName(string categoryName, int? skip = 0, int? take = 10)
{
var products = data.Products.All().Where(p => p.Category !=null && p.Category.Name.Contains(categoryName))
.OrderBy(p => p.CreatedOn).Skip(skip.Value).Take(take.Value).To<ProductListViewModel>().ToList();
return products;
}
public IEnumerable<ProductListViewModel> GetProductsByName(string name, int? skip = 0, int? take = 10)
{
var products = data.Products.All().Where(p => p.Name.Contains(name))
.OrderBy(p => p.CreatedOn).Skip(skip.Value).Take(take.Value).To<ProductListViewModel>().ToList();
return products;
}
public IEnumerable<ProductListViewModel> GetProductsByNameAndFromPrice(string name, decimal price, int? skip = 0, int? take = 10)
{
var products = data.Products.All().Where(p => p.Name.Contains(name) && p.Price >=price)
.OrderBy(p => p.CreatedOn).Skip(skip.Value).Take(take.Value).To<ProductListViewModel>().ToList();
return products;
}
public IEnumerable<ProductListViewModel> GetProductsByNameAndToPrice(string name, decimal price, int? skip = 0, int? take = 10)
{
var products = data.Products.All().Where(p => p.Name.Contains(name) && p.Price <= price)
.OrderBy(p => p.CreatedOn).Skip(skip.Value).Take(take.Value).To<ProductListViewModel>().ToList();
return products;
}
}
}
using MvcCoreTemplate.Data.Models;
using MvcCoreTemplate.Web.Infrastructure.Mapping;
using System;
using System.Collections.Generic;
using System.Text;
namespace MvcCoreTemplate.Web.Infrastructure.Models.Images
{
public class GalleryImageViewModel : ImageViewModel,IMapFrom<GalleryImage>
{
public int Order { get; set; }
public int ProductId { get; set; }
}
}
using MvcCoreTemplate.Data.Models;
using MvcCoreTemplate.Web.Infrastructure.Mapping;
using MvcCoreTemplate.Web.Infrastructure.Models.Base;
using System;
using System.Collections.Generic;
using System.Text;
namespace MvcCoreTemplate.Web.Infrastructure.Models.Images
{
public class ImageViewModel : BaseViewModel<int>,IMapFrom<Image>
{
public string Url { get; set; }
public string Title { get; set; }
}
}
using MvcCoreTemplate.Web.Infrastructure.Mapping;
using System;
using System.Collections.Generic;
using System.Text;
using AutoMapper;
using MvcCoreTemplate.Data.Models;
using MvcCoreTemplate.Web.Infrastructure.Models.Images;
namespace MvcCoreTemplate.Web.Infrastructure.Models.Products
{
public class ProductDetailsViewModel : ProductListViewModel
{
public ProductDetailsViewModel()
{
this.GalleryImages = new List<GalleryImageViewModel>();
}
public IEnumerable<GalleryImageViewModel> GalleryImages { get; set; }
}
}
namespace MvcCoreTemplate.Web.Infrastructure.Models.Products
{
using System;
using AutoMapper;
using MvcCoreTemplate.Data.Models;
using MvcCoreTemplate.Web.Infrastructure.Mapping;
using MvcCoreTemplate.Web.Infrastructure.Models.Base;
public class ProductListViewModel : BaseViewModel<int>, IMapFrom<Product>, IHaveCustomMappings
{
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public decimal? DiscountPercent { get; set; }
public bool? IsInHouse { get; set; }
public int? CategoryId { get; set; }
public string Category { get; set; }
public string ImageUrl { get; set; }
public virtual void CreateMappings(IMapperConfigurationExpression configuration)
{
configuration.CreateMap<Product, ProductListViewModel>()
.ForMember(m => m.Category, opt => opt.MapFrom(t => t.Category !=null ? t.Category.Name :null))
.ReverseMap();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcCoreTemplate.Services.Data;
namespace MvcCoreTemplate.Web.Controllers
{
public class ProductsController : BaseController
{
private IProductService productsService;
public ProductsController(IProductService productsService,ICommonService commonService) : base(commonService)
{
this.productsService = productsService;
}
public IActionResult Index(int? skip,int? take)
{
var products = productsService.GetLatestProducts(skip, take);
return View(products);
}
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using MvcCoreTemplate.Web.Models;
namespace MvcCoreTemplate.Web.Data
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace MvcCoreTemplate.Web.Data.Migrations
{
public partial class CreateIdentitySchema : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
ConcurrencyStamp = table.Column<string>(nullable: true),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
LoginProvider = table.Column<string>(nullable: false),
Name = table.Column<string>(nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false),
ConcurrencyStamp = table.Column<string>(nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
LockoutEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
PasswordHash = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
SecurityStamp = table.Column<string>(nullable: true),
TwoFactorEnabled = table.Column<bool>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true),
RoleId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});