Giới thiệu
Việc thêm tính năng danh sách yêu thích (wishlist) giúp nâng cao trải nghiệm người dùng bằng cách cho phép khách hàng lưu lại sản phẩm để mua sau. Trong Optimizely Commerce, bạn có thể tận dụng cơ sở hạ tầng giỏ hàng (cart) hiện có để triển khai wishlist mà không cần tạo thêm các bảng cơ sở dữ liệu hoặc logic phức tạp.
Những gì bạn sẽ học:
- Sử dụng hệ thống giỏ hàng của Commerce cho wishlist
- Phân biệt wishlist và giỏ hàng mua sắm
- Thực hiện các thao tác thêm/xóa sản phẩm khỏi wishlist
- Hiển thị các mặt hàng trong wishlist
Điều kiện tiên quyết:
- Optimizely Commerce 14.x
- .NET 6.0+
- Hiểu biết cơ bản về hệ thống giỏ hàng Commerce
Phương pháp tiếp cận
Thay vì xây dựng một hệ thống wishlist riêng biệt, chúng ta sẽ sử dụng chức năng giỏ hàng của Optimizely Commerce với một tên giỏ hàng duy nhất để phân biệt các mặt hàng trong wishlist với các mặt hàng trong giỏ hàng mua sắm thực tế.
Khái niệm chính:
Shopping Cart → Cart Name: "Default"
Wishlist → Cart Name: "Wishlist"
Phương pháp này mang lại:
- ✅ Tái sử dụng cơ sở hạ tầng giỏ hàng hiện có
- ✅ Lưu trữ tự động và liên kết người dùng
- ✅ Bảo mật và xác thực tích hợp sẵn
- ✅ Dễ dàng chuyển sang giỏ hàng để mua
Bước 1: Tạo Service Wishlist
Tạo một service để quản lý các thao tác wishlist sử dụng API giỏ hàng của Commerce.
// File: Business/Services/IWishlistService.cs
using Mediachase.Commerce.Orders;
namespace YourProject.Business.Services
{
public interface IWishlistService
{
ICart GetWishlist();
bool AddToWishlist(string code, decimal quantity = 1);
bool RemoveFromWishlist(string code);
bool MoveToCart(string code);
int GetWishlistItemCount();
}
}
Bước 2: Triển khai Wishlist Service
// File: Business/Services/WishlistService.cs
using EPiServer.Commerce.Order;
using Mediachase.Commerce;
using Mediachase.Commerce.Orders;
using System.Linq;
namespace YourProject.Business.Services
{
public class WishlistService : IWishlistService
{
private const string WishlistCartName = "Wishlist";
private readonly IOrderRepository _orderRepository;
private readonly IOrderGroupFactory _orderGroupFactory;
private readonly ICurrentMarket _currentMarket;
public WishlistService(
IOrderRepository orderRepository,
IOrderGroupFactory orderGroupFactory,
ICurrentMarket currentMarket)
{
_orderRepository = orderRepository;
_orderGroupFactory = orderGroupFactory;
_currentMarket = currentMarket;
}
public ICart GetWishlist()
{
var market = _currentMarket.GetCurrentMarket();
return _orderRepository.LoadOrCreateCart<ICart>(
market.MarketId,
WishlistCartName);
}
public bool AddToWishlist(string code, decimal quantity = 1)
{
var wishlist = GetWishlist();
// Check if item already exists
var existingItem = wishlist.GetAllLineItems()
.FirstOrDefault(x => x.Code == code);
if (existingItem != null)
{
// Update quantity if already in wishlist
existingItem.Quantity += quantity;
}
else
{
// Add new item
var lineItem = _orderGroupFactory.CreateLineItem(code, wishlist);
lineItem.Quantity = quantity;
wishlist.AddLineItem(lineItem);
}
return _orderRepository.Save(wishlist);
}
public bool RemoveFromWishlist(string code)
{
var wishlist = GetWishlist();
var lineItem = wishlist.GetAllLineItems()
.FirstOrDefault(x => x.Code == code);
if (lineItem == null)
return false;
wishlist.RemoveLineItem(lineItem);
return _orderRepository.Save(wishlist);
}
public bool MoveToCart(string code)
{
var wishlist = GetWishlist();
var lineItem = wishlist.GetAllLineItems()
.FirstOrDefault(x => x.Code == code);
if (lineItem == null)
return false;
// Get shopping cart
var cart = _orderRepository.LoadOrCreateCart<ICart>(
_currentMarket.GetCurrentMarket().MarketId,
"Default");
// Add to cart
var cartLineItem = _orderGroupFactory.CreateLineItem(code, cart);
cartLineItem.Quantity = lineItem.Quantity;
cart.AddLineItem(cartLineItem);
// Remove from wishlist
wishlist.RemoveLineItem(lineItem);
// Save both
_orderRepository.Save(cart);
return _orderRepository.Save(wishlist);
}
public int GetWishlistItemCount()
{
var wishlist = GetWishlist();
return wishlist.GetAllLineItems().Count();
}
}
}
Bước 3: Đăng ký Service
// File: Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Other services...
services.AddTransient<IWishlistService, WishlistService>();
}
Bước 4: Tạo Wishlist Controller
// File: Controllers/WishlistController.cs
using Microsoft.AspNetCore.Mvc;
using YourProject.Business.Services;
namespace YourProject.Controllers
{
public class WishlistController : Controller
{
private readonly IWishlistService _wishlistService;
public WishlistController(IWishlistService wishlistService)
{
_wishlistService = wishlistService;
}
[HttpGet]
public IActionResult Index()
{
var wishlist = _wishlistService.GetWishlist();
return View(wishlist);
}
[HttpPost]
public IActionResult Add(string code, decimal quantity = 1)
{
var success = _wishlistService.AddToWishlist(code, quantity);
if (success)
{
return Json(new {
success = true,
message = "Added to wishlist",
count = _wishlistService.GetWishlistItemCount()
});
}
return Json(new { success = false, message = "Failed to add" });
}
[HttpPost]
public IActionResult Remove(string code)
{
var success = _wishlistService.RemoveFromWishlist(code);
return Json(new {
success = success,
count = _wishlistService.GetWishlistItemCount()
});
}
[HttpPost]
public IActionResult MoveToCart(string code)
{
var success = _wishlistService.MoveToCart(code);
return Json(new {
success = success,
message = success ? "Moved to cart" : "Failed to move"
});
}
}
}
Bước 5: Tạo Wishlist View
@* File: Views/Wishlist/Index.cshtml *@
@model Mediachase.Commerce.Orders.ICart
<div class="wishlist-page">
<h1>Danh sách yêu thích của tôi</h1>
@if (!Model.GetAllLineItems().Any())
{
<p class="empty-message">Danh sách yêu thích của bạn đang trống</p>
}
else
{
<div class="wishlist-items">
@foreach (var item in Model.GetAllLineItems())
{
<div class="wishlist-item" data-code="@item.Code">
<img src="@item.GetEntryContent()?.GetDefaultAsset()?.Url"
alt="@item.DisplayName" />
<div class="item-details">
<h3>@item.DisplayName</h3>
<p class="price">@item.PlacedPrice.ToString("C")</p>
<p class="quantity">SL: @item.Quantity</p>
</div>
<div class="item-actions">
<button class="btn-move-to-cart"
data-code="@item.Code">
Thêm vào Giỏ hàng
</button>
<button class="btn-remove"
data-code="@item.Code">
Xóa
</button>
</div>
</div>
}
</div>
}
</div>
<script>
// Add to Cart from Wishlist
$('.btn-move-to-cart').on('click', function() {
const code = $(this).data('code');
$.post('/wishlist/movetocart', { code: code })
.done(function(data) {
if (data.success) {
location.reload();
}
});
});
// Remove from Wishlist
$('.btn-remove').on('click', function() {
const code = $(this).data('code');
$.post('/wishlist/remove', { code: code })
.done(function(data) {
if (data.success) {
$(`[data-code="${code}"]`).fadeOut();
updateWishlistCount(data.count);
}
});
});
</script>
Bước 6: Thêm Nút Wishlist vào Sản phẩm
@* File: Views/Product/Index.cshtml (excerpt) *@
<button class="btn-add-to-wishlist"
data-code="@Model.Code">
<i class="icon-heart"></i> Thêm vào Danh sách yêu thích
</button>
<script>
$('.btn-add-to-wishlist').on('click', function() {
const code = $(this).data('code');
$.post('/wishlist/add', { code: code, quantity: 1 })
.done(function(data) {
if (data.success) {
alert('Đã thêm vào danh sách yêu thích!');
updateWishlistCount(data.count);
}
});
});
function updateWishlistCount(count) {
$('.wishlist-count').text(count);
}
</script>
Các vấn đề thường gặp
Vấn đề 1: Sản phẩm không được lưu trữ
Vấn đề: Các mặt hàng trong wishlist biến mất sau khi phiên làm việc (session) kết thúc
Nguyên nhân: Giỏ hàng của người dùng ẩn danh (anonymous users) có thể không được lưu trữ đúng cách
Giải pháp:
// Ensure user identity is set
public ICart GetWishlist()
{
var market = _currentMarket.GetCurrentMarket();
var customerId = GetCurrentCustomerId(); // Get from authentication
return _orderRepository.LoadOrCreateCart<ICart>(
market.MarketId,
WishlistCartName,
customerId);
}
private Guid GetCurrentCustomerId()
{
// Return authenticated user ID or anonymous ID
if (User.Identity.IsAuthenticated)
return Guid.Parse(User.Identity.Name);
return GetAnonymousCustomerId();
}
Vấn đề 2: Các mặt hàng trùng lặp
Vấn đề: Cùng một sản phẩm được thêm nhiều lần vào wishlist
Giải pháp:
public bool AddToWishlist(string code, decimal quantity = 1)
{
var wishlist = GetWishlist();
var existingItem = wishlist.GetAllLineItems()
.FirstOrDefault(x => x.Code == code);
if (existingItem != null)
{
// Don't increase quantity for wishlist
return true; // Already in wishlist
}
var lineItem = _orderGroupFactory.CreateLineItem(code, wishlist);
lineItem.Quantity = 1; // Always set to 1 for wishlist
wishlist.AddLineItem(lineItem);
return _orderRepository.Save(wishlist);
}
Vấn đề 3: Số lượng Wishlist không cập nhật
Vấn đề: Bộ đếm trên giao diện người dùng (UI counter) không phản ánh trạng thái wishlist hiện tại
Giải pháp:
// Create a ViewComponent for dynamic wishlist count
public class WishlistCountViewComponent : ViewComponent
{
private readonly IWishlistService _wishlistService;
public WishlistCountViewComponent(IWishlistService wishlistService)
{
_wishlistService = wishlistService;
}
public IViewComponentResult Invoke()
{
var count = _wishlistService.GetWishlistItemCount();
return View(count);
}
}
@* Use in layout *@
<a href="/wishlist">
<i class="icon-heart"></i>
@await Component.InvokeAsync("WishlistCount")
</a>
Các thực hành tốt nhất
1. Sử dụng Tên Giỏ hàng Riêng biệt
// ✅ NÊN: Sử dụng tên rõ ràng, nhất quán
private const string WishlistCartName = "Wishlist";
private const string DefaultCartName = "Default";
// ❌ KHÔNG NÊN: Sử dụng tên mơ hồ
private const string WishlistCartName = "Cart2";
2. Xử lý Người dùng Khách (Guest Users)
// ✅ NÊN: Hợp nhất wishlist khi người dùng đăng nhập
public void MergeAnonymousWishlist(Guid anonymousId, Guid customerId)
{
var anonymousWishlist = _orderRepository.LoadCart<ICart>(
anonymousId, WishlistCartName);
var customerWishlist = _orderRepository.LoadCart<ICart>(
customerId, WishlistCartName);
foreach (var item in anonymousWishlist.GetAllLineItems())
{
AddToWishlist(item.Code, item.Quantity);
}
_orderRepository.Delete(anonymousWishlist.OrderLink);
}
3. Thêm Kiểm tra Trạng thái Wishlist
// ✅ NÊN: Cung cấp phương thức kiểm tra xem mặt hàng có trong wishlist không
public bool IsInWishlist(string code)
{
var wishlist = GetWishlist();
return wishlist.GetAllLineItems()
.Any(x => x.Code == code);
}
Sử dụng trong view:
@if (await WishlistService.IsInWishlist(Model.Code))
{
<button class="btn-in-wishlist" disabled>
<i class="icon-heart-filled"></i> Đã có trong Wishlist
</button>
}
else
{
<button class="btn-add-to-wishlist" data-code="@Model.Code">
<i class="icon-heart"></i> Thêm vào Danh sách yêu thích
</button>
}
Tham chiếu Nhanh
Các Thao tác Chính
| Thao tác | Phương thức | Trả về |
|---|---|---|
| Lấy wishlist | GetWishlist() |
ICart |
| Thêm mặt hàng | AddToWishlist(code, qty) |
bool |
| Xóa mặt hàng | RemoveFromWishlist(code) |
bool |
| Chuyển sang giỏ hàng | MoveToCart(code) |
bool |
| Lấy số lượng | GetWishlistItemCount() |
int |
Tên Giỏ hàng
Shopping Cart: "Default"
Wishlist: "Wishlist"
Save for Later: "SavedForLater" // Tùy chọn
Xác minh
Sau khi triển khai, hãy kiểm tra:
-
Thêm vào Wishlist
- Nhấp vào "Thêm vào Danh sách yêu thích" trên trang sản phẩm
- Xác minh mặt hàng xuất hiện trong trang wishlist
- Kiểm tra bộ đếm trên tiêu đề được cập nhật
-
Xóa khỏi Wishlist
- Nhấp vào nút xóa
- Xác minh mặt hàng biến mất
- Kiểm tra bộ đếm giảm
-
Chuyển sang Giỏ hàng
- Nhấp vào "Thêm vào Giỏ hàng" từ wishlist
- Xác minh mặt hàng được chuyển sang giỏ hàng mua sắm
- Xác minh mặt hàng bị xóa khỏi wishlist
-
Lưu trữ (Persistence)
- Thêm các mặt hàng vào wishlist
- Đăng xuất và đăng nhập lại
- Xác minh các mặt hàng vẫn còn trong wishlist
Kết luận
Việc triển khai chức năng wishlist sử dụng hệ thống giỏ hàng của Optimizely Commerce là hiệu quả và dễ bảo trì. Bằng cách sử dụng tên giỏ hàng duy nhất, bạn tận dụng được cơ sở hạ tầng hiện có mà không cần thêm sự phức tạp nào.
Lợi ích chính:
- ✅ Tái sử dụng chức năng giỏ hàng đã được kiểm chứng
- ✅ Tự động liên kết người dùng và lưu trữ
- ✅ Dễ dàng bảo trì và mở rộng
- ✅ Nhất quán với kiến trúc Commerce
...