JCart : Customer Registration

To facilitate new customer registration we will provide a new Registration form where customer provide his details and register with our system.

Let us implement the back-end customer service operations.

public interface CustomerRepository extends JpaRepository<Customer, Integer>{
	Customer findByEmail(String email);
}
@Service
@Transactional
public class CustomerService 
{
	@Autowired CustomerRepository customerRepository;
	
	public Customer getCustomerByEmail(String email) {
		return customerRepository.findByEmail(email);
	}

	public Customer createCustomer(Customer customer) {
		return customerRepository.save(customer);
	}
}
@Component
public class CustomerValidator implements Validator
{
	@Autowired private CustomerService custmoerService;

	@Override
	public boolean supports(Class<?> clazz) {
		return Customer.class.isAssignableFrom(clazz);
	}

	@Override
	public void validate(Object target, Errors errors) {
		Customer customer = (Customer) target;
		Customer customerByEmail = custmoerService.getCustomerByEmail(customer.getEmail());
		if(customerByEmail != null){
			errors.rejectValue("email", "error.exists", 
			new Object[]{customer.getEmail()}, 
			"Email "+customer.getEmail()+" already in use");
		}
	}
	
}

Let us implement the CustomerController registration handler methods as follows:

@Controller
public class CustomerController extends JCartSiteBaseController
{	
	@Autowired private CustomerService customerService;
	@Autowired private CustomerValidator customerValidator;
	@Autowired protected PasswordEncoder passwordEncoder;
	
	@Override
	protected String getHeaderTitle()
	{
		return "Login/Register";
	}

	@RequestMapping(value="/register", method=RequestMethod.GET)
	protected String registerForm(Model model)
	{
		model.addAttribute("customer", new Customer());
		return "register";
	}
	
	@RequestMapping(value="/register", method=RequestMethod.POST)
	protected String register(@Valid @ModelAttribute("customer") Customer customer, 
		BindingResult result, Model model, RedirectAttributes redirectAttributes)
	{
		customerValidator.validate(customer, result);
		if(result.hasErrors()){
			return "register";
		}
		String password = customer.getPassword();
		String encodedPwd = passwordEncoder.encode(password);
		customer.setPassword(encodedPwd);
		
		Customer persistedCustomer = customerService.createCustomer(customer);
		logger.debug("Created new Customer with id : {} and email : {}", persistedCustomer.getId(), persistedCustomer.getEmail());
		redirectAttributes.addFlashAttribute("info", "Customer created successfully");
		return "redirect:/login";
	}
	
}

Finally let us create the register.html thymeleaf view as follows:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
	  xmlns:th="http://www.thymeleaf.org"
	  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
      layout:decorator="layout/mainLayout">
      
<head>
	<title>Register</title>
</head>
<body>
	<div layout:fragment="content">
		<div class="single-product-area">
			<div class="zigzag-bottom"></div>
			<div class="container">
				
				<div class="row">
					
					<div class="col-md-offset-3 col-md-6" >
						<form id="login-form-wrap" th:action="@{/register}" th:object="${customer}" method="post">

							<p class="form-row form-row-first">
								<label for="firstName">FirstName <span class="required">*</span>
								</label>
								<input type="text" th:field="*{firstName}" class="input-text"/>
								<p th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}" th:errorclass="text-danger">Incorrect data</p>
							</p>
							
							<p class="form-row form-row-first">
								<label for="lastName">LastName <span class="required">*</span>
								</label>
								<input type="text" th:field="*{lastName}" class="input-text"/>
								<p th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}" th:errorclass="text-danger">Incorrect data</p>
								
							</p>
							
							<p class="form-row form-row-first">
								<label for="email">Email <span class="required">*</span>
								</label>
								<input type="email" th:field="*{email}" class="input-text" placeholder="Email"/>
								<p th:if="${#fields.hasErrors('email')}" th:errors="*{email}" th:errorclass="text-danger">Incorrect data</p>
							</p>
							<p class="form-row form-row-last">
								<label for="password">Password <span class="required">*</span>
								</label>
								<input type="password" th:field="*{password}" class="input-text" placeholder="Password"/>
								<p th:if="${#fields.hasErrors('password')}" th:errors="*{password}" th:errorclass="text-danger">Incorrect data</p>
							</p>
							
							<p class="form-row form-row-first">
								<label for="phone">Phone <span class="required">*</span>
								</label>
								<input type="text" th:field="*{phone}" class="input-text"/>
								<p th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}" th:errorclass="text-danger">Incorrect data</p>
							</p>
							<div class="clear"></div>


							<p class="form-row">
								<input type="submit" value="Login" class="button"/>
							</p>
							
							<p>
								<div th:if="${info!=null}" class="alert alert-warning alert-dismissable" >
									<p><i class="icon fa fa-warning"></i> <span th:text="${info}"></span></p>
								</div>   
							</p>
							<p class="lost_password">
								Existing Customer? <a href="#" th:href="@{/login}" th:text="#{label.login}">Login</a>
							</p>
							
							<div class="clear"></div>
						</form>
						
					</div>
				</div>
				
			</div>
		</div>
	</div>
	
