Advertisement
Guest User

ProductController

a guest
May 7th, 2020
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 20.65 KB | None | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data.Entity;
  4. using System.Data.Entity.Infrastructure;
  5. using System.Linq;
  6. using System.Net;
  7. using System.Threading.Tasks;
  8. using System.Web.Mvc;
  9. using BabyStore.DAL;
  10. using BabyStore.Models.BabyStoreModelClasses;
  11. using BabyStore.RepositoryLayer;
  12. using BabyStore.RepositoryLayer.UnitOfWork;
  13. using BabyStore.ViewModel.Products;
  14. using BabyStore.Utilities;
  15. using Serilog;
  16.  
  17. namespace BabyStore.Controllers.ModelControllers
  18. {
  19.     [Authorize(Roles = "Admin")]
  20.     public class ProductsController : Controller
  21.     {
  22.         private readonly IUnitOfWork<StoreContext> _unitOfWork = new GenericUnitOfWork<StoreContext>();
  23.         private readonly IGenericRepository<Category> _categoryRepo;
  24.         private readonly IGenericRepository<Product> _productRepo;
  25.         private readonly IGenericRepository<ProductImage> _productImageRepo;
  26.  
  27.         #region Constructors
  28.         public ProductsController()
  29.         {
  30.             _categoryRepo = _unitOfWork.GenericRepository<Category>();
  31.             _productRepo = _unitOfWork.GenericRepository<Product>();
  32.             _productImageRepo = _unitOfWork.GenericRepository<ProductImage>();
  33.         }
  34.  
  35.         public ProductsController(IUnitOfWork<StoreContext> unitOfWork)
  36.         {
  37.             _unitOfWork = unitOfWork;
  38.             _categoryRepo = _unitOfWork.GenericRepository<Category>();
  39.             _productRepo = _unitOfWork.GenericRepository<Product>();
  40.             _productImageRepo = _unitOfWork.GenericRepository<ProductImage>();
  41.         }
  42.         #endregion
  43.  
  44.         // GET: Products
  45.         [AllowAnonymous]
  46.         public async Task<ActionResult> Index(string category, string search, string sortBy, int? page)
  47.         {
  48.             ProductIndexViewModel viewModel = new ProductIndexViewModel();
  49.  
  50.             var products = _productRepo.GetTable().Include(p => p.Category);
  51.  
  52.             if (!string.IsNullOrWhiteSpace(search))
  53.             {
  54.                 products = SearchProductsFor(search, products);
  55.                 viewModel.Search = search;
  56.             }
  57.            
  58.             GroupSearchIntoCategoriesAndCountPerCategory(viewModel, products);
  59.  
  60.             if (!string.IsNullOrWhiteSpace(category))
  61.             {
  62.                 products = products.Where(p => p.Category.Name == category);
  63.                 viewModel.Category = category;
  64.             }
  65.            
  66.             products = SortProducts(sortBy, products);
  67.      
  68.             await SetViewModelPages(viewModel, products, page);
  69.             SetViewModelSortProperties(sortBy, viewModel);
  70.  
  71.             return View(viewModel);
  72.         }
  73.  
  74.         // GET: Products/Details/5
  75.         [AllowAnonymous]
  76.         public ActionResult Details(int? id)
  77.         {
  78.             if (id == null)
  79.             {
  80.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
  81.             }
  82.             Product product = _productRepo.Find(id);
  83.             if (product == null)
  84.             {
  85.                 return HttpNotFound();
  86.             }
  87.             return View(product);
  88.         }
  89.  
  90.         // GET: Products/Create
  91.         public ActionResult Create()
  92.         {
  93.             ProductViewModel viewModel = new ProductViewModel();
  94.             viewModel.CategoryList = new SelectList(_categoryRepo.GetTable(), "ID", "Name");
  95.             viewModel.ImageLists = new List<SelectList>();
  96.             for (int i = 0; i < Constants.NumberOfProductImages; i++)
  97.             {
  98.                 viewModel.ImageLists.Add(new SelectList(_productImageRepo.GetTable(), "ID","FileName"));
  99.             }
  100.  
  101.             return View(viewModel);
  102.         }
  103.  
  104.         // POST: Products/Create
  105.         // To protect from overposting attacks, please enable the specific properties you want to bind to, for
  106.         // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
  107.         [HttpPost]
  108.         [ValidateAntiForgeryToken]
  109.         public ActionResult Create(ProductViewModel viewModel)
  110.         {
  111.             var product = new Product();
  112.             product.Name = viewModel.Name;
  113.             product.Category = _categoryRepo.Find(viewModel.CategoryID);
  114.             product.CategoryID = viewModel.CategoryID;
  115.             product.Description = viewModel.Description;
  116.             product.Price = viewModel.Price;
  117.             product.ProductImageMappings = new List<ProductImageMapping>();
  118.             string[] productImages = viewModel.ProductImages.Where(pi => !string.IsNullOrWhiteSpace(pi)).ToArray();
  119.  
  120.             for (int i = 0; i < productImages.Length; i++)
  121.             {
  122.                 var prodImageMapping = new ProductImageMapping();
  123.                 prodImageMapping.ImageNumber = i;
  124.                 prodImageMapping.ProductImage = _productImageRepo.Find(int.Parse(productImages[i]));
  125.  
  126.                 product.ProductImageMappings.Add(prodImageMapping);
  127.             }
  128.  
  129.             if (ModelState.IsValid)
  130.             {
  131.                 _productRepo.Add(product);
  132.                 _unitOfWork.Save();
  133.                 return RedirectToAction("Index");
  134.             }
  135.             var allCategories = _categoryRepo.GetTable();
  136.  
  137.             viewModel.CategoryList = new SelectList(allCategories, "ID", "Name", product.CategoryID);
  138.             viewModel.ImageLists = new List<SelectList>();
  139.             for (int i = 0; i < Constants.NumberOfProductImages; i++)
  140.             {
  141.                 viewModel.ImageLists.Add(new SelectList(_productImageRepo.GetTable(), "ID", "FileName", viewModel.ProductImages[i]));
  142.             }
  143.             ViewBag.CategoryID = new SelectList(allCategories, "ID", "Name", product.CategoryID);
  144.  
  145.             return View(product);
  146.         }
  147.  
  148.         // GET: Products/Edit/5
  149.         public ActionResult Edit(int? id)
  150.         {
  151.             if (id == null)
  152.             {
  153.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
  154.             }
  155.             Product product = _productRepo.Find(id);
  156.             if (product == null)
  157.             {
  158.                 return HttpNotFound();
  159.             }
  160.  
  161.             ProductViewModel viewModel = MapProductToViewModel(product);
  162.  
  163.             return View(viewModel);
  164.         }
  165.  
  166.         // POST: Products/Edit/5
  167.         // To protect from overposting attacks, please enable the specific properties you want to bind to, for
  168.         // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
  169.         [HttpPost]
  170.         [ValidateAntiForgeryToken]
  171.         public ActionResult Edit(ProductViewModel viewModel)
  172.         {
  173.             string[] fieldsToBind = new string[] { "Name", "Description", "Price", "CategoryID" };
  174.             var productToUpdate = _productRepo.GetTable().Include(p => p.ProductImageMappings).Single(p => p.ID == viewModel.ID);
  175.  
  176.             if (productToUpdate == null)
  177.             {
  178.                 Product deletedProduct = new Product();
  179.                 TryUpdateModel(deletedProduct, fieldsToBind);
  180.                 ModelState.AddModelError(string.Empty, "Unable to save your changes because the product has been deleted by another user.");
  181.  
  182.                 ProductViewModel outViewModel = MapProductToViewModel(deletedProduct);
  183.                 return View(outViewModel);
  184.             }
  185.  
  186.             if (TryUpdateModel(productToUpdate, "", fieldsToBind))
  187.             {
  188.                
  189.                 if (productToUpdate.ProductImageMappings == null)
  190.                 {
  191.                     productToUpdate.ProductImageMappings = new List<ProductImageMapping>();
  192.                 }
  193.  
  194.                 string[] submitedImages = viewModel.ProductImages.Where(pi => !string.IsNullOrEmpty(pi)).ToArray();
  195.                 for (int i = 0; i < submitedImages.Length; i++)
  196.                 {
  197.                     var imageMappingToEdit =
  198.                         productToUpdate.ProductImageMappings.FirstOrDefault(pi => pi.ImageNumber == i);
  199.                     var image = _productImageRepo.Find(int.Parse(submitedImages[i]));
  200.  
  201.                     //if there is nothing stored then we need to add a new mapping
  202.                     if (imageMappingToEdit == null)
  203.                     {
  204.                         //add image to the imagemappings
  205.                         productToUpdate.ProductImageMappings.Add(new ProductImageMapping
  206.                         {
  207.                             ImageNumber = i,
  208.                             ProductImage = image,
  209.                             ProductImageID = image.ID
  210.                         });
  211.                     }
  212.                     //else it's not a new file so edit the current mapping
  213.                     else
  214.                     {
  215.                         //if they are not the same
  216.                         if (imageMappingToEdit.ProductImageID != int.Parse(submitedImages[i]))
  217.                         {
  218.                             //assign image property of the image mapping
  219.                             imageMappingToEdit.ProductImage = image;
  220.                         }
  221.                     }
  222.                 }
  223.  
  224.                 //remove old images if they differ from new submited iamges on product
  225.                 for (int i = submitedImages.Length; i < Constants.NumberOfProductImages; i++)
  226.                 {
  227.                     var imageMappingToEdit =
  228.                         productToUpdate.ProductImageMappings.FirstOrDefault(pim => pim.ImageNumber == i);
  229.                     //if there is something stored in the mapping
  230.                     if (imageMappingToEdit != null)
  231.                     {
  232.                         //delete the record from the mapping table directly.
  233.                         //just calling productToUpdate.ProductImageMappings.Remove(imageMappingToEdit)
  234.                         //results in a FK error
  235.                         _unitOfWork.GenericRepository<ProductImageMapping>().Delete(imageMappingToEdit);
  236.                     }
  237.                 }
  238.  
  239.                 _productRepo.SetOriginalValueRowVersion(productToUpdate, viewModel.RowVersion);
  240.                 if(_unitOfWork.Save(ModelState, productToUpdate, VerifyProductFields))
  241.                     return RedirectToAction("Index");
  242.  
  243.                 var retViewModel = SetViewModel(viewModel, productToUpdate);
  244.                 return View(retViewModel);
  245.             }  
  246.              
  247.             return View(viewModel);
  248.         }
  249.  
  250.         private void VerifyProductFields(Product databaseProductValues, Product uiFilledProductValues, Product productToUpdate)
  251.         {
  252.             if (databaseProductValues.Name != uiFilledProductValues.Name)
  253.             {
  254.                 ModelState.AddModelError("Name", "Current value in database: " + databaseProductValues.Name);
  255.             }
  256.             if (databaseProductValues.Description != uiFilledProductValues.Description)
  257.             {
  258.                 ModelState.AddModelError("Description", "Current value in database: " + databaseProductValues.Description);
  259.             }
  260.             if (databaseProductValues.CategoryID != uiFilledProductValues.CategoryID)
  261.             {
  262.                 ModelState.AddModelError("CategoryID", "Current value in database: " + databaseProductValues.CategoryID);
  263.             }
  264.             if (databaseProductValues.Price != uiFilledProductValues.Price)
  265.             {
  266.                 ModelState.AddModelError("Price", "Current value in database: " + databaseProductValues.Price);
  267.             }
  268.  
  269.             ModelState.AddModelError(string.Empty, "The record has been modified by another user after you loaded the screen.Your changes have not yet been saved. "
  270.                    + "The new values in the database are shown below. If you want to overwrite these values with your changes then click save otherwise go back to the categories page.");
  271.  
  272.             productToUpdate.RowVersion = databaseProductValues.RowVersion;
  273.             //var dbMapping = databaseProductValues.ProductImageMappings.ToList();//TODO: how do i retrtieve data for image mapping to verify the changes
  274.             //var uiMapping = uiFilledProductValues.ProductImageMappings.ToList();
  275.  
  276.             //if (Enumerable.SequenceEqual(dbMapping, uiMapping))
  277.             //{
  278.             //    ModelState.AddModelError("Price", "Current value in database: " + databaseProductValues.Price);
  279.             //}    
  280.         }
  281.  
  282.         // GET: Products/Delete/5
  283.         public ActionResult Delete(int? id, bool? deletionError)
  284.         {
  285.             if (id == null)
  286.             {
  287.                 return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
  288.             }
  289.             Product product = _productRepo.Find(id);
  290.             if (product == null)
  291.             {
  292.                 if (deletionError.GetValueOrDefault())
  293.                 {
  294.                     return RedirectToAction("Index");
  295.                 }
  296.                 return HttpNotFound();
  297.             }
  298.  
  299.             if (deletionError.GetValueOrDefault())
  300.             {
  301.                 ModelState.AddModelError(string.Empty, "The product you attempted to delete has been modified by another user after you loaded it. " +
  302.                     "The delete has not been performed.The current values in the database are shown above. " +
  303.                     "If you still want to delete this record click the Delete button again, otherwise go back to the products page.");
  304.             }
  305.             return View(product);
  306.         }
  307.  
  308.         // POST: Products/Delete/5
  309.         [HttpPost]
  310.         [ValidateAntiForgeryToken]
  311.         public ActionResult Delete(Product product)
  312.         {
  313.             try
  314.             {
  315.                 _productRepo.Delete(product);
  316.                 _unitOfWork.Save();
  317.                 return RedirectToAction("Index");
  318.             }
  319.             catch (DbUpdateConcurrencyException)
  320.             {
  321.                 return RedirectToAction("Delete", new { deletionError = true, id = product.ID });
  322.             }
  323.         }
  324.  
  325.         // POST: Products/Create
  326.         // To protect from overposting attacks, please enable the specific properties you want to bind to, for
  327.         // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
  328.         [HttpPost]
  329.         [ValidateAntiForgeryToken]
  330.         [Authorize(Roles="Users")]
  331.         public ActionResult AddReview(int id, string comment)
  332.         {
  333.             Review review = new Review();
  334.             var productToUpdate = _productRepo.Find(id);
  335.             review.Product = productToUpdate;
  336.             review.ProductId = productToUpdate.ID;
  337.             review.UserId = User.Identity.Name;
  338.             review.Comment = comment;
  339.             review.DateWriten = DateTime.Now.ToUniversalTime();
  340.            
  341.             productToUpdate.Reviews.Add(review);
  342.            
  343.             if (ModelState.IsValid)
  344.             {
  345.                 _unitOfWork.GenericRepository<Review>().Add(review);
  346.                 _productRepo.Update(productToUpdate);
  347.                 _unitOfWork.Save();
  348.                 return RedirectToAction("Details", productToUpdate);
  349.             }
  350.  
  351.             return View("Details", productToUpdate);
  352.         }
  353.  
  354.  
  355.         #region Private methods
  356.  
  357.         private static IQueryable<Product> SearchProductsFor(string search, IQueryable<Product> products)
  358.         {
  359.             return products.Where(
  360.                 p => p.Name.Contains(search) || p.Category.Name.Contains(search) || p.Description.Contains(search));
  361.         }
  362.  
  363.         private static void GroupSearchIntoCategoriesAndCountPerCategory(ProductIndexViewModel viewModel, IQueryable<Product> products)
  364.         {
  365.             viewModel.CatsWithCount = from matchingProducts in products
  366.                                       where
  367.                                           matchingProducts.CategoryID != null
  368.                                       group matchingProducts by
  369.                                           matchingProducts.Category.Name
  370.                 into
  371.                     catGroup
  372.                                       select new CategoryWithCount()
  373.                                       {
  374.                                           CategoryName = catGroup.Key,
  375.                                           ProductCount = catGroup.Count()
  376.                                       };
  377.         }
  378.  
  379.         private ProductViewModel SetViewModel(ProductViewModel viewModel, Product productToUpdate)
  380.         {
  381.             var retViewModel = new ProductViewModel();
  382.             retViewModel.Name = viewModel.Name;
  383.             retViewModel.Description = viewModel.Description;
  384.             retViewModel.CategoryID = (int)viewModel.CategoryID;
  385.             retViewModel.Price = viewModel.Price;
  386.             retViewModel.RowVersion = productToUpdate.RowVersion;
  387.  
  388.             retViewModel.CategoryList = new SelectList(_categoryRepo.GetTable(), "ID", "Name", viewModel.CategoryID);
  389.             retViewModel.ImageLists = new List<SelectList>();
  390.  
  391.             var allProductImages = _productImageRepo.GetTable();
  392.             foreach (var imageMapping in productToUpdate.ProductImageMappings.OrderBy(pi => pi.ImageNumber))
  393.             {
  394.                 retViewModel.ImageLists.Add(new SelectList(allProductImages, "ID", "FileName", imageMapping.ProductImageID));
  395.             }
  396.             for (int i = retViewModel.ImageLists.Count; i < Constants.NumberOfProductImages; i++)
  397.             {
  398.                 retViewModel.ImageLists.Add(new SelectList(allProductImages, "ID", "FileName"));//BUG: db.productImages instead of returning a list of productImages, he returns a list with selected image, check front end, this line seems fine
  399.             }
  400.  
  401.             return retViewModel;
  402.         }
  403.  
  404.         private ProductViewModel MapProductToViewModel(Product product)
  405.         {
  406.             var viewModel = new ProductViewModel();
  407.             viewModel.Name = product.Name;
  408.             viewModel.Description = product.Description;
  409.             viewModel.CategoryID = (int)product.CategoryID;
  410.             viewModel.Price = product.Price;
  411.             viewModel.RowVersion = product.RowVersion;
  412.  
  413.             viewModel.CategoryList = new SelectList(_categoryRepo.GetTable(), "ID", "Name", product.CategoryID);
  414.             viewModel.ImageLists = new List<SelectList>();
  415.  
  416.             var allProductImages = _productImageRepo.GetTable();
  417.             foreach (var imageMapping in product.ProductImageMappings.OrderBy(pi => pi.ImageNumber))
  418.             {
  419.                 viewModel.ImageLists.Add(new SelectList(allProductImages, "ID", "FileName", imageMapping.ProductImageID));
  420.             }
  421.             for (int i = viewModel.ImageLists.Count; i < Constants.NumberOfProductImages; i++)
  422.             {
  423.                 viewModel.ImageLists.Add(new SelectList(allProductImages, "ID", "FileName"));//BUG: db.productImages instead of returning a list of productImages, he returns a list with selected image, check front end, this line seems fine
  424.             }
  425.  
  426.             return viewModel;
  427.         }
  428.  
  429.         private static IQueryable<Product> SortProducts(string sortBy, IQueryable<Product> products)
  430.         {
  431.             switch (sortBy)
  432.             {
  433.                 case "price_lowest":
  434.                     products = products.OrderBy(p => p.Price);
  435.                     break;
  436.                 case "price_highest":
  437.                     products = products.OrderByDescending(p => p.Price);
  438.                     break;
  439.                 default:
  440.                     products = products.OrderBy(p => p.Name);
  441.                     break;
  442.             }
  443.             return products;
  444.         }
  445.  
  446.         private static void SetViewModelSortProperties(string sortBy, ProductIndexViewModel viewModel)
  447.         {
  448.             viewModel.SortBy = sortBy;
  449.             viewModel.Sorts = new Dictionary<string, string>
  450.             {
  451.                 {"Price low to high", "price_lowest"},
  452.                 {"Price high to low", "price_highest"}
  453.             };
  454.         }
  455.  
  456.         private static async Task SetViewModelPages(ProductIndexViewModel viewModel, IQueryable<Product> products, int? page)
  457.         {
  458.             int currentPage = (page ?? 1);
  459.             viewModel.CurrentPage = currentPage;
  460.             viewModel.TotalNumberOfProducts = products.Count();
  461.             viewModel.TotalPages = (int)Math.Ceiling((decimal)viewModel.TotalNumberOfProducts / Constants.PageItems);
  462.             viewModel.currentPageOfProducts = await products.ReturnPages(currentPage, Constants.PageItems);
  463.         }
  464.  
  465.         protected override void Dispose(bool disposing)
  466.         {
  467.             _unitOfWork.Dispose();
  468.             base.Dispose(disposing);
  469.         }
  470.  
  471.         protected override void OnException(ExceptionContext filterContext)
  472.         {
  473.             Log.Error("Exception occured with message: {Message}", filterContext.Exception.Message);
  474.             Log.Error("Stacktrace: {StackTrace}", filterContext.Exception.StackTrace);
  475.             base.OnException(filterContext);
  476.         }
  477.  
  478.  
  479.         #endregion
  480.     }
  481. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement