Giter Club home page Giter Club logo

laravelshoppingcart's Introduction

Attention

Due to the difficulty experienced by certain individuals and companies in comprehending the copyleft provision of the MIT license, as evidenced by the unauthorized relicensing of my work and that of others, I have opted to terminate all further development and assistance for this repository. Given that this package is intended for use in e-commerce projects, it is expected that the necessary resources would be available for the development and support of your own solutions.

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

laravelshoppingcart's People

Contributors

4ilo avatar absemetov avatar bumbummen99 avatar caddydz avatar crinsane avatar cwprogger avatar dtwebuk avatar github-actions[bot] avatar jam-iko avatar jorgejavierleon avatar kekenec avatar macbookandrew avatar manojo123 avatar mattusik avatar mightyteja avatar nielsiano avatar norris1z avatar olegbespalov avatar pazkal avatar pendalff avatar publiux avatar quintenbuis avatar rakibabu avatar royduin avatar sartoric avatar sasin91 avatar sobhanatar avatar tiotobing avatar tkaw220 avatar xpundel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravelshoppingcart's Issues

Ability to customise how the cart handles pricing?

So is it somehow possible we can make it so that the cart can take prices in cents rather than decimal? Generally speaking it's better practice to keep it as cents in the DB. Or at minimum add an option for those who want it so that others who don't want to use it don't have to.

As a side question as well; with the buyable interface is it possible we can let the price be it's own thing?

I'm making a SaaS application where products can have multiple prices, and I'm storing them via ManyToMany with the product. It would nice to be able to pass in the Price model along with the product on Cart::add.

Not sure if these are already possible but from my testing it doesn't seem like it!

Storing extra data

Hey, thanks for this awesome script

I have a question... seeing that can do multiple instances, is there any way at all possible that we can store extra data pertaining to an instance in there also?

I was hoping to store some extra data relating to the cart itself, not items, but some options that are related to the cart.

For example, I am building a shopping cart for a pizza store that has multiple stores.
When they flick between stores, the cart instance changes to the one relating to that store, however, I also need to store some other data like delivery address in that instance also so it is not shared by the other instances if that makes sense?

Is there any ability to add options like this to an instance , as opposed to doing it in the session?

Thanks very much in advance

Please supply a valid weight. in laravel 6.2

this is in CartController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Product;
use Cart;

class CartController extends Controller
{
    public function addToCart($id)
    {
    	$product = Product::find($id);
    	
    	Cart::add($id, $product->title,1, $product->price,['size'=>'s','image'=>$product->image]);

    	return back()->withInfo('Added product to cart');
    }
}

this in web.php

Route::get('/', function () {
	$cartItems = Cart::content();
	$categories = App\Category::all();
	$products = App\Product::paginate(8);
    return view('welcome', compact('products','categories','cartItems'));
});
Route::get('/addToCart/{id}', 'CartController@addToCart')-> name('addToCart');

this is the button

<a href="{{route('addToCart', $product->id)}}" class="btn btn-primary pull-right btn-sm">Add to Cart</a>

please help

Not associate model even i use item id as integer

I got this issue

{message: "The supplied model Product does not exist.",…}
exception: "Gloudemans\Shoppingcart\Exceptions\UnknownModelException"
file: "E:\xampp\htdocs\Laravel\brandzshop\vendor\bumbummen99\shoppingcart\src\Cart.php"
line: 467
message: "The supplied model Product does not exist."

public function addToCart($request)
{
$cart = [];
$product = Product::find($request->input('product_id'));
if ($product) {
$cart = Cart::add([
'id'=>$product->id,
'name' => $product->name,
'price' => $product->price,
'qty' => 1,
'weight' => 0,
]);

       Cart::associate($cart->rowId, 'Product');
	}
    
    return $cart;
}

How to use cart in appserviceprovider

i try to add some data into appserviceprovider to share data into header for showing quick data but it not get data, only when i go to detail page it get my counting data

my AppServiceProvider:
public function boot()
{
$data['items'] = Cart::content();
$data['cart_total'] = 0;
$data['promotion_total'] = 0;
$data['coupons_total'] = 0;

    foreach(Cart::content() as $item){
        $data['promotion_total'] += $item->price*$item->qty*$item->options->promotion/100;
        $data['cart_total'] += $item->price*$item->qty;

        if($item->discountRate > 0){
            $data['coupons_total'] += ($item->price*$item->qty - $item->price*$item->qty*$item->options->promotion/100)*$item->discountRate/100;
        } else {
            $data['coupons_total'] += $item->options->coupons_value;
        }
    }

    $data['pay_total'] = (double)$data['cart_total']-(double)$data['promotion_total']-(double)$data['coupons_total'];

view()->share($data);
}