</body>
    
</html>

Now new customers can click on Register link and register themselves. Once the registration is successful he can login and proceed to checkout the cart.

JCart : Customer Login

So far we have implemented the functionality where customers can browse the categories, add products to cart, view Cart and update/remove items.
But to checkout the cart the customer should login into the system. So if the customer is not yet loggedin we should redirect customer to login page. If customer is already registered with our system he can login or he should be able to register. So, we will start implementing Customer Login/Registration usecases.

Let us create login form thymeleaf view jcart-site/src/main/resources/templates/login.html as follows:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" 
	  xmlns:th="http://www.thymeleaf.org"
	  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
      layout:decorator="layout/mainLayout">
      
<head>
	<title>Login</title>
</head>
<body>
	<div layout:fragment="content">
		<div class="single-product-area">
			<div class="zigzag-bottom"></div>
			<div class="container">
				
				<div class="row">
					
					<div class="col-md-offset-4 col-md-4" >
						<form id="login-form-wrap" th:action="@{/login}" method="post">


							<p class="form-row form-row-first">
								<label for="email">Email <span class="required">*</span>
								</label>
								<input type="text" id="username" name="username" class="input-text" placeholder="Email"/>
							</p>
							<p class="form-row form-row-last">
								<label for="password">Password <span class="required">*</span>
								</label>
								<input type="password" id="password" name="password" class="input-text" placeholder="Password"/>
							</p>
							<div class="clear"></div>


							<p class="form-row">
								<input type="submit" value="Login" class="button"/>
							</p>
							
							<p>
								<div th:if="${param.error}" class="alert alert-danger alert-dismissable" >
									<p><i class="icon fa fa-ban"></i> <span th:text="#{error.login_failed}">Invalid Email and Password.</span></p>
								</div>
								<div th:if="${param.logout}" class="alert alert-info alert-dismissable" >
									<p><i class="icon fa fa-info"></i> <span th:text="#{info.logout_success}">You have been logged out.</span></p>
								</div>		          
								<div th:if="${info!=null}" class="alert alert-warning alert-dismissable" >
									<p><i class="icon fa fa-warning"></i> <span th:text="${info}"></span></p>
								</div>   
							</p>
							<p class="lost_password">
								New Customer? <a href="#" th:href="@{/register}" th:text="#{label.register}">Register</a>
							</p>
							
							<div class="clear"></div>
						</form>
						
					</div>
				</div>
				
			</div>
		</div>
	</div>
	
</body>
    
</html>

We are using SpringSecurity for Customer Authentication and we have already configured SpringSecurity in our previous post JCart : Initial code setup for ShoppingCart

We have already created a couple of sample customer records using the seed data sql script jcart-core/src/main/resources/data.sql.
You can try to login using sivaprasadreddy.k@gmail.com/siva credentials. If login is successful it will redirect to /checkout url which we have not yet implemented, otherwise it will show login error.

JCart : Iteration -6

In this Iteration-6 we will be implementing the Customer Login/Register and placing the orders.
As per of this we will implement the following usecases:

JCart : View Cart

In our earlier post we have implemented Add To Cart functionality. In this post we will implement showing the Cart Item details.

In out mainLayout.html header we have ShoppingCart icon showing the cart item count as follows:

<div class="shopping-item">
	<a href="#" th:href="@{/cart}">Cart <i class="fa fa-shopping-cart"></i> <span id="cart-item-count" class="product-count">(0)</span></a>
</div>

When customer clicks on Cart icon we will show the Cart details. Let us implement the “/cart” url handler method in CartController as follows:

@Controller
public class CartController extends JCartSiteBaseController
{
	....
	
	@RequestMapping(value="/cart", method=RequestMethod.GET)
	public String showCart(HttpServletRequest request, Model model)
	{
		Cart cart = getOrCreateCart(request);
		model.addAttribute("cart", cart);
		return "cart";
	}
}

Now let us create thymeleaf view template cart.html as follows:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
	  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
      layout:decorator="layout/mainLayout">
      
      <head>
        <title>Cart</title>
    </head>
    <body>
    	<div layout:fragment="content">
    
		    <div class="single-product-area">
		        <div class="zigzag-bottom"></div>
		        <div class="container">
		            <div class="row">
		                <div class="woocommerce-info col-md-offset-2 col-md-8" th:if="${#lists.isEmpty(cart.items)}">
							<h2>Cart is Empty</h2>
						</div>
		                <div class="col-md-offset-2 col-md-8" th:unless="${#lists.isEmpty(cart.items)}">
		                    <div class="product-content-right">
		                        <div class="woocommerce">
		                            <form method="post" action="#">
		                                <table cellspacing="0" class="shop_table cart">
		                                    <thead>
		                                        <tr>
		                                            <th class="product-remove">&nbsp;</th>
		                                            <th class="product-thumbnail">&nbsp;</th>
		                                            <th class="product-name">Product</th>
		                                            <th class="product-price">Price</th>
		                                            <th class="product-quantity">Quantity</th>
		                                            <th class="product-subtotal">Total</th>
		                                        </tr>
		                                    </thead>
		                                    <tbody>
		                                        <tr class="cart_item" th:each="item : ${cart.items}">
		                                            <td class="product-remove">
		                                                <a title="Remove this item" class="remove" href="#" 
		                                                	th:onclick="'javascript:removeItemFromCart( \''+${item.product.sku}+'\');'">×</a> 
		                                            </td>
		
		                                            <td class="product-thumbnail">
		                                                <a href="#" th:href="@{/products/{sku}(sku=${item.product.sku})}">
		                                                	<img width="145" height="145" alt="poster_1_up" 
		                                                	class="shop_thumbnail" src="assets/img/products/2.jpg"
		                                                	th:src="@{'/products/images/{id}.jpg'(id=${item.product.id})}"/>
		                                                </a>
		                                            </td>
		
		                                            <td class="product-name">
		                                                <a href="#" th:href="@{/products/{sku}(sku=${item.product.sku})}"
		                                                	th:text="${item.product.name}">Product name</a> 
		                                            </td>
		
		                                            <td class="product-price">
		                                                <span class="amount" th:text="${item.product.price}">$15.00</span> 
		                                            </td>
		
		                                            <td class="product-quantity">
		                                                <div class="quantity buttons_added">
		                                                	<input type="text" size="5" value="1" th:value="${item.quantity}" 
		                                                			th:onchange="'javascript:updateCartItemQuantity( \''+${item.product.sku}+'\' , '+this.value+');'"/>                                                   
		                                                </div>
		                                            </td>
		
		                                            <td class="product-subtotal">
		                                                <span class="amount" th:text="${item.product.price * item.quantity}">$150.00</span> 
		                                            </td>
		                                        </tr>
		                                        <tr>
		                                            <td class="actions" colspan="6">
		                                                <a class="add_to_cart_button" href="#" th:href="@{/checkout}">CHECKOUT</a>
		                                            </td>
		                                        </tr>
		                                    </tbody>
		                                </table>
		                            </form>
		
		                            <div class="cart-collaterals">
					                     <div class="cart_totals ">
		                                <h2>Cart Totals</h2>
		
		                                <table cellspacing="0">
		                                    <tbody>
		                                        <tr class="cart-subtotal">
		                                            <th>Cart Subtotal</th>
		                                            <td><span class="amount" th:text="${cart.totalAmount}">$15.00</span></td>
		                                        </tr>
		
		                                        <tr class="shipping">
		                                            <th>Shipping and Handling</th>
		                                            <td>Free Shipping</td>
		                                        </tr>
		
		                                        <tr class="order-total">
		                                            <th>Order Total</th>
		                                            <td><strong><span class="amount" th:text="${cart.totalAmount}">$15.00</span></strong> </td>
		                                        </tr>
		                                    </tbody>
		                                </table>
		                            </div>
		
		                            </div>
		                        </div>                        
		                    </div>                    
		                </div>
		            </div>
		        </div>
		    </div>
		
		</div>
  </body>
</html>

Now run the application and add items to cart and click on Cart Icon which should display Cart page with all the Cart item details.

Observe that we have already added HTML markup and JavaScript function calls to update the Item quantity and removing an item. We will implement these functionalities in a moment.

Let us add the following two JavaScript functions to update item count and remove items.

function updateCartItemQuantity(sku, quantity)
{
	$.ajax ({ 
		url: '/cart/items', 
		type: "PUT", 
		dataType: "json",
		contentType: "application/json",
		data : '{ "product" :{ "sku":"'+ sku +'"},"quantity":"'+quantity+'"}',
		complete: function(responseData, status, xhttp){ 
			updateCartItemCount();        	
			location.href = '/cart' 
		}
	});
}

function removeItemFromCart(sku)
{
	$.ajax ({ 
		url: '/cart/items/'+sku, 
		type: "DELETE", 
		dataType: "json",
		contentType: "application/json",
		complete: function(responseData, status, xhttp){ 
			updateCartItemCount();
			location.href = '/cart' 
		}
	});
}

Next we will implement the CartController handler methods as follows:

@Controller
public class CartController extends JCartSiteBaseController
{
	...
	...
	
	@RequestMapping(value="/cart/items", method=RequestMethod.PUT)
	@ResponseBody
	public void updateCartItem(@RequestBody LineItem item, HttpServletRequest request, HttpServletResponse response)
	{
		Cart cart = getOrCreateCart(request);
		if(item.getQuantity() <= 0){
			String sku = item.getProduct().getSku();
			cart.removeItem(sku);
		} else {
			cart.updateItemQuantity(item.getProduct(), item.getQuantity());
		}
	}
	
	@RequestMapping(value="/cart/items/{sku}", method=RequestMethod.DELETE)
	@ResponseBody
	public void removeCartItem(@PathVariable("sku") String sku, HttpServletRequest request)
	{
		Cart cart = getOrCreateCart(request);
		cart.removeItem(sku);
	}

}

Now that we have completed all the Cart related usecases. In our next post we will see how to implement Checkout functionality.

JCart : ShoppingCart Add Item To Cart

In our HomePage/CategoryPage/ProductPage we have a button Add To Cart as follows:

<a class="add_to_cart_button" data-quantity="1" data-product_sku="" data-product_id="70" 
	rel="nofollow" href="#"
	th:onclick="'javascript:addItemToCart(\'' + ${product.sku} + '\');'">Add to cart</a>

When customer clicks on Add To Cart button it will trigger addItemToCart(sku) JavaScript function passing the product SKU value.

Now create jcart-site/src/main/resources/static/assets/js/app.js and implement addItemToCart(sku) function as follows:

function addItemToCart(sku)
{
	$.ajax ({ 
		url: '/cart/items', 
		type: "POST", 
		dataType: "json",
		contentType: "application/json",
		data : '{"sku":"'+ sku +'"}"',
		complete: function(responseData, status, xhttp){
			updateCartItemCount();			
		}
	}); 
}

This function triggers an Ajax call to url ‘/cart/items’ using jQuery and if it is successful we are calling another JavaScript function updateCartItemCount().

The updateCartItemCount() function updates the current Cart Items count in the page header section.

<div class="shopping-item">
	<a href="#" th:href="@{/cart}">Cart <i class="fa fa-shopping-cart"></i> <span id="cart-item-count" class="product-count">(0)</span></a>
</div>
function updateCartItemCount()
{
	$.ajax ({ 
		url: '/cart/items/count', 
		type: "GET", 
		dataType: "json",
		contentType: "application/json",
		complete: function(responseData, status, xhttp){ 
			$('#cart-item-count').text('('+responseData.responseJSON.count+')');
		}
	});
}

The updateCartItemCount() function triggers an Ajax call to url: ‘/cart/items/count’ to get the current Cart Item count. Once the response is received we are setting the count value.

We need to display the current Cart Item Count on all pages, so let us invoke updateCartItemCount() function in app.js for all the page load as follows:

jQuery(document).ready(function($){
	updateCartItemCount();
});

function updateCartItemCount()
{
	...
}

function updateCartItemCount()
{
	...
}

Now let us implement the back-end functionality to handle Cart related operations.

First let us create the model objects to hold Cart and LineItem data.

public class Cart
{
	private List<LineItem> items;
	private Customer customer;
	private Address deliveryAddress;
	private Payment payment;
	
	public Cart()
	{
		items = new ArrayList<LineItem>();
		customer = new Customer();
		deliveryAddress = new Address();
		payment = new Payment();
	}
	

	public void addItem(Product product)
	{
		for (LineItem lineItem : items)
		{
			if(lineItem.getProduct().getSku().equals(product.getSku())){
				lineItem.setQuantity(lineItem.getQuantity()+1);
				return;
			}
		}
		LineItem item = new LineItem(product, 1);
		this.items.add(item);		
	}
	
	public void updateItemQuantity(Product product, int quantity)
	{
		for (LineItem lineItem : items)
		{
			if(lineItem.getProduct().getSku().equals(product.getSku())){
				lineItem.setQuantity(quantity);
			}
		}
	}
	
	public void removeItem(String sku)
	{
		LineItem  item = null;
		for (LineItem lineItem : items)
		{
			if(lineItem.getProduct().getSku().equals(sku)){
				item = lineItem;
				break;
			}
		}
		if(item != null){
			items.remove(item);
		}
	}
	
	public void clearItems()
	{
		items = new ArrayList<LineItem>();
	}
	
	public int getItemCount()
	{
		int count = 0;
		for (LineItem lineItem : items) {
			count +=  lineItem.getQuantity();
		}
		return count;
	}
		
	public BigDecimal getTotalAmount()
	{
		BigDecimal amount = new BigDecimal("0.0");
		for (LineItem lineItem : items)
		{
			amount = amount.add(lineItem.getSubTotal());
		}
		return amount;
	}
	
	//setters & getters
	
}
public class LineItem
{
	private Product product;
	private int quantity;
	
	public LineItem()
	{
	}
	
	public LineItem(Product product, int quantity)
	{
		this.product = product;
		this.quantity = quantity;
	}

	public BigDecimal getSubTotal()
	{
		return product.getPrice().multiply(new BigDecimal(quantity));
	}
	//setters & getters
}

We may need to get the current Cart object in one or more Controllers. So let us create a method getOrCreateCart(HttpServletRequest) in JCartSiteBaseController so that it will be available in all controllers.

public abstract class JCartSiteBaseController
{
	....
	....
	
	protected Cart getOrCreateCart(HttpServletRequest request)
	{
		Cart cart = null;
		cart = (Cart) request.getSession().getAttribute("CART_KEY");
		if(cart == null){
			cart = new Cart();
			request.getSession().setAttribute("CART_KEY", cart);
		}
		return cart;
	}
}

Now let us implement the CartController as follows:

@Controller
public class CartController extends JCartSiteBaseController
{
	@Autowired
	private CatalogService catalogService;
	
	@Override
	protected String getHeaderTitle()
	{
		return "Cart";
	}
		
	@RequestMapping(value="/cart/items/count", method=RequestMethod.GET)
	@ResponseBody
	public Map<String, Object> getCartItemCount(HttpServletRequest request, Model model)
	{
		Cart cart = getOrCreateCart(request);
		int itemCount = cart.getItemCount();
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("count", itemCount);
		return map;
	}
		
	@RequestMapping(value="/cart/items", method=RequestMethod.POST)
	@ResponseBody
	public void addToCart(@RequestBody Product product, HttpServletRequest request)
	{
		Cart cart = getOrCreateCart(request);
		Product p = catalogService.getProductBySku(product.getSku());
		cart.addItem(p);
	}
	
}

Now run the application and click on Add To Cart in HomePage/CategoryPage/ProductPage, the product should be added to Cart and the Cart Item Count in header should be updated accordingly.