Commit cc0489ae authored by Artem's avatar Artem

improve shopping cart using ajax

parent 833c0e38
......@@ -14,24 +14,21 @@ class Cart /*extends Model*/
$this->total_price = 0;
}
public function updateTotalQty()
public function updateTotalPosAmount()
{
$result = 0;
foreach ($this->items as $item)
{
$result += $item['qty'];
}
$this->total_qty = $result;
$this->total_qty = count($this->items);
}
public function updateTotalPrice()
public function updatePrices()
{
$result = 0;
foreach ($this->items as $item)
$total_price = 0;
foreach ($this->items as $id => $item)
{
$result += $item['qty'] * $item['item']->price;
$subtotal_price = $item['qty'] * $item['item']->price;
$this->items[$id]['subtotal_price'] = $subtotal_price;
$total_price += $subtotal_price;
}
$this->total_price = $result;
$this->total_price = $total_price;
}
public function addItem($id)
......@@ -42,27 +39,34 @@ class Cart /*extends Model*/
$this->items[$id] = [
'qty' => 1
];
}
$this->total_qty++;
$this->updateTotalPosAmount();
}
public function updateQty($products_qty)
public function updateQty($product_id, $changed_qty)
{
foreach ($products_qty as $id => $qty) {
if ($qty <= 0) {
$this->removeItem($id);
if (array_key_exists($product_id, $this->items)) {
if ($changed_qty <= 0) {
$this->removeItem($product_id);
} else {
$this->items[$id]['qty'] = (int)$qty;
$this->updateTotalQty();
$this->items[$product_id]['qty'] = $changed_qty;
}
} else {
$this->addItem($product_id);
$this->items[$product_id]['qty'] = $changed_qty;
}
$this->updateCurrentItems();
$this->updatePrices();
}
public function removeItem($id)
{
unset($this->items[$id]);
$this->updateTotalQty();
$this->updateTotalPosAmount();
}
public function updateCurrentItems()
......@@ -81,6 +85,6 @@ class Cart /*extends Model*/
$this->items[$product->id]['item'] = $product;
}
$this->updateTotalPrice();
$this->updatePrices();
}
}
......@@ -17,11 +17,11 @@ class CartController extends Controller
}
$cart = Session::get('cart');
// need to take products from database because product data may change
$cart->updateCurrentItems();
$products = $cart->items;
return view('cart', compact('cart','products'));
return view('cart', compact('cart'));
}
public function addToCart(Product $product)
......@@ -36,14 +36,17 @@ class CartController extends Controller
}
return $cart->total_qty;
}
public function updateQtyInCart(Request $request)
{
$products_qty = json_decode($request->productsQty, true);
if (!Session::has('cart')) {
return route('homepage');
}
$cart = Session::get('cart');
$cart->updateQty($products_qty);
$cart->updateQty($request->productId, $request->changedQty);
if (!$cart->total_qty) {
Session::forget('cart');
......@@ -51,17 +54,18 @@ class CartController extends Controller
}
}
public function removeFromCart(Product $product)
public function removeFromCart(Request $request)
{
$cart = Session::get('cart');
$cart->removeItem($product->id);
$cart->removeItem($request->productId);
if (!$cart->total_qty) {
Session::forget('cart');
return route('homepage');
}
$cart->updateCurrentItems();
}
public function clearCart()
......@@ -69,4 +73,25 @@ class CartController extends Controller
Session::forget('cart');
return redirect(route('homepage'));
}
public function getItems()
{
$cart = Session::get('cart');
// make array of products in order to get correct order of items in js
$cart_products = [];
foreach ($cart->items as $item) {
$cart_products[] = $item;
}
$response = [
'cart' => $cart,
'products' => $cart_products,
'update_item_url' => route('cart.updateQtyInCart'),
'remove_item_url' => route('cart.removeFromCart'),
'item_url' => '/product/'
];
return json_encode($response);
}
}
......@@ -37,7 +37,8 @@ class OrderController extends Controller
'ordered_reference' => $product->reference,
'ordered_price' => $product->price,
'ordered_qty' => $item['qty'],
'subtotal_price' => $product->price * $item['qty'],
'subtotal_price' => $item['subtotal_price'],
// 'subtotal_price' => $product->price * $item['qty'],
]);
}
......
function setUpdateCartQtyEventListener() {
document.querySelector('.js-update-qty-btn').addEventListener('click', function(e) {
class Cart {
constructor(cartElem) {
this.cartHtmlElem = cartElem;
this.getItemsUrl = cartElem.dataset.getItemsUrl;
}
const actionUrl = this.dataset.action;
generateProductRow(product) {
return `<tr>
<td>
<div class="row">
<div class="col-sm-3 hidden-xs"><img src="/img/${product.item.type}.jpg" width="100" height="100" alt="${product.item.reference}" class="img-responsive"/></div>
<div class="col-sm-9">
<h4 class="nomargin"><a href="${this.itemUrl + product.item.id}">${product.item.reference}</a></h4>
<p>${product.item.description_short}</p>
</div>
</div>
</td>
<td>€ ${product.item.price}</td>
<td>
<input type="number"
min="0"
class="js-qty-input form-control text-center"
data-product-id="${product.item.id}"
value="${product.qty}">
</td>
<td class="text-center">€ ${product.subtotal_price}</td>
<td class="actions text-right">
<button class="js-remove-btn btn btn-danger btn-sm"
data-action="${this.removeItemUrl}"
data-product-id="${product.item.id}"
>Remove</button>
</td>
</tr>`;
}
const qtyInputs = document.querySelectorAll('.js-qty-input');
showProducts() {
let productsList = '';
const productsQty = {};
for (const item in this.products) {
const product = this.products[item];
productsList += this.generateProductRow(product);
}
qtyInputs.forEach(item => {
const productId = item.dataset.product_id;
productsQty[productId] = item.value;
});
this.cartHtmlElem.innerHTML = productsList;
document.querySelector('.amount-in-cart').textContent = this.positionsAmount;
document.querySelector('.js-total-cart-price').textContent = 'Total € ' + this.totalPrice;
}
removeItemFromCart(e) {
if (!e.target.classList.contains('js-remove-btn')) return;
const actionUrl = e.target.dataset.action;
const productId = e.target.dataset.productId;
axios.post(actionUrl, {
productsQty: JSON.stringify(productsQty)
productId: productId,
})
.then(function (response) {
.then( (response) => {
const redirectLinkWhenCartIsEmpty = response.data;
if (redirectLinkWhenCartIsEmpty) {
window.location.replace(redirectLinkWhenCartIsEmpty);
} else {
document.location.reload(true);
document.querySelector('.js-cart').removeEventListener('click', this.removeItemFromCart);
cart.show();
toastr.success('Product deleted');
}
})
.catch(function (error) {
console.log(error);
toastr.error('Error, something went wrong');
});
});
}
}
function setRemoveItemEventListener() {
document.querySelector('.js-products-in-cart').addEventListener('click', function(e) {
if (!e.target.classList.contains('js-remove-btn')) return;
const actionUrl = e.target.dataset.action;
setUpdateItemQtyEventListener() {
document.querySelectorAll('.js-qty-input').forEach(changeQtyInput => {
let isChanged;
changeQtyInput.addEventListener('input', (event) => {
if (isChanged) {
return;
}
axios.post(actionUrl)
.then(function (response) {
const redirectLinkWhenCartIsEmpty = response.data;
// this.show();
isChanged = true;
const actionUrl = this.updateItemUrl;
const productId = event.target.dataset.productId;
if (redirectLinkWhenCartIsEmpty) {
window.location.replace(redirectLinkWhenCartIsEmpty);
} else {
document.location.reload(true);
}
setTimeout(() => {
axios.post(actionUrl, {
productId: productId,
changedQty: event.target.value,
})
.then( (response) => {
const redirectLinkWhenCartIsEmpty = response.data;
if (redirectLinkWhenCartIsEmpty) {
window.location.replace(redirectLinkWhenCartIsEmpty);
} else {
cart.show();
toastr.success('Quantity updated');
}
})
.catch(function (error) {
console.log(error);
toastr.error('Error, something went wrong');
});
isChanged = false;
}, 1000);
});
});
}
events() {
document.querySelector('.js-cart').addEventListener('click', this.removeItemFromCart );
this.setUpdateItemQtyEventListener();
}
show() {
axios.get(this.getItemsUrl)
.then( response => {
// this.products = response.data.cart.items;
this.products = response.data.products;
this.positionsAmount = response.data.cart.total_qty;
this.totalPrice = response.data.cart.total_price;
this.updateItemUrl = response.data.update_item_url;
this.removeItemUrl = response.data.remove_item_url;
this.itemUrl = response.data.item_url;
this.showProducts();
this.events();
})
.catch(function (error) {
console.log(error);
toastr.error('Error, something went wrong');
alert('Something went wrong');
});
});
}
}
setUpdateCartQtyEventListener();
setRemoveItemEventListener();
const cartElem = document.querySelector('.js-cart');
const cart = new Cart(cartElem);
cart.show();
......@@ -61,8 +61,8 @@
</tr>
</thead>
<tbody class="js-products-in-cart">
@foreach($products as $product)
<tbody class="js-cart" data-get-items-url="{{ route('cart.getItems') }}">
{{-- @foreach($products as $product)
<tr>
<td>
<div class="row">
......@@ -88,7 +88,7 @@
>Remove</button>
</td>
</tr>
@endforeach
@endforeach--}}
</tbody>
<tfoot>
......@@ -97,11 +97,11 @@
<a href="{{ route('homepage') }}" class="btn btn-primary"> Continue Shopping</a>
</td>
<td colspan="2" class="text-right">
<button class="js-update-qty-btn btn btn-warning"
data-action="{{ route('cart.updateQtyInCart') }}"
>Update quantity</button>
{{-- <button class="js-update-qty-btn btn btn-warning"--}}
{{-- data-action="{{ route('cart.updateQtyInCart') }}"--}}
{{-- >Update quantity</button>--}}
</td>
<td class="hidden-xs text-center"><b>Total € {{ $cart->total_price }}</b></td>
<td class="hidden-xs text-center"><b class="js-total-cart-price"></b></td>
<td>
<button class="btn btn-success btn-block" data-toggle="modal" data-target="#placeOrderModal">
Send order
......
......@@ -30,8 +30,11 @@ Route::prefix('cart')->group(function() {
Route::get('/', 'CartController@index')->name('cart.index');
Route::post('/add/{product}', 'CartController@addToCart')->name('cart.addToCart');
Route::post('/update', 'CartController@updateQtyInCart')->name('cart.updateQtyInCart');
Route::post('/remove/{product}', 'CartController@removeFromCart')->name('cart.removeFromCart');
// Route::post('/remove/{product}', 'CartController@removeFromCart')->name('cart.removeFromCart');
Route::post('/remove', 'CartController@removeFromCart')->name('cart.removeFromCart');
Route::post('/clear', 'CartController@clearCart')->name('cart.clearCart');
Route::get('/getItems', 'CartController@getItems')->name('cart.getItems');
});
Route::middleware('auth')->prefix('profile')->group(function() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment