A simple shopping cart implementation for Laravel

Overview

LaravelShoppingcart

CI Code Checks codecov StyleCI Total Downloads Latest Stable Version Latest Unstable Version License

This is a fork of Crinsane's LaravelShoppingcart extended with minor features compatible with Laravel 8+. An example integration can be found here.

Installation

Install the package through Composer.

Run the Composer require command from the Terminal:

composer require bumbummen99/shoppingcart

Now you're ready to start using the shoppingcart in your application.

As of version 2 of this package it's possibly to use dependency injection to inject an instance of the Cart class into your controller or other class

You definitely should publish the config file and take a look at it.

php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="config"

This will give you a cart.php config file in which you can make changes to the packages behaivor.

Updates

As of version 4.2.0 this package does, when being used with PostgreSQL, encode the cart content to base64 before storing into database due to an issue with saving values including zero bytes. Please consider clearing your cart table in case you are upgrading using PostgreSQL from a version <4.2.0.

Table of Contents

Look at one of the following topics to learn more about LaravelShoppingcart

Important note

As all the shopping cart that calculate prices including taxes and discount, also this module could be affected by the "totals rounding issue" (*) due to the decimal precision used for prices and for the results. In order to avoid (or at least minimize) this issue, in the Laravel shoppingcart package the totals are calculated using the method "per Row" and returned already rounded based on the number format set as default in the config file (cart.php). Due to this WE DISCOURAGE TO SET HIGH PRECISION AS DEFAULT AND TO FORMAT THE OUTPUT RESULT USING LESS DECIMAL Doing this can lead to the rounding issue.

The base price (product price) is left not rounded.

Usage

The shoppingcart gives you the following methods to use:

Cart::add()

Adding an item to the cart is really simple, you just use the add() method, which accepts a variety of parameters.

In its most basic form you can specify the id, name, quantity, price and weight of the product you'd like to add to the cart.

Cart::add('293ad', 'Product 1', 1, 9.99, 550);

As an optional fifth parameter you can pass it options, so you can add multiple items with the same id, but with (for instance) a different size.

Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']);

The add() method will return an CartItem instance of the item you just added to the cart.

Maybe you prefer to add the item using an array? As long as the array contains the required keys, you can pass it to the method. The options key is optional.