please help me know which way i wrong.

How to completely disable session storage?

I'm working on a project where I ONLY want my cart to be stored in the database. For example, I do a check on the checkout page that if the cart is empty, redirect them back to the products page.

However, it would seem that even when the database is empty, this package is persisting in session storage which seems very dodgy. If someone opts to use database storage over session storage, there should not be duplication between the two since this would leave to inconsistent results. If I clear the cart from the database, that should be reflected everywhere. Instead, when I clear the session from the database, it doesn't even matter because the checkout is still stored in session.

I want to completely disable this session storage. How do I do it? Thanks.

Update the cart options

Hay if I try to update the price for items it updates the price, but what about options what if I need to update the options as well

I try this
Cart::update( $row->rowId, [ 'price' => $finalProPriceCheck->price, 'flagPriceChanged' => 1 ] );

Price updated but option flagPriceChanged didn't

any help here please

adding same product with diffrent Sizes

Hi i am trying to add Same product with diffrent sizes...but it overrides the size...and one thing more
Why it is necessary to add weight index.
Cart::add(['id' => $productId, 'name' => 'Product 1', 'qty' => 1, 'price' => 9.99, 'weight' => 550, 'options' => ['size' => $size_option]]);

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!

How to let Cart keep track of a custom "Price" model.

I'm writing a SaaS application where multiple different pricing tiers are sold, and the way I've dealt with this is by making a custom "Price" model that contains information such as;

  • The price amount.
  • The "duration" of what is being purchased. (e.g 14 Days)
  • Few other bits that I can't share.

Point is, it's its own model which has a UUID which is associated to the product. However, there could be 3-4 of these on a single product.

I was wondering if it's possible for me to record the "Price" UUID the user selected somehow in the cart so that I can reference it in other parts of my application when it comes to processing payments, etc... Almost like a "metadata" column or something?

For example, currently I have the issue of;

  1. Select Product
  2. Select "Price"
  3. Add to cart
  4. Process Payment
  5. On Payment Success, Deliver Product.
  6. ??? Uh-oh. How do I know how long their "subscription" should be since I no longer have access to the Price model itself, only the amount from the price that was recorded by the cart.

Hopefully I've explained myself to a decent capacity and hopefully someone can explain how I could accomplish this!

RowId of cart instance is not persistent across different cart instances

A new rowId is generated each time an item is removed from the default cart instance and added to a new cart instance. I tested Crinsane/LaravelShoppingcart package and it works, the rowId is persistent across the different instances. Please take a look at my sample code

// BuyLaterController Class

public function buyLater($id)

{
 //$id is rowId
  $item = Cart::get($id);

  $remove = Cart::remove($id);



    $duplicates = Cart::instance('buyLater')->search(function ($cartItem, $rowId) use($id) {
      return $rowId === $id;
    });

    if($duplicates->isNotEmpty())
    {
      return redirect()->route('cart.index')->with('message' , 'Item is already in save for later list');
    }

    $user_id = Product::find($item->id)->user_id;

    //dd($item);
    Cart::add(['id'=> $item->id,
                                     'name' => $item->name,
                                     'qty' => 1,
                                     'price' => $item->price,
                                     'weight' => 55,
                                     'options' => ['delivery' => $item->shipping_price, 'user_id' => $user_id,]
                                    ])->associate('App\Product');
   //dd($item->rowId);
    dd(Cart::content());


    return redirect()->route('cart.index')->with('message', 'Item was successfully added to save for later list');
}

Thank you for looking into it and more blessings to you.

Problem in Tax Customization

Hi, Thank you for this repo. This is a very good package. But I have problem with customization cart Tax.
I want to set tax 0. I try it into vendor/bumbummen99/shopingcart/src/config/cart.php and their tax set to 0 but still tax not reset. What should I do?

Is it possible to load the associated models?

As it was shown in the docs:

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

I have used similar approach to add models to cart.
Once I get the content out of the cart by doing:

Cart::content();

I can see the collection of CartItem models.

Question: is it possible to somehow load the related model?
I tried to modify the collection, but once it's casted to JSON, I think it calls CartItems toArray method or something. small_image_url attribute is not present in JSON:

$items = cart()->content()->transform(function ($item) {
    $product = Product::find($item->id);
    $item->small_image_url = $product->getImageUrl(Product::CONVERSION_SMALL);

    return $item;
});

How to attach cart to accounts?

Hey, unfortunately I've been unable to figure out how to make the cart work only with accounts. I created the database using the relevant documentation here but I'm still not entirely sure how to implement it. Basically I want the cart only to work with the specific authenticated user and to sync with the database so they can share the cart between browsers. How can I achieve this?

Cart item total rounding issue

When the gross price is set to true the subtotal of each cart item gets calculated like:

round($this->price / (1 + ($this->taxRate / 100)), $decimals) * $this->qty

This results in a incorrect rounding issue:

d0e8087272e0f6636ac411a4af8efe0e

The expected result of the total should be 4.00

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".

Weight option

Hi, I try to update laravel from 5.1 to 6.8 and update shopping cart
So now i have problem "Undefined index: weight"
We don't use the weight option, how can i remove this require?

Per product taxRate

Hey,

good work on the repo! Only just found this after forking the original repo myself and updating it to work with Laravel 5.8. I've currently also modified the package to be able to set per-product taxRates (which defaults back to config('cart.tax') in case there is non set. Only breaking change would be the Buyable interface which now requires a getBuyableTaxRate() method.

Seems a bit useless for me to have this in a separate repo if you feel like having this feature in this package as well. Feels better to bundle the powers :-)

Changes can be found here: https://github.com/PendoNL/LaravelShoppingcart

Let me know if this sounds interesting.

Cart::instance('default')->destroy() cleans cart when using form but not directly in controller

I use "bumbummen99/shoppingcart": "^2.8", with "laravel/framework": "5.8.*".
The problem is exactly what the title say
I have a form in my header like below:

<li class="list-group-item"> <form action="{{ route('cart.clean')}}" method="post"> {{ csrf_field() }} <button title="{{__('cart.cleantitle')}}" type="submit" class="btn btn-danger" value="Remove">{{__('cart.clean')}}</button> </form> </li> ---------------------------------------------``
which works as intended and delete cart products per will.

The cart.clean function is:

public function clean() { Cart::instance('default')->destroy(); $notification = array( 'message' => __('alerts.cartclean'), 'alert-type' => 'success' ); return redirect()->back()->with($notification); }

but in my checkoutcontroller i use:
Cart::instance('default')->destroy();
or Cart::destroy(); , i have tryed both

in order to clean the cart products after the order is submitted and sends the order email to the customer

Can someone help me with this??

Create test for erase()

The Cart::erase() method has no unit test affecting coverage and production security. The Cart::erase() method should have a unit test verifying its functionality.

To verify:

  • Create Cart with known instance id
  • Add item to Cart
  • Store Cart
  • Erase Cart
  • Restore Cart
  • Verify that Cart is empty.
  • (Optional) Verify that database is empty

How to split cartitem

i try to make coupons with select an cartitem in list but if i have 2 same of product, i can't split it to apply coupons. So how i can do it
My code:

            <thead>
                <tr>
                    <th class="product-remove">#</th>
                    <th class="product-image">Images</th>
                    <th class="t-product-name">Name</th>
                    <th class="product-unit-price">Price</th>
                    <th class="product-quantity">Quantity</th>
                    <th class="product-subtotal">Total</th>
                    <th class="product-remove">Discount</th>
                </tr>
            </thead>
            <tbody>
                @foreach (Cart::content() as $item)
                <tr>
                    <td class="product-remove">{{ $loop->iteration }}</td>
                    <td class="product-image">
                        <div class="cart-product-image">
                            <a href="single-product.html">
                                <img width="104px" height="104px" src="{{ asset('storage/books/'.$item->options->img) }}" alt="">
                            </a>
                        </div>
                    </td>
                    <td class="t-product-name">{{ $item->name }}</td>
                    <td class="product-unit-price"><span class="cart-price">{{ number_format($item->price*$item->qty,0,',','.') }} đ</span></td>
                    <td class="product-quantity">{{ $item->qty }}</td>
                    <td class="product-subtotal"><span class="total">{{ Cart::subtotal() }} đ</span></td>
                    <td class="product-remove">
                        <div class="btn-group btn-group-toggle" data-toggle="buttons">
                                <input type="radio" id='regular' name="optradio">
                        </div>
                    </td>
                </tr>
                @endforeach
            </tbody>
        </table>

Differentiate events for merge

This is more an enhancement than an issue.

I have listeners that listen to "add" event that do things that shouldn't be done on "merge" event, so what do you think about adding a "cart.merged' event on merge() instead of getting several "cart.added" ?

Of course there could be an option to select between dispatching a single "merge" event or several "add" events as now

Am I the only one with this kind of need ?

Cart::content() collection use groupBy on options

I have shopping cart witch is full of 6 products
each product has it's own id and options as second array
let me show a live example of what I have

"3cdf86cbbf156640791c6a07a0144866" => CartItem {#904 ▼
      +rowId: "3cdf86cbbf156640791c6a07a0144866"
      +id: 986
      +qty: "1"
      +name: "product name"
      +price: 278.0
      +weight: 0.0
      +options: CartItemOptions {#905 ▼
        #items: array:13 [▼
          "image" => "1570450974.jpg"
          "details" => "some details here"
          "supplier_id" => 27
          "tax" => "5.00"
          "product_campaign_id" => ""
          "purchases_limitations" => null
          "option_item_id" => null
          "option_item_name" => " "
          "flagCampaignLimitation" => false
          "itemQuy" => "1"
          "packageId" => 41
          "flagPriceChanged" => 0
          "flagProductDeleted" => 0
        ]
      }
      +taxRate: "5.00"
      -associatedModel: null
      -discountRate: 0
    } 

now I can group products by it's ids as shown in the documentation.
Cart::content()->groupBy('id');

but what if I need to group the cart by the supplier_id witch is landing in the second array

+options: CartItemOptions {#905 ▼
        #items: array:13 [▼
          "supplier_id" => 27
        ]
      }

I try something like this in my controller

$collection = Cart::content()->groupBy( 'id' )->transform( function ( $item, $k ) {
			return $item->groupBy( 'supplier_id' );
		} );

and the results was not all suppliers grouped togethr and form of the array becomes like this

986 => Collection {#1133 ▼
      #items: array:1 [▼
        "" => Collection {#1467 ▼
          #items: array:1 [▼
            0 => CartItem {#904 ▼
              +rowId: "3cdf86cbbf156640791c6a07a0144866"
              +id: 986
              +qty: "1"
              +name: "منتج للمورد الجديد اثنين من اثنين"
              +price: 278.0
              +weight: 0.0
              +options: CartItemOptions {#905 ▼
                #items: array:13 [▼
                  "image" => "1570450974.jpg"
                  "details" => "تفاصيل المنتج بالعربية"
                  "supplier_id" => 27
                  "tax" => "5.00"
                  "product_campaign_id" => ""
                  "purchases_limitations" => null
                  "option_item_id" => null
                  "option_item_name" => " "
                  "flagCampaignLimitation" => false
                  "itemQuy" => "1"
                  "packageId" => 41
                  "flagPriceChanged" => 0
                  "flagProductDeleted" => 0
                ]
              }
              +taxRate: "5.00"
              -associatedModel: null
              -discountRate: 0
            }
          ]
        }
      ]
    }

is there is any easier why to do groupBy on supplier_id witch is in second array options

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;

 }

Update stored cart

I just stumlbed in this need for a project I'm working on.
I need to update the stored cart. Could be real-time (on update) or not.

The main issue is the fact that the store function doesn't overwrite a stored cart, and the restore function (the only way to delete carts from db) can't restore carts on a different instance (compared to the one stored) so basically it overwrite the "session" one.

Is there an existing way to move a cart from one instance to another ?
I didn't find it and the only idea I had is to iterate through the cart items but I'm not sure it will works due to the instance switch needed ... still have to try

Any idea ?
Tnx

Fixed discounts feature

The current implementation of discounts is based on the rate (percentage). It would be nice to have the ability to set a fixed discount

Cant store to database

I have published the config and migration and i have change the connection and the table name.
This is my function to store

public function store($instance){
    Cart::instance($instance)->store('abcdefg');
}

This is my /config/cart.php

'database' => [

        'connection' => 'ppip_ai',

        'table' => 'shoppingcart',

    ],

and this is my migrations

public function up()
    {
        Schema::create(config('cart.database.table'), function (Blueprint $table) {
            $table->string('identifier', 50);
            $table->string('instance', 50);
            $table->longText('content');
            $table->timestamps();

            $table->primary('identifier');
        });
    }

and I got error like this

InvalidArgumentException thrown with message "Database [ppip_ai] not configured."

is there any chance to store my cart?

Is there a way to store cart in users cookie (~forever)?

Is there a way to store cart in user's cookies forever?
I don't want to store session forever or change session driver to cookie, but I want to store cart in cookies forever (or for a long time).
So, is there a way to change cart's storage to cookies?

Ability to use multiple discount/coupons?

Currently it seems like you can only have 1 discount per product. It would be cool to have the ability to have as many as you want (given they do not make the amount <= 0), so that you could say have a 10% site-wide discount with an additional 5% coupon code or something like that?

It would also be really neat to be able to get all information based on that discount. E.G

- id
- discount_percentage
- discount_amount (sub-total - discount_percentage)
- description (description of the discount)
- metadata (perhaps the code)

There are a few other cart packages that have something like this, such as; https://github.com/darryldecode/laravelshoppingcart

You can see they have this cartConditions array;

$coupon101 = new CartCondition(array(
            'name' => 'COUPON 101',
            'type' => 'coupon',
            'value' => '-5%',
        ));

Cart::addItemCondition($productID, $coupon101);

Hopefully this can be added!

Ability to calculate tax "Exclusively" from the gross sum.

Currently the tax in this package is calculated like this;

case 'tax':
                return round($this->priceTarget * ($this->taxRate / 100), $decimals);
            case 'priceTax':
                return round($this->priceTarget + $this->tax, $decimals);

But in the EU, it is common that prices are listed VAT inclusive and use a different formula. It's calculated like so;

VAT calculation formula for VAT exclusion is the following: to calculate VAT having the gross amount you should divide the gross amount by 1 + VAT percentage (i.e. if it is 15%, then you should divide by 1.15), then subtract the gross amount, multiply by -1 and round to the closest value (including eurocents). The last two operations are not mandatory since you see the VAT value even before you do them.

Source: https://vatcalconline.com/


To see the difference, let me give an example;

Product Price: 14.99
VAT: 21%

Current Method: 14.99 * (21 / 100) = 3.1479
EU Method: 14.99 - (14.99 / 1.21) = 2.6016

Is it possible we can get this as a configuration option whether we want it to be inclusive or exclusive? Since technically speaking anyone currently using this to calculate VAT isn't doing it correctly and there could be issues legally because of this.

Ability to add extra global costs?

In my use case I need to add shipping or transaction costs depending what payment method the customer chooses. So something like: Cart::setGlobalShippingCost($price), Cart::setGlobalTransactionCost($price) or Cart::setGlobalCost($name, $price).

Would be a neat additional feature!

Only 2 items in cart

Hey) Why in cart only 2 items maximum?
When i adding 3d item on product page it's ok i see 3 items, but when i go to the cart page or just update page the last item get lost.

I can't understand whats happened coz after i use only Cart::count() and subtotal to get new information.

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

Crinsane#344
and this PR
hardevine@3e4f5a1

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

vendor:publish migrations not working for me

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

didn't work

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

did.
Is it due to missing groups in 2nd publishes() method in ShoppingcartServiceProvider?

Thanks for the bundle

Cart::count() should not be called statically

I have Laravel 6.6.1 installed and the Count method gives me this error:
Non-static method Gloudemans\Shoppingcart\Cart::count() should not be called statically

I call it this way:
public function cuantos() { if (request()->ajax()){ return Cart::count(); } }

Before I had no error. Do you know how I can solve it?

Undefined index: weight

Hello,

I have a problem whan I want to add product on my cart. I have this error Undefined index: weight on /vendor/bumbummen99/shoppingcart/src/CartItem.php

`public static function fromArray(array $attributes)
{
$options = array_get($attributes, 'options', []);

    return new self($attributes['id'], $attributes['name'], $attributes['price'], $attributes['weight'], $options);
}`

This is my public function:
` public function addToCart() {
$product = Product::find(request()->product_id);

    $cartItem = Cart::add([
        'id' => $product->id,
        'name' => $product->name,
        'price' => $product->price,
        'quantity' => request()->quantity
    ]);

    Cart::associate($cartItem->rowId, 'App\Product');
    
    return redirect()->route('cart')->with('success', true)->with('message', 'Votre produit à bien été ajouté au panier');
}`

Do you have idea how to fix that?

Thanks

Cart::update() changes the rowId

Now am storing the shopping cart items to database as soon as the customer add the item to cart.
I use to add the rowId of this item to my table.
Now whenever I do Cart::update() it changes the rowId of the item in the current session, and of course the item in the shopping_carts table stay with the same old rowId.

my question how to get the new rowId as soon as I fire Cart::update() so I can update in my table with the new one.

since I try to debug the returned rowId after update but it return the old one, and when the page is fully loaded it return the new rowId

any help here please
thanks

CartItem priceTax is not correctly calculated

Probably is something inherited from the Crinsane version, but the 2 functions updateFromBuyable and updateFromArray calculate the priceTax as
$this->priceTax = $this->price + $this->tax;

while the __get function use (as it should be) the discounted one
$this->priceTarget + $this->tax;

Requesting the priceTax from the code never go through the __get() function as the 2 methods (updateFromBuyable, updateFromArray) already added a dynamic property to the object

If there are no objections I'm going to remove the row
$this->priceTax = $this->price + $this->tax;
from both, so the method (and the "discounted" one) will be used

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);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.