Cart::add(['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => 'large']]);

New in version 2 of the package is the possibility to work with the Buyable interface. The way this works is that you have a model implement the Buyable interface, which will make you implement a few methods so the package knows how to get the id, name and price from your model. This way you can just pass the add() method a model and the quantity and it will automatically add it to the cart.

As an added bonus it will automatically associate the model with the CartItem

Cart::add($product, 1, ['size' => 'large']);

As an optional third parameter you can add options.

Cart::add($product, 1, ['size' => 'large']);

Finally, you can also add multipe items to the cart at once. You can just pass the add() method an array of arrays, or an array of Buyables and they will be added to the cart.

When adding multiple items to the cart, the add() method will return an array of CartItems.

Cart::add([
  ['id' => '293ad', 'name' => 'Product 1', 'qty' => 1, 'price' => 10.00, 'weight' => 550],
  ['id' => '4832k', 'name' => 'Product 2', 'qty' => 1, 'price' => 10.00, 'weight' => 550, 'options' => ['size' => 'large']]
]);

Cart::add([$product1, $product2]);

Cart::update()

To update an item in the cart, you'll first need the rowId of the item. Next you can use the update() method to update it.

If you simply want to update the quantity, you'll pass the update method the rowId and the new quantity:

$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';

Cart::update($rowId, 2); // Will update the quantity

If you would like to update options of an item inside the cart,

$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';

Cart::update($rowId, ['options'  => ['size' => 'small']]); // Will update the size option with new value

If you want to update more attributes of the item, you can either pass the update method an array or a Buyable as the second parameter. This way you can update all information of the item with the given rowId.

Cart::update($rowId, ['name' => 'Product 1']); // Will update the name

Cart::update($rowId, $product); // Will update the id, name and price

Cart::remove()

To remove an item for the cart, you'll again need the rowId. This rowId you simply pass to the remove() method and it will remove the item from the cart.

$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';

Cart::remove($rowId);

Cart::get()

If you want to get an item from the cart using its rowId, you can simply call the get() method on the cart and pass it the rowId.

$rowId = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';

Cart::get($rowId);

Cart::content()

Of course you also want to get the carts content. This is where you'll use the content method. This method will return a Collection of CartItems which you can iterate over and show the content to your customers.

Cart::content();

This method will return the content of the current cart instance, if you want the content of another instance, simply chain the calls.

Cart::instance('wishlist')->content();

Cart::destroy()

If you want to completely remove the content of a cart, you can call the destroy method on the cart. This will remove all CartItems from the cart for the current cart instance.

Cart::destroy();

Cart::weight()

The weight() method can be used to get the weight total of all items in the cart, given their weight and quantity.

Cart::weight();

The method will automatically format the result, which you can tweak using the three optional parameters

Cart::weight($decimals, $decimalSeperator, $thousandSeperator);

You can set the default number format in the config file.

If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the total property $cart->weight

Cart::total()

The total() method can be used to get the calculated total of all items in the cart, given there price and quantity.

Cart::total();

The method will automatically format the result, which you can tweak using the three optional parameters

Cart::total($decimals, $decimalSeparator, $thousandSeparator);

You can set the default number format in the config file.

If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the total property $cart->total

Cart::tax()

The tax() method can be used to get the calculated amount of tax for all items in the cart, given there price and quantity.

Cart::tax();

The method will automatically format the result, which you can tweak using the three optional parameters

Cart::tax($decimals, $decimalSeparator, $thousandSeparator);

You can set the default number format in the config file.

If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the tax property $cart->tax

Cart::subtotal()

The subtotal() method can be used to get the total of all items in the cart, minus the total amount of tax.

Cart::subtotal();

The method will automatically format the result, which you can tweak using the three optional parameters

Cart::subtotal($decimals, $decimalSeparator, $thousandSeparator);

You can set the default number format in the config file.

If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the subtotal property $cart->subtotal

Cart::discount()

The discount() method can be used to get the total discount of all items in the cart.

Cart::discount();

The method will automatically format the result, which you can tweak using the three optional parameters

Cart::discount($decimals, $decimalSeparator, $thousandSeparator);

You can set the default number format in the config file.

If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the subtotal property $cart->discount

Cart::initial()

The initial() method can be used to get the total price of all items in the cart before applying discount and taxes.

It could be deprecated in the future. When rounded could be affected by the rounding issue, use it carefully or use Cart::priceTotal()

Cart::initial();

The method will automatically format the result, which you can tweak using the three optional parameters.

Cart::initial($decimals, $decimalSeparator, $thousandSeparator);

You can set the default number format in the config file.

Cart::priceTotal()

The priceTotal() method can be used to get the total price of all items in the cart before applying discount and taxes.

Cart::priceTotal();

The method return the result rounded based on the default number format, but you can tweak using the three optional parameters

Cart::priceTotal($decimals, $decimalSeparator, $thousandSeparator);

You can set the default number format in the config file.

If you're not using the Facade, but use dependency injection in your (for instance) Controller, you can also simply get the subtotal property $cart->initial

Cart::count()

If you want to know how many items there are in your cart, you can use the count() method. This method will return the total number of items in the cart. So if you've added 2 books and 1 shirt, it will return 3 items.

Cart::count();
$cart->count();

Cart::search()

To find an item in the cart, you can use the search() method.

This method was changed on version 2

Behind the scenes, the method simply uses the filter method of the Laravel Collection class. This means you must pass it a Closure in which you'll specify you search terms.

If you for instance want to find all items with an id of 1:

$cart->search(function ($cartItem, $rowId) {
	return $cartItem->id === 1;
});

As you can see the Closure will receive two parameters. The first is the CartItem to perform the check against. The second parameter is the rowId of this CartItem.

The method will return a Collection containing all CartItems that where found

This way of searching gives you total control over the search process and gives you the ability to create very precise and specific searches.

Cart::setTax($rowId, $taxRate)

You can use the setTax() method to change the tax rate that applies to the CartItem. This will overwrite the value set in the config file.

Cart::setTax($rowId, 21);
$cart->setTax($rowId, 21);

Cart::setGlobalTax($taxRate)

You can use the setGlobalTax() method to change the tax rate for all items in the cart. New items will receive the setGlobalTax as well.

Cart::setGlobalTax(21);
$cart->setGlobalTax(21);

Cart::setGlobalDiscount($discountRate)

You can use the setGlobalDiscount() method to change the discount rate for all items in the cart. New items will receive the discount as well.

Cart::setGlobalDiscount(50);
$cart->setGlobalDiscount(50);

Cart::setDiscount($rowId, $taxRate)

You can use the setDiscount() method to change the discount rate that applies a CartItem. Keep in mind that this value will be changed if you set the global discount for the Cart afterwards.

Cart::setDiscount($rowId, 21);
$cart->setDiscount($rowId, 21);

Buyable

For the convenience of faster adding items to cart and their automatic association, your model has to implement the Buyable interface. You can use the CanBeBought trait to implement the required methods but keep in mind that these will use predefined fields on your model for the required values.

<?php
namespace App\Models;

use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;

class Product extends Model implements Buyable {
    use Gloudemans\Shoppingcart\CanBeBought;
}

If the trait does not work for on the model or you wan't to map the fields manually the model has to implement the Buyable interface methods. To do so, it must implement such functions:

    public function getBuyableIdentifier(){
        return $this->id;
    }
    public function getBuyableDescription(){
        return $this->name;
    }
    public function getBuyablePrice(){
        return $this->price;
    }
    public function getBuyableWeight(){
        return $this->weight;
    }

Example:

<?php
namespace App\Models;

use Gloudemans\Shoppingcart\Contracts\Buyable;
use Illuminate\Database\Eloquent\Model;

class Product extends Model implements Buyable {
    public function getBuyableIdentifier($options = null) {
        return $this->id;
    }
    public function getBuyableDescription($options = null) {
        return $this->name;
    }
    public function getBuyablePrice($options = null) {
        return $this->price;
    }
    public function getBuyableWeight($options = null) {
        return $this->weight;
    }
}

Collections

On multiple instances the Cart will return to you a Collection. This is just a simple Laravel Collection, so all methods you can call on a Laravel Collection are also available on the result.

As an example, you can quicky get the number of unique products in a cart:

Cart::content()->count();

Or you can group the content by the id of the products:

Cart::content()->groupBy('id');

Instances

The packages supports multiple instances of the cart. The way this works is like this:

You can set the current instance of the cart by calling Cart::instance('newInstance'). From this moment, the active instance of the cart will be newInstance, so when you add, remove or get the content of the cart, you're work with the newInstance instance of the cart. If you want to switch instances, you just call Cart::instance('otherInstance') again, and you're working with the otherInstance again.

So a little example:

Cart::instance('shopping')->add('192ao12', 'Product 1', 1, 9.99, 550);

// Get the content of the 'shopping' cart
Cart::content();

Cart::instance('wishlist')->add('sdjk922', 'Product 2', 1, 19.95, 550, ['size' => 'medium']);

// Get the content of the 'wishlist' cart
Cart::content();

// If you want to get the content of the 'shopping' cart again
Cart::instance('shopping')->content();

// And the count of the 'wishlist' cart again
Cart::instance('wishlist')->count();

You can also use the InstanceIdentifier Contract to extend a desired Model to assign / create a Cart instance for it. This also allows to directly set the global discount.

<?php

namespace App;
...
use Illuminate\Foundation\Auth\User as Authenticatable;
use Gloudemans\Shoppingcart\Contracts\InstanceIdentifier;

class User extends Authenticatable implements InstanceIdentifier
{
	...

	/**
     * Get the unique identifier to load the Cart from
     *
     * @return int|string
     */
    public function getInstanceIdentifier($options = null)
    {
        return $this->email;
    }

    /**
     * Get the unique identifier to load the Cart from
     *
     * @return int|string
     */
    public function getInstanceGlobalDiscount($options = null)
    {
        return $this->discountRate ?: 0;
    }
}

// Inside Controller
$user = \Auth::user();
$cart = Cart::instance($user);



N.B. Keep in mind that the cart stays in the last set instance for as long as you don't set a different one during script execution.

N.B.2 The default cart instance is called default, so when you're not using instances,Cart::content(); is the same as Cart::instance('default')->content().

Models

Because it can be very convenient to be able to directly access a model from a CartItem is it possible to associate a model with the items in the cart. Let's say you have a Product model in your application. With the associate() method, you can tell the cart that an item in the cart, is associated to the Product model.

That way you can access your model right from the CartItem!

The model can be accessed via the model property on the CartItem.

If your model implements the Buyable interface and you used your model to add the item to the cart, it will associate automatically.

Here is an example:

// First we'll add the item to the cart.
$cartItem = Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large']);

// Next we associate a model with the item.
Cart::associate($cartItem->rowId, 'Product');

// Or even easier, call the associate method on the CartItem!
$cartItem->associate('Product');

// You can even make it a one-liner
Cart::add('293ad', 'Product 1', 1, 9.99, 550, ['size' => 'large'])->associate('Product');

// Now, when iterating over the content of the cart, you can access the model.
foreach(Cart::content() as $row) {
	echo 'You have ' . $row->qty . ' items of ' . $row->model->name . ' with description: "' . $row->model->description . '" in your cart.';
}

Database

Configuration

To save cart into the database so you can retrieve it later, the package needs to know which database connection to use and what the name of the table is. By default the package will use the default database connection and use a table named shoppingcart. You can change that in the configuration.

To make your life easy, the package also includes a ready to use migration which you can publish by running:

php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations"

This will place a shoppingcart table's migration file into database/migrations directory. Now all you have to do is run php artisan migrate to migrate your database.

Storing the cart

To store your cart instance into the database, you have to call the store($identifier) method. Where $identifier is a random key, for instance the id or username of the user.

Cart::store('username');

// To store a cart instance named 'wishlist'
Cart::instance('wishlist')->store('username');

Restoring the cart

If you want to retrieve the cart from the database and restore it, all you have to do is call the restore($identifier) where $identifier is the key you specified for the store method.

Cart::restore('username');

// To restore a cart instance named 'wishlist'
Cart::instance('wishlist')->restore('username');

Merge the cart

If you want to merge the cart with another one from the database, all you have to do is call the merge($identifier) where $identifier is the key you specified for the store method. You can also define if you want to keep the discount and tax rates of the items and if you want to dispatch "cart.added" events.

// Merge the contents of 'savedcart' into 'username'.
Cart::instance('username')->merge('savedcart', $keepDiscount, $keepTaxrate, $dispatchAdd, 'savedcartinstance');

Erasing the cart

If you want to erase the cart from the database, all you have to do is call the erase($identifier) where $identifier is the key you specified for the store method.

Cart::erase('username');

// To erase a cart switching to an instance named 'wishlist'
Cart::instance('wishlist')->erase('username');

Calculators

The calculation logic for the package is implemented and defined in Calculator classes. These implement the Gloudemans\Shoppingcart\Contracts\Calculator Contract and and determine how the prices are calculated and rounded. The calculators can be configured in the confugration file. This is the default calculator:

<?php

namespace Gloudemans\Shoppingcart\Calculation;

use Gloudemans\Shoppingcart\CartItem;
use Gloudemans\Shoppingcart\Contracts\Calculator;

class DefaultCalculator implements Calculator
{
    public static function getAttribute(string $attribute, CartItem $cartItem)
    {
        $decimals = config('cart.format.decimals', 2);

        switch ($attribute) {
            case 'discount':
                return $cartItem->price * ($cartItem->getDiscountRate() / 100);
            case 'tax':
                return round($cartItem->priceTarget * ($cartItem->taxRate / 100), $decimals);
            case 'priceTax':
                return round($cartItem->priceTarget + $cartItem->tax, $decimals);
            case 'discountTotal':
                return round($cartItem->discount * $cartItem->qty, $decimals);
            case 'priceTotal':
                return round($cartItem->price * $cartItem->qty, $decimals);
            case 'subtotal':
                return max(round($cartItem->priceTotal - $cartItem->discountTotal, $decimals), 0);
            case 'priceTarget':
                return round(($cartItem->priceTotal - $cartItem->discountTotal) / $cartItem->qty, $decimals);
            case 'taxTotal':
                return round($cartItem->subtotal * ($cartItem->taxRate / 100), $decimals);
            case 'total':
                return round($cartItem->subtotal + $cartItem->taxTotal, $decimals);
            default:
                return;
        }
    }
}

Exceptions

The Cart package will throw exceptions if something goes wrong. This way it's easier to debug your code using the Cart package or to handle the error based on the type of exceptions. The Cart packages can throw the following exceptions:

Exception Reason
CartAlreadyStoredException When trying to store a cart that was already stored using the specified identifier
InvalidRowIDException When the rowId that got passed doesn't exists in the current cart instance
UnknownModelException When you try to associate an none existing model to a CartItem.

Events

The cart also has events build in. There are five events available for you to listen for.

Event Fired Parameter
cart.adding When adding an item to the cart. The CartItem that is being added.
cart.updating When updating an item to the cart. The CartItem that is being updated.
cart.removing When removing an item to the cart. The CartItem that is being removed.
cart.added When an item was added to the cart. The CartItem that was added.
cart.updated When an item was updated to the cart. The CartItem that was updated.
cart.removed When an item was removed from the cart. The CartItem that was removed.
cart.merged When the content of a cart is merged -
cart.stored When the content of a cart was stored. -
cart.restored When the content of a cart was restored. -
cart.erased When the content of a cart was erased. -

Example

Below is a little example of how to list the cart content in a table:

// Add some items in your Controller.
Cart::add('192ao12', 'Product 1', 1, 9.99);
Cart::add('1239ad0', 'Product 2', 2, 5.95, ['size' => 'large']);

// Display the content in a View.
<table>
   	<thead>
       	<tr>
           	<th>Product</th>
           	<th>Qty</th>
           	<th>Price</th>
           	<th>Subtotal</th>
       	</tr>
   	</thead>

   	<tbody>

   		<?php foreach(Cart::content() as $row) :?>

       		<tr>
           		<td>
               		<p><strong><?php echo $row->name; ?></strong></p>
               		<p><?php echo ($row->options->has('size') ? $row->options->size : ''); ?></p>
           		</td>
           		<td><input type="text" value="<?php echo $row->qty; ?>"></td>
           		<td>$<?php echo $row->price; ?></td>
           		<td>$<?php echo $row->total; ?></td>
       		</tr>

	   	<?php endforeach;?>

   	</tbody>
   	
   	<tfoot>
   		<tr>
   			<td colspan="2">&nbsp;</td>
   			<td>Subtotal</td>
   			<td><?php echo Cart::subtotal(); ?></td>
   		</tr>
   		<tr>
   			<td colspan="2">&nbsp;</td>
   			<td>Tax</td>
   			<td><?php echo Cart::tax(); ?></td>
   		</tr>
   		<tr>
   			<td colspan="2">&nbsp;</td>
   			<td>Total</td>
   			<td><?php echo Cart::total(); ?></td>
   		</tr>
   	</tfoot>
</table>

Collaborators

bumbummen99
Patrick
Sartoric
Sartoric

Contributors

bumbummen99
Patrick
Crinsane
Rob Gloudemans
Norris1z
Norris Oduro
olegbespalov
Oleg Bespalov
cwprogger
Andrew Savchenko
ChrisThompsonTLDR
Chris Thompson
Jam-Iko
Jam-Iko
mattusik
Matus Rohal
rakibabu
Rakhal Imming
tiotobing
Tiotobing
Sartoric
Sartoric
macbookandrew
Andrew Minion
dtwebuk
Daniel Tomlinson
tkaw220
Edwin Aw
manojo123
Jorge Moura
jorgejavierleon
Jorge Javier León
geisi
Tim Geisendörfer
adamgoose
Adam Engebretson
andcl
Andrés
ganyicz
Filip Ganyicz
guysolamour
Guy-roland ASSALE
jackmcdade
Jack McDade
jeremyvaught
Jeremy Vaught
jmarkese
John Markese
nexxai
JT Smith
mrabbani
Mahbub Rabbani
mauriciv
Mauricio Vera
xpundel
Mikhail Lisnyak
absemetov
Nadir Absemetov
nielsiano
Niels Stampe
4ilo
Olivier
PazkaL
Pascal Kousbroek
quintenbuis
Quinten Buis
publiux
Raul Ruiz
royduin
Roy Duineveld
CaddyDz
Salim Djerbouh
pendalff
Fukalov Sem
sobhanatar
Sobhan Atar
mightyteja
Teja Babu S
kekenec
Kekenec
sasin91
Sasin91
Comments
  • Cart total rounding issue

    Cart total rounding issue

    I've seen that there is a rounding issue on the cart total

    (discount,subtotal,taxes,total) rounding

    and in fact the __get() is not rounding the result of the internal calculation (adding more decimals to the results). This lead to a potential rounding issue in the total.

    Digging a bit I've found this in Crinsane version

    https://github.com/Crinsane/LaravelShoppingcart/issues/344 and this PR https://github.com/hardevine/LaravelShoppingcart/pull/5/commits/3e4f5a14ba58bd7cee950de3d91ea10960c2724f

    I guess it should be done something similar here using round as some values are used for other calculation

    bug critical stale 
    opened by Sartoric 16
  • more than two product not adding to cart

    more than two product not adding to cart

    when i am trying to add to cart first two product added but not adding more then two product i am using it on laravel 7 here is my controller code

      try{
    
        $id           = $request->id;
        $product_name = $request->product_name;
        $qty          = $request->qty;
        $price        = $request->price;
        $current_qty  = $request->current_qty;
        $image        = $request->product_image;
        $qty_unit     = $request->qty_unit;
    
        if ($qty_unit != '') 
        {
           $product_name = $product_name.'('.$qty_unit.')'; 
        }
    
        // checking stock 
        if($qty > $current_qty)
        {
            return response()->json(['status'=>'error','message' => 'Product out of Stock']);
    
        }
         $cart =  Cart::content()->where('id',$id)->first();
         // checking if cart have the product alredy 
         if($cart)
         {  
    
            if($cart->qty+$qty > $current_qty)
            {
            return response()->json(['status'=>'error','message' => 'Product out of Stock']);
            }
    
         }
    
         Cart::add(['id' => $id, 'name' => $product_name, 'qty' => $qty, 'price' => $price, 'weight' => 0, 'options' => ['image' => $image],'discount' => 0]);
         
         return response()->json(['status' => 'success','message' => 'Product Added To Cart']);
     }
     catch(\Exception $e)
     {
      
      return $e;
    
     }
    
    question stale 
    opened by shakhawatfci 12
  • Session storage is not working

    Session storage is not working

    Item added to session does not save

    Product = Product::find($id);

    $user = \auth()->id();
    
    $item = Cart::instance($user)->add([
        'id' => $Product->id,
        'name' => $Product->name,
        'qty' => 1,
        'price' => $Product->price,
        'weight' => 200,
        'options' => ['size' => 'large']
    ])->associate($Product);
    
    $items = Cart::instance($user)->content();
    
    dd($items);
    
    bug stale 
    opened by chiwex 10
  • Having trouble understanding the

    Having trouble understanding the "flow"

    Ok so this is probably old hat for most of you but this is the first time I'm building a site with a cart functionality in it, and I'm having trouble wrapping my brain around a few super basic things and I feel like I'm missing a critical piece of knowledge that is going to make this all come together.

    To begin, I've written some very basic logic to generate a cartId like this:

    public function get_cartId()
    {
        if (Auth::guard('api')->user()) {
            $cartId = Auth::guard('api')->user()->id;
        } else {
            $cartId = md5(random_bytes(4));
        }
    
        return $cartId;
    }
    

    The idea is that if you're logged in, the cart should be attached to your actual user ID; if you are not logged in, the cartId will just be a random MD5. This allows new users to browse and add items to a cart without actually needing a login before hand.

    Now here's where I already start to get lost. If I'm reading the documentation correctly, I have to call the Cart::instance($cartId); before I do anything else. Then let's add an item to the cart, so I do Cart::add([...]); Next, if I'm reading the documentation correctly, if I want to persist/store the cart to the DB, I need to call Cart::store($cartId); within my controller after I've performed the action. So at this point, my controller should look something like this:

    public function store(CartRequest $request)
    {
        $cartId = CartService::get_cartId($request);
    
        Cart::instance($cartId);
    
        Cart::add(
            (int) $request->product_id,
            (string) 'Example description',
            (int) $request->quantity,
            (int) 10,
            0
        );
    
        Cart::store($cartId);
    
        return response('Item added to cart', 202)->cookie('cart_id', $cartId);
    }
    

    But now, if the person tries to add a second item to their cart, they get an error saying the cart with that cartId already exists, because apparently the store() method doesn't do an updateOrCreate() behind the scenes(????). So now I have to add a Cart::restore($cartId); before the ::instance(); because it removes the entry from the DB temporarily until the ::store(); puts it back. The destroy() controller method needs an equivalent number of calls, substituting the ::remove() for the ::add().

    Having 4 Cart:: calls in a single controller method seems excessive, and I feel like I'm missing some foundational piece of "cart-making" and am doing way too much to accomplish a relatively simple task.

    Can someone help clear up whether what I'm doing is correct or not, and assuming that I'm doing it wrong, can you even just give some super basic pseudocode of what I should be doing?

    stale 
    opened by nexxai 9
  • Cannot install on Laravel 9 Project

    Cannot install on Laravel 9 Project

    Hello, looking at your previous issues it seemed that this project can be installed on Laravel 9.

    I receive this issue when trying to install:

    Your requirements could not be resolved to an installable set of packages.

    Problem 1 - Root composer.json requires bumbummen99/shoppingcart ^4.1 -> satisfiable by bumbummen99/shoppingcart[4.1.0]. - bumbummen99/shoppingcart 4.1.0 requires illuminate/support 5.4.||5.5.||5.6.||5.7.||5.8.*||^6.0||^7.0||^8.0 -> found illuminate/support[v5.4.0, ..., 5.8.x-dev, v6.0.0, ..., 6.x-dev, v7.0.0, ..., 7.x-dev, v8.0.0, ..., 8.x-dev] but these were not loaded, likely because it conflicts with another require.

    You can also try re-running composer require with an explicit version constraint, e.g. "composer require bumbummen99/shoppingcart:*" to figure out if any version is installable, or "composer require bumbummen99/shoppingcart:^2.1" if you know which you need.

    If there is anything you can suggest to be able to use 4.1 with Laravel 9 it would be greatly appreciated

    opened by mrdigitalau 8
  • [Question] Update cart when products price update

    [Question] Update cart when products price update

    I have a problem when using package. -Customer add product to cart which price 10$ -Admin change price to 15$ or flash sale price -30% -When checkout price still 10$ -> wrong.

    How i can fix that ? Thanks.

    stale 
    opened by vanthao03596 7
  • Fix products sequence after changing cart item options

    Fix products sequence after changing cart item options

    This PR fixes the problem with changing products sequence on updating cart items options.

    Whenever you update your cart item using array it regenerates its own rowId. If you change any option of the item (for example color or size) - you will get a new rowId resulting in cart item rewriting in the collection and moving to the end of the list.

    This PR preserves item's original position in the collection while keeping its new rowId.

    opened by cwprogger 7
  • Issue with discountTotal and fixed value discount

    Issue with discountTotal and fixed value discount

    Today I've faced an issue due to the rounding for the property discountTotal.

    Something mentioned also here : #48

    Long story short. I apply some fixed amount custom coupon to the cart, so I convert the value to percentage based on cart total. I already have a percentage discount applied to the cart.

    |discount|value| |---|---| |% | 1,96| |Coupon | 25 | |Total | 26,95 (clearly wrong 😁 )|

    The issues seems to be due to the discountFloat() getting for each item in the cart the already rounded discountTotal value (and of course to the fact that the discount is a fixed value converted to percentage)

    Avoid rounding at Calculator level seems to solve (or at least minimize the issue) so I'll send a PR to update the Calculator.

    Maybe we could rethink if this can be applied to other totals too.

    stale 
    opened by Sartoric 6
  • 502 Bad Gateway whenever Cart has more than 1 item?

    502 Bad Gateway whenever Cart has more than 1 item?

    If I submit a post request to an endpoint, and inside the controller I do something like this;

            Cart::add([
                [
                    'id' => '293ad', 'name' => 'Product 1', 'qty' => 1,
                    'price' => 10.00, 'weight' => 550
                ],
                [
                    'id' => '4832k', 'name' => 'Product 2', 'qty' => 1,
                    'price' => 10.00, 'weight' => 550,
                    'options' => ['size' => 'large']
                ]
            ]);
    

    Whenever I return a response (even if it's just something plaintext), I get a 502 Bad Gateway. If I use dd() before the response that comes back fine and upon inspecting the Cart everything appears normal.

    If I make the cart only 1 item like so;

            Cart::add([
                [
                    'id' => '293ad', 'name' => 'Product 1', 'qty' => 1,
                    'price' => 10.00, 'weight' => 550
                ],
            ]);
    

    It works and the response comes back just fine.

    I have absolutely no idea why this is happening and can't really seem to figure it out. Hopefully someone can provide some answers!

    question stale 
    opened by frequentredundancy 6
  • Translate Readme to other languages

    Translate Readme to other languages

    In order to increase the use of the Readme and to reach a broader audience. In order to participate, please create a Translation and name it e.g. "Readme_en-US.md".

    enhancement help wanted Hacktoberfest stale 
    opened by bumbummen99 5
  • SQLSTATE[42000]: Syntax error or access violation

    SQLSTATE[42000]: Syntax error or access violation

    Hello! when I runned the migration generated by this command php artisan vendor:publish --provider="Gloudemans\Shoppingcart\ShoppingcartServiceProvider" --tag="migrations"

    I get the following error:

    Illuminate\Database\QueryException SQLSTATE[42000]: Syntax error or access violation: 1103 Incorrect table name '' (SQL: create table `` (identifier varchar(255) not null, instance varchar(255) not null, content longtext not null, created_at timestamp null, updated_at timestamp null) default character set utf8mb4 collate 'utf8mb4_unicode_ci')

    at C:\xampp\htdocs\web-site\vendor\laravel\framework\src\Illuminate\Database\Connection.php:671 667| // If an exception occurs when attempting to run a query, we'll format the error 668| // message to include the bindings with SQL, which will make this exception a 669| // lot more helpful to the developer instead of just the database's errors. 670| catch (Exception $e) {

    671| throw new QueryException( 672| $query, $this->prepareBindings($bindings), $e 673| ); 674| } 675|

    1 C:\xampp\htdocs\web-site\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDO\Exception.php:18 Doctrine\DBAL\Driver\PDO\Exception::("SQLSTATE[42000]: Syntax error or access violation: 1103 Incorrect table name ''")

    2 C:\xampp\htdocs\web-site\vendor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOConnection.php:84 Doctrine\DBAL\Driver\PDO\Exception::new(Object(PDOException))

    Why?

    Regards

    stale 
    opened by pixsolution 4
  • Laravel Socialite Cognito is destroying the session with Cart on login

    Laravel Socialite Cognito is destroying the session with Cart on login

    Working in Laravel 9 with LaravelShoppingcart and Socialite Cognito. I have a registration schedule where students can apply for diverse courses and added to the Cart. At the checkout I make an authentication with cognito, but it returns a new session and destroy the last session where the cart is stored.

    I have checked the docs and the config files, but I cannot find any point about this.

    Can you help me, please?

    Working in Laravel 9 with LaravelShoppingcart and Socialite Cognito. I have a registration schedule where students can apply for diverse courses and added to the Cart. At the checkout I make an authentication with cognito, but it returns a new session and destroy the last session where the cart is stored.

    I have checked the docs and the config files, but I cannot find any point about this.

    Can you help me, please?

    question stale 
    opened by basiltap 2
  • Deprecate session, use database instead

    Deprecate session, use database instead

    Instead of storing CartItems in the session we should store them in the database instead. While this does put more load on the database, it will allow for a far better implementation in regards to API usage (there is no session). Also it will allow us to use the power of Eloquent.

    ToDo:

    • [ ] Store CartItems in the database
    • [ ] Optimize queries
    • [ ] Cache what can be cached
    enhancement help wanted 
    opened by bumbummen99 4
  • Refactoring

    Refactoring

    • [x] Raise required PHP and dependency versions according to supported versions
    • [x] Add type hints up to 8.0
    • [x] Support Laravel 9
    • [x] Use MoneyPHP instead of direct values
    • [ ] Adjust the general workflow
    enhancement help wanted 
    opened by bumbummen99 3
  • Floating point issues generates wrong totals

    Floating point issues generates wrong totals

    Hello, This problem touches on all versions because this is a PHP floating point behaviour. Usually when dealing with prices all plugins must multiply prices by 100 and deal with Cents not Dollars + Cents.

    Example: Lets say i create a cart with some items, i attach a global discount of 10% and my order total is 60.40 Eur. I save it to Database. If i recreate the cart in order to refund some items i want to show the difference between the original total and new total. This is how i spotted the problem. The app was showing me that 60.40 Eur - 60.40 !== 0.0 Screen Shot 2021-12-14 at 11 26 32 AM Eur = 7.105427357601E-15

    ($order->total - ($cart->totalFloat() + $transportValue))
    $order->total is of type float 60.40
    $cart->totalFloat() is of type float 60.40 (but actually, 60.39 because its rounded up)
    $transportValue = 10.00 Eur
    

    https://stackoverflow.com/questions/12291042/weird-php-floating-point-behavior

    help wanted 
    opened by bogdanbradeanu 5
Releases(4.2.0)
Owner
Patrick
Software-Engineer ⚙️ WebDeveloper 🕷️. Currently working for Intelli Shop
Patrick
Shopping Cart Implementation for Laravel Framework

Shopping Cart Implementation for Laravel Framework

darryl fernandez 1.2k Jan 4, 2023
Benefit PHP Shopping Cart Class (Simple Lightweight)

Benefit-PHP-Shopping-Cart-Class Benefit PHP Shopping Cart Class (Simple Lightweight) Table of Contents Initialization Get All Get Item Get Item Child

Osman Cakmak 8 Sep 6, 2022
AvoRed an Open Source Laravel Shopping Cart

AvoRed is commin up as a headless graphql version. AvoRed is a free open-source e-commerce platform written in PHP based on Laravel. Its an ingenuous

AvoRed Laravel E commerce 1.4k Dec 30, 2022
Laravel Shopping Cart Package

LaraCart - Laravel Shopping Cart Package (http://laracart.lukepolo.com) Features Coupons Session Based System Cross Device Support Multiple cart insta

Luke Policinski 516 Dec 10, 2022
AvoRed an Open Source Laravel Shopping Cart

AvoRed is commin up as a headless graphql version. AvoRed is a free open-source e-commerce platform written in PHP based on Laravel. Its an ingenuous

AvoRed Laravel E commerce 1.4k Dec 28, 2022
LiteCart - Free Shopping Cart Platform - Built with PHP, jQuery HTML 5 and CSS 3

LiteCart is a lightweight e-commerce platform for online merchants. Developed in PHP, HTML 5, and CSS 3. LiteCart is a registered trademark, property

LiteCart 153 Dec 28, 2022
A free shopping cart system. OpenCart is an open source PHP-based online e-commerce solution.

OpenCart is a free open source ecommerce platform for online merchants. OpenCart provides a professional and reliable foundation from which to build a successful online store.

OpenCart 6.6k Dec 31, 2022
PrestaShop is an Open Source e-commerce platform, committed to providing the best shopping cart experience for both merchants and customers.

PrestaShop is an Open Source e-commerce web application, committed to providing the best shopping cart experience for both merchants and customers. It is written in PHP, is highly customizable, supports all the major payment services, is translated in many languages and localized for many countries, has a fully responsive design (both front and back office), etc. See all the available features.

PrestaShop 6.9k Dec 31, 2022
A robust session-based shopping bag for Laravel

Shopping Bag A robust session-based shopping bag for Laravel Go to documentation Documentation Documentation for Shopping Bag can be found in the docs

Laraware 30 Dec 13, 2021
Topshop offers its customers a modern shopping experience by bringing computers, appliances, clothing and many other items at their fingertips.

Topshop offers its customers a modern shopping experience by bringing computers, appliances, clothing and many other items at their fingertips. With just a few clicks, users can create an account, add products to their cart and place their order.

Abhijeet Pitumbur 2 Aug 8, 2022
The Online Shopping System in PHP using XAMPP as virtual Server.

Online shopping is a form of electronic commerce which allows consumers to directly buy goods or services from a seller over the Internet using a web browser or a mobile app.

Magesh Surya Ambikapathi 5 Sep 15, 2022
(Live Link) Extensive ecommerce site with vendors, mods & ability to add to cart without being logged in. Upgraded to Laravel 8.x

(Live Link) Extensive ecommerce site with vendors, mods & ability to add to cart without being logged in. Upgraded to Laravel 8.x

null 14 Dec 21, 2022
Magento 2 module to only allow checkout when the number of items in the cart are a multiple of X.

Cart Quantity Multiple - Magento 2 Module Introduction This module allows to limit checkout only when the contents of the cart are a multiple of X

ADVOCODO 3 Apr 7, 2022
Zen Cart® is a full-function e-commerce application for your website.

Zen Cart® - The Art of E-Commerce Zen Cart® was the first Open Source e-Commerce web application to be fully PA-DSS Certified. Zen Cart® v1.5.8 is an

Zen Cart 304 Jan 6, 2023
Magento 2 Share Cart extension Free

Mageplaza Share Cart Extension helps customers in sharing their shopping cart with friends and family as well. This is a supportive method to promote store’s conversion rate via the existing users, and this can significantly contribute to the revenue of the store.

Mageplaza 12 Jul 22, 2022
A simple shoppingcart implementation for Phalcon.

Phalcon Shopping Cart A simple shoppingcart implementation for Phalcon. Installation Install the package through Composer. Run the Composer require co

Sergey Mukhin 3 Oct 7, 2022
PHP implementation of Fowler's Money pattern.

Money PHP library to make working with money safer, easier, and fun! "If I had a dime for every time I've seen someone use FLOAT to store currency, I'

Money PHP 4.2k Jan 2, 2023
Example of Pre-Loader Implementation for Magento 2

Example of preloader Implements optimistic preloader for configurable product data without taking into account simple product status for: Price of con

EcomDev B.V. 10 Dec 12, 2021
Simple E-Comerce build use Laravel 5.6 require php version 5.6 or 7.4

Simple E-Comerce build use Laravel 5.6 require php version 5.6 or 7.4

Rangga Darmajati 3 Oct 7, 2021