mirror of
https://github.com/wearepixel/laravel-cart.git
synced 2026-11-14 13:51:44 +00:00
init commit :rocket
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
target-branch: "develop"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
- package-ecosystem: "composer"
|
||||
directory: "/"
|
||||
target-branch: "develop"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
labels:
|
||||
- "dependencies"
|
||||
@@ -0,0 +1,32 @@
|
||||
name: dependabot-auto-merge
|
||||
on: pull_request_target
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
steps:
|
||||
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v2.2.0
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
- name: Auto-merge Dependabot PRs for semver-minor updates
|
||||
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}
|
||||
run: gh pr merge --auto --merge "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
||||
- name: Auto-merge Dependabot PRs for semver-patch updates
|
||||
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
|
||||
run: gh pr merge --auto --merge "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
@@ -0,0 +1,25 @@
|
||||
name: PHP Formatting
|
||||
|
||||
on: pull_request
|
||||
|
||||
jobs:
|
||||
php-formatting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Run Pint"
|
||||
uses: aglipanci/laravel-pint-action@latest
|
||||
with:
|
||||
verboseMode: true
|
||||
configPath: "pint.json"
|
||||
pintVersion: 1.17.2
|
||||
|
||||
- name: Commit changes
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "Apply Laravel Pint"
|
||||
commit_options: '--no-verify --signoff'
|
||||
file_pattern: "*.php"
|
||||
commit_user_name: "Pixel Tech"
|
||||
commit_user_email: "tech@wearepixel.com.au"
|
||||
commit_author: "Pixel Tech<tech@wearepixel.com.au>"
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master, develop]
|
||||
pull_request:
|
||||
branches: [master, develop]
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Tests
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
php: ['8.1','8.2', '8.3']
|
||||
stability: [prefer-stable]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, gd, redis, memcached
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Install dependencies
|
||||
uses: nick-fields/retry@v3
|
||||
with:
|
||||
timeout_minutes: 5
|
||||
max_attempts: 5
|
||||
command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress ${{ matrix.flags }}
|
||||
|
||||
|
||||
- name: Execute tests
|
||||
run: vendor/bin/pest
|
||||
@@ -0,0 +1,5 @@
|
||||
/vendor
|
||||
.DS_Store
|
||||
.idea
|
||||
.phpunit.result.cache
|
||||
READMETODO.md
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Pixel <hello@wearepixel.com.au>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,491 @@
|
||||
[](https://packagist.org/packages/wearepixel/laravel-cart)
|
||||
[](https://github.com/wearepixel/laravel-cart/actions?query=workflow%3ATests+branch%3Amaster)
|
||||
[](https://packagist.org/packages/wearepixel/laravel-cart)
|
||||
[](https://packagist.org/packages/wearepixel/laravel-cart)
|
||||
|
||||
A Cart Implementation for Laravel.
|
||||
|
||||
Supported Laravel Versions: 9, 10, 11
|
||||
|
||||
## Quick Demo
|
||||
|
||||
```php
|
||||
// Add an item to the cart
|
||||
\Cart::add(1, 'Product 1', 19.99, 2, ['size' => 'large']);
|
||||
|
||||
// Get the cart contents
|
||||
$cartContents = \Cart::getContent();
|
||||
|
||||
// Update item quantity by its id
|
||||
\Cart::update(1, ['quantity' => 3]);
|
||||
|
||||
// Remove an item from the cart by its id
|
||||
\Cart::remove(1);
|
||||
|
||||
// Get cart total
|
||||
\$total = Cart::getTotal();
|
||||
|
||||
// Clear the entire cart
|
||||
\Cart::clear();
|
||||
```
|
||||
|
||||
## Installing
|
||||
|
||||
Install the package through [Composer](http://getcomposer.org/).
|
||||
|
||||
`composer require "wearepixel/laravel-cart"`
|
||||
|
||||
## Configuration
|
||||
|
||||
You can publish the configuration file to customize various options.
|
||||
|
||||
```php
|
||||
php artisan vendor:publish --provider="Wearepixel\Cart\CartServiceProvider" --tag="config"
|
||||
```
|
||||
|
||||
## HOW TO USE
|
||||
|
||||
- [Usage](#usage)
|
||||
- [Conditions](#conditions)
|
||||
- [Items](#items)
|
||||
- [Associating Models](#associating-models)
|
||||
- [Instances](#instances)
|
||||
- [Exceptions](#exceptions)
|
||||
- [Events](#events)
|
||||
- [Format Response](#format)
|
||||
- [Examples](#examples)
|
||||
- [Using Different Storage](#storage)
|
||||
- [License](#license)
|
||||
|
||||
## Usage
|
||||
|
||||
The cart has a default sessionKey that holds the cart data and stores it in the session.
|
||||
|
||||
This
|
||||
also serves as a cart unique identifier which you can use to bind a cart to a specific user if you want to.
|
||||
|
||||
Make sure to call `\Cart::session($sessionKey)` before calling any other cart methods.
|
||||
|
||||
Usually this is not required.
|
||||
|
||||
```php
|
||||
// Binds the cart to a unique id (user id, session id, etc.)
|
||||
\Cart::session(User::first()->id);
|
||||
|
||||
// If you don't want to bind the cart on a project level, you can add it to each call
|
||||
\Cart::session(User::first()->id)->add(1, 'Product 1', 19.99, 2, ['size' => 'large']);
|
||||
```
|
||||
|
||||
### Adding to the cart: **Cart::add()**
|
||||
|
||||
There are a few ways to add items to the cart.
|
||||
|
||||
```php
|
||||
// Add a simple product to the cart
|
||||
Cart::add(
|
||||
455, # product id
|
||||
'Sample Item', # product name
|
||||
100.99, # product price
|
||||
2, # quantity
|
||||
[] # optional attributes
|
||||
);
|
||||
|
||||
// array format
|
||||
Cart::add([
|
||||
456, # product id
|
||||
'Leather Shoes', # product name
|
||||
187, # product price
|
||||
1, # quantity
|
||||
[] # optional attributes
|
||||
]);
|
||||
|
||||
|
||||
// add an item with attributes
|
||||
Cart::add([
|
||||
457, // product id
|
||||
'T-Shirt', // product name
|
||||
29.99, // product price
|
||||
1, // quantity
|
||||
[
|
||||
'size' => 'L',
|
||||
'color' => 'Blue'
|
||||
] // attributes
|
||||
]);
|
||||
|
||||
// add an item with conditions
|
||||
Cart::add([
|
||||
458, // product id
|
||||
'Headphones', // product name
|
||||
199.99, // product price
|
||||
1, // quantity
|
||||
[], // attributes
|
||||
[
|
||||
[
|
||||
'name' => '10% Off',
|
||||
'type' => 'discount',
|
||||
'value' => '-10%'
|
||||
]
|
||||
] // conditions
|
||||
]);
|
||||
|
||||
// add multiple items at one time
|
||||
Cart::add(
|
||||
[
|
||||
456, # product id
|
||||
'Leather Shoes', # product name
|
||||
187, # product price
|
||||
1, # quantity
|
||||
[] # optional attributes
|
||||
],
|
||||
[
|
||||
431, # product id
|
||||
'Leather Jacket', # product name
|
||||
254.50, # product price
|
||||
1, # quantity
|
||||
[] # optional attributes
|
||||
]
|
||||
);
|
||||
```
|
||||
|
||||
### Updating an item on a cart: **Cart::update()**
|
||||
|
||||
```php
|
||||
Cart::update(
|
||||
456, # product id
|
||||
[
|
||||
'name' => 'New Item Name', // new item name
|
||||
'price' => 98.67, // new item price as a float or string
|
||||
]
|
||||
);
|
||||
|
||||
// updating a product's quantity
|
||||
Cart::update(
|
||||
456, # product id
|
||||
[
|
||||
'quantity' => 2, // by default adding the quantity (so if from 4 to 6)
|
||||
]
|
||||
);
|
||||
|
||||
// reducing it...
|
||||
Cart::update(
|
||||
456,
|
||||
[
|
||||
'quantity' => -1, // so if from 4 to 3
|
||||
]
|
||||
);
|
||||
|
||||
// you can replace the quantity by setting relative to false
|
||||
Cart::update(
|
||||
456, # product id
|
||||
[
|
||||
'quantity' => [
|
||||
'relative' => false,
|
||||
'value' => 5 // if the quantity was 2, it will now be 5
|
||||
],
|
||||
]
|
||||
);
|
||||
```
|
||||
|
||||
### Removing an item on a cart: **Cart::remove()**
|
||||
|
||||
```php
|
||||
// Remove an item from the cart by its id
|
||||
Cart::remove(456);
|
||||
```
|
||||
|
||||
### Getting an item on a cart: **Cart::get()**
|
||||
|
||||
```php
|
||||
// Get an item from the cart by its id
|
||||
Cart::get(456);
|
||||
|
||||
// You can also get the total price of the item
|
||||
$summedPrice = Cart::get($itemId)->getPriceSum();
|
||||
```
|
||||
|
||||
### Getting the cart content: **Cart::getContent()**
|
||||
|
||||
```php
|
||||
// Returns a collection of the cart's contents
|
||||
$cartData = Cart::getContent();
|
||||
|
||||
// Gets the total number of items (not quantity) in the cart
|
||||
$cartCollection->count();
|
||||
|
||||
// Transform the collection to an array or a JSON
|
||||
$cartCollection->toArray();
|
||||
$cartCollection->toJson();
|
||||
```
|
||||
|
||||
Get cart total quantity: **Cart::getTotalQuantity()**
|
||||
|
||||
```php
|
||||
$cartTotalQuantity = Cart::getTotalQuantity();
|
||||
```
|
||||
|
||||
### Cart subtotal: **Cart::getSubTotal()**
|
||||
|
||||
```php
|
||||
$subTotal = Cart::getSubTotal();
|
||||
```
|
||||
|
||||
### Cart subtotal without conditions: **Cart::getSubTotalWithoutConditions()**
|
||||
|
||||
```php
|
||||
$subTotalWithoutConditions = Cart::getSubTotalWithoutConditions();
|
||||
```
|
||||
|
||||
Cart Total: **Cart::getTotal()**
|
||||
|
||||
```php
|
||||
$total = Cart::getTotal();
|
||||
```
|
||||
|
||||
### Check if cart is empty: **Cart::isEmpty()**
|
||||
|
||||
```php
|
||||
Cart::isEmpty();
|
||||
```
|
||||
|
||||
### Clearing the Cart: **Cart::clear()**
|
||||
|
||||
```php
|
||||
Cart::clear();
|
||||
```
|
||||
|
||||
## Conditions
|
||||
|
||||
Conditions are very useful in terms for adding things like discounts, taxes, shipping costs, etc.
|
||||
|
||||
Conditions can be added on the entire cart or on individual items.
|
||||
|
||||
Conditions on a cart level should always have a `target` of `subtotal` or `total`. This tells the cart which value to apply the condition to.
|
||||
|
||||
You can also provide an `order` to cart conditions which tells the cart in what order to apply the conditions. Item level conditions do not support the `order` parameter.
|
||||
|
||||
### Conditions on the cart
|
||||
|
||||
#### Adding a condition to the cart: **Cart::condition()**
|
||||
|
||||
```php
|
||||
// Add a single condition to the cart
|
||||
$condition = new \Wearepixel\Cart\CartCondition([
|
||||
'name' => 'Tax: 10%',
|
||||
'type' => 'tax',
|
||||
'target' => 'subtotal', // this condition will be applied to cart's subtotal when getSubTotal() is called.
|
||||
'value' => '10%',
|
||||
'attributes' => [ // add extra attributes here
|
||||
'description' => 'Compulsory tax',
|
||||
]
|
||||
]);
|
||||
|
||||
Cart::condition($condition);
|
||||
|
||||
// Add multiple conditions
|
||||
$tax = new \Wearepixel\Cart\CartCondition([
|
||||
'name' => 'Tax: 10%',
|
||||
'type' => 'tax',
|
||||
'target' => 'subtotal', // this condition will be applied to cart's subtotal when getSubTotal() is called.
|
||||
'value' => '10%',
|
||||
'order' => 2
|
||||
]);
|
||||
|
||||
$shipping = new \Wearepixel\Cart\CartCondition([
|
||||
'name' => 'Shipping: $15',
|
||||
'type' => 'shipping',
|
||||
'target' => 'subtotal', // this condition will be applied to cart's subtotal when getSubTotal() is called.
|
||||
'value' => '+15',
|
||||
'order' => 1
|
||||
]);
|
||||
|
||||
Cart::condition($tax);
|
||||
Cart::condition($shipping);
|
||||
|
||||
// or as an array
|
||||
Cart::condition([$tax, $shipping]);
|
||||
|
||||
// add condition to only apply on totals, not in subtotal
|
||||
$shipping = new \Wearepixel\Cart\CartCondition([
|
||||
'name' => 'Express Shipping $15',
|
||||
'type' => 'shipping',
|
||||
'target' => 'total',
|
||||
'value' => '+15',
|
||||
'order' => 1
|
||||
]);
|
||||
|
||||
Cart::condition($shipping);
|
||||
```
|
||||
|
||||
#### Getting conditions on the cart: **Cart::getConditions()**
|
||||
|
||||
```php
|
||||
// To get all applied conditions on a cart, use below:
|
||||
$cartConditions = Cart::getConditions();
|
||||
|
||||
foreach($cartConditions as $condition)
|
||||
{
|
||||
$condition->getTarget(); // the target of which the condition was applied
|
||||
$condition->getName(); // the name of the condition
|
||||
$condition->getType(); // the type
|
||||
$condition->getValue(); // the value of the condition
|
||||
$condition->getOrder(); // the order of the condition
|
||||
$condition->getAttributes(); // the attributes of the condition, returns an empty [] if no attributes added
|
||||
}
|
||||
|
||||
// You can also get a condition that has been applied on the cart by using its name:
|
||||
$condition = Cart::getCondition('Tax: 10%');
|
||||
$condition->getTarget(); // the target of which the condition was applied
|
||||
$condition->getName(); // the name of the condition
|
||||
$condition->getType(); // the type
|
||||
$condition->getValue(); // the value of the condition
|
||||
$condition->getAttributes(); // the attributes of the condition, returns an empty [] if no attributes added
|
||||
```
|
||||
|
||||
### Calculating condition value: **Cart::getCondition('Tax: 10%')->getCalculatedValue($subTotal)**
|
||||
|
||||
```php
|
||||
$subTotal = Cart::getSubTotal();
|
||||
$condition = Cart::getCondition('VAT 12.5%');
|
||||
$conditionCalculatedValue = $condition->getCalculatedValue($subTotal);
|
||||
```
|
||||
|
||||
### Condition on items
|
||||
|
||||
Item conditions are useful if you have discounts to be applied specifically on an item and not on the whole cart value.
|
||||
|
||||
```php
|
||||
|
||||
// lets create first our condition instance
|
||||
$saleCondition = new \Wearepixel\Cart\CartCondition([
|
||||
'name' => '50% Off',
|
||||
'type' => 'tax',
|
||||
'value' => '-50%',
|
||||
]);
|
||||
|
||||
// Create the product data with the condition
|
||||
$product = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => $saleCondition
|
||||
];
|
||||
|
||||
// Now add the product to the cart
|
||||
Cart::add($product);
|
||||
|
||||
// You can of course also do multiple conditions on an item
|
||||
$saleCondition = new \Wearepixel\Cart\CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
|
||||
$discountCode = new CartCondition([
|
||||
'name' => 'Discount Code',
|
||||
'type' => 'promo',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => [$saleCondition, $discountCode]
|
||||
];
|
||||
|
||||
Cart::add($item);
|
||||
```
|
||||
|
||||
> NOTE: All cart per-item conditions should be added before calling **Cart::getSubTotal()**
|
||||
|
||||
Then Finally you can call **Cart::getSubTotal()** to get the Cart sub total with the applied conditions on each of the items.
|
||||
|
||||
```php
|
||||
// the subtotal will be calculated based on the conditions added that has target => "subtotal"
|
||||
// and also conditions that are added on per item
|
||||
$cartSubTotal = Cart::getSubTotal();
|
||||
```
|
||||
|
||||
#### Add condition to existing Item on the cart: **Cart::addItemCondition($productId, $itemCondition)**
|
||||
|
||||
Adding Condition to an existing Item on the cart is simple as well.
|
||||
|
||||
```php
|
||||
$condition = new CartCondition([
|
||||
'name' => 'COUPON 101',
|
||||
'type' => 'coupon',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
|
||||
Cart::addItemCondition(456, $condition);
|
||||
```
|
||||
|
||||
### Clearing Cart Conditions: **Cart::clearCartConditions()**
|
||||
|
||||
```php
|
||||
Cart::clearCartConditions()
|
||||
```
|
||||
|
||||
### Remove a specific cart condtion: **Cart::removeCartCondition($conditionName)**
|
||||
|
||||
```php
|
||||
Cart::removeCartCondition('Summer Sale 5%')
|
||||
```
|
||||
|
||||
### Remove a specific item condition: **Cart::removeItemCondition($itemId, $conditionName)**
|
||||
|
||||
```php
|
||||
Cart::removeItemCondition(456, 'SALE 5%')
|
||||
```
|
||||
|
||||
### Clear all item conditions: **Cart::clearItemConditions($itemId)**
|
||||
|
||||
```php
|
||||
Cart::clearItemConditions(456)
|
||||
```
|
||||
|
||||
### Get conditions by type: **Cart::getConditionsByType($type)**
|
||||
|
||||
This returns all conditions that has been added to the cart by the type specified.
|
||||
|
||||
```php
|
||||
$tax = Cart::getConditionsByType('tax');
|
||||
```
|
||||
|
||||
### Remove conditions by type: **Cart::removeConditionsByType($type)**
|
||||
|
||||
```php
|
||||
Cart::removeConditionsByType('tax');
|
||||
```
|
||||
|
||||
## Cart Items
|
||||
|
||||
The method **Cart::getContent()** returns a collection of items.
|
||||
|
||||
Apart from the above methods, you can also get the price of an item with or without **item level conditions** applied.
|
||||
|
||||
These methods do not apply cart level conditions.
|
||||
|
||||
```php
|
||||
// With no conditions, just the price * quantity
|
||||
$item->getPriceSum();
|
||||
|
||||
// With conditions applied get the price of a single quantity
|
||||
$item->getPriceWithConditions();
|
||||
|
||||
// Get the sum with conditions applied
|
||||
$item->getPriceSumWithConditions();
|
||||
|
||||
// Without conditions applied
|
||||
$item->getPriceSumWithConditions();
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
Huge credits to the original maintainer of the package, located at [darryldecode/laravelshoppingcart](https://github.com/darryldecode/laravelshoppingcart)
|
||||
|
||||
We have forked this package to update it to Laravel 11 and to add some features and support for newer PHP versions.
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "wearepixel/laravel-cart",
|
||||
"version": "1.0.0",
|
||||
"description": "A cart implementation for Laravel",
|
||||
"keywords": [
|
||||
"laravel",
|
||||
"shopping cart",
|
||||
"cart"
|
||||
],
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Darryl Fernandez",
|
||||
"email": "engrdarrylfernandez@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Joel Male",
|
||||
"email": "joel@wearepixel.com.au"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"illuminate/support": "^9.0|^10.0|^11.0",
|
||||
"illuminate/validation": "^9.0|^10.0|^11.0",
|
||||
"illuminate/translation": "^9.0|^10.0|^11.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.4.2",
|
||||
"laravel/pint": "dev-main",
|
||||
"pestphp/pest": "2.34.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Wearepixel\\Cart\\": "src/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Wearepixel\\Cart\\CartServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Cart": "Wearepixel\\Cart\\Facades\\CartFacade"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"pestphp/pest-plugin": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+5647
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* formatting
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* the formatting of shopping cart values
|
||||
*/
|
||||
'format_numbers' => env('SHOPPING_FORMAT_VALUES', false),
|
||||
|
||||
'decimals' => env('SHOPPING_DECIMALS', 0),
|
||||
|
||||
'dec_point' => env('SHOPPING_DEC_POINT', '.'),
|
||||
|
||||
'thousands_sep' => env('SHOPPING_THOUSANDS_SEP', ','),
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* persistence
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* the configuration for persisting cart
|
||||
*/
|
||||
'storage' => null,
|
||||
|
||||
/*
|
||||
* ---------------------------------------------------------------
|
||||
* events
|
||||
* ---------------------------------------------------------------
|
||||
*
|
||||
* the configuration for cart events
|
||||
*/
|
||||
'events' => null,
|
||||
];
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory suffix=".php">./src</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"preset": "laravel",
|
||||
"rules": {
|
||||
"concat_space": {
|
||||
"spacing": "one"
|
||||
},
|
||||
"class_attributes_separation": false,
|
||||
"ordered_imports": {
|
||||
"sort_algorithm": "length"
|
||||
}
|
||||
}
|
||||
}
|
||||
+856
@@ -0,0 +1,856 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart;
|
||||
|
||||
use Wearepixel\Cart\Helpers\Helpers;
|
||||
use Wearepixel\Cart\Validators\CartItemValidator;
|
||||
use Wearepixel\Cart\Exceptions\InvalidItemException;
|
||||
use Wearepixel\Cart\Exceptions\UnknownModelException;
|
||||
use Wearepixel\Cart\Exceptions\InvalidConditionException;
|
||||
|
||||
class Cart
|
||||
{
|
||||
/**
|
||||
* the item storage
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* the event dispatcher
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* the cart session key
|
||||
*/
|
||||
protected $instanceName;
|
||||
|
||||
/**
|
||||
* the session key use for the cart
|
||||
*/
|
||||
protected $sessionKey;
|
||||
|
||||
/**
|
||||
* the session key use to persist cart items
|
||||
*/
|
||||
protected $sessionKeyCartItems;
|
||||
|
||||
/**
|
||||
* the session key use to persist cart conditions
|
||||
*/
|
||||
protected $sessionKeyCartConditions;
|
||||
|
||||
/**
|
||||
* Configuration to pass to ItemCollection
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* This holds the currently added item id in cart for association
|
||||
*/
|
||||
protected $currentItemId;
|
||||
|
||||
/**
|
||||
* The number of decimals to use when formatting prices
|
||||
*/
|
||||
protected $decimals;
|
||||
|
||||
/**
|
||||
* The decimal point character
|
||||
*/
|
||||
protected $decPoint;
|
||||
|
||||
/**
|
||||
* our object constructor
|
||||
*/
|
||||
public function __construct($session, $events, $instanceName, $session_key, $config)
|
||||
{
|
||||
$this->events = $events;
|
||||
$this->session = $session;
|
||||
$this->instanceName = $instanceName;
|
||||
$this->sessionKey = $session_key;
|
||||
$this->sessionKeyCartItems = $this->sessionKey . '_cart_items';
|
||||
$this->sessionKeyCartConditions = $this->sessionKey . '_cart_conditions';
|
||||
$this->config = $config;
|
||||
$this->currentItemId = null;
|
||||
$this->fireEvent('created');
|
||||
}
|
||||
|
||||
/**
|
||||
* sets the session key
|
||||
*
|
||||
* @param string $sessionKey the session key or identifier
|
||||
* @return $this|bool
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function session($sessionKey)
|
||||
{
|
||||
if (! $sessionKey) {
|
||||
throw new \Exception('Session key is required.');
|
||||
}
|
||||
|
||||
$this->sessionKey = $sessionKey;
|
||||
$this->sessionKeyCartItems = $this->sessionKey . '_cart_items';
|
||||
$this->sessionKeyCartConditions = $this->sessionKey . '_cart_conditions';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* get instance name of the cart
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getInstanceName()
|
||||
{
|
||||
return $this->instanceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* get an item on a cart by item ID
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($itemId)
|
||||
{
|
||||
return $this->getContent()->get($itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if an item exists by item ID
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($itemId)
|
||||
{
|
||||
return $this->getContent()->has($itemId);
|
||||
}
|
||||
|
||||
/**
|
||||
* add item to the cart, it can be an array or multi dimensional array
|
||||
*
|
||||
* @param string|array $id
|
||||
* @param string $name
|
||||
* @param float $price
|
||||
* @param int $quantity
|
||||
* @param array $attributes
|
||||
* @param CartCondition|array $conditions
|
||||
* @param string $associatedModel
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidItemException
|
||||
*/
|
||||
public function add($id, $name = null, $price = null, $quantity = null, $attributes = [], $conditions = [], $associatedModel = null)
|
||||
{
|
||||
// if the first argument is an array,
|
||||
// we will need to call add again
|
||||
if (is_array($id)) {
|
||||
// the first argument is an array, now we will need to check if it is a multi dimensional
|
||||
// array, if so, we will iterate through each item and call add again
|
||||
if (Helpers::isMultiArray($id)) {
|
||||
foreach ($id as $item) {
|
||||
$this->add(
|
||||
$item['id'],
|
||||
$item['name'],
|
||||
$item['price'],
|
||||
$item['quantity'],
|
||||
Helpers::issetAndHasValueOrAssignDefault($item['attributes'], []),
|
||||
Helpers::issetAndHasValueOrAssignDefault($item['conditions'], []),
|
||||
Helpers::issetAndHasValueOrAssignDefault($item['associatedModel'], null)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$this->add(
|
||||
$id['id'],
|
||||
$id['name'],
|
||||
$id['price'],
|
||||
$id['quantity'],
|
||||
Helpers::issetAndHasValueOrAssignDefault($id['attributes'], []),
|
||||
Helpers::issetAndHasValueOrAssignDefault($id['conditions'], []),
|
||||
Helpers::issetAndHasValueOrAssignDefault($id['associatedModel'], null)
|
||||
);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'name' => $name,
|
||||
'price' => Helpers::normalizePrice($price),
|
||||
'quantity' => $quantity,
|
||||
'attributes' => new ItemAttributeCollection($attributes),
|
||||
'conditions' => $conditions,
|
||||
];
|
||||
|
||||
if (isset($associatedModel) && $associatedModel != '') {
|
||||
$data['associatedModel'] = $associatedModel;
|
||||
}
|
||||
|
||||
// validate data
|
||||
$item = $this->validate($data);
|
||||
|
||||
// get the cart
|
||||
$cart = $this->getContent();
|
||||
|
||||
// if the item is already in the cart we will just update it
|
||||
if ($cart->has($id)) {
|
||||
|
||||
$this->update($id, $item);
|
||||
} else {
|
||||
|
||||
$this->addRow($id, $item);
|
||||
}
|
||||
|
||||
$this->currentItemId = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* update a cart
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* the $data will be an associative array, you don't need to pass all the data, only the key value
|
||||
* of the item you want to update on it
|
||||
* @return bool
|
||||
*/
|
||||
public function update($id, $data)
|
||||
{
|
||||
if ($this->fireEvent('updating', $data) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cart = $this->getContent();
|
||||
|
||||
$item = $cart->pull($id);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
// if the key is currently "quantity" we will need to check if an arithmetic
|
||||
// symbol is present so we can decide if the update of quantity is being added
|
||||
// or being reduced.
|
||||
if ($key == 'quantity') {
|
||||
// we will check if quantity value provided is array,
|
||||
// if it is, we will need to check if a key "relative" is set
|
||||
// and we will evaluate its value if true or false,
|
||||
// this tells us how to treat the quantity value if it should be updated
|
||||
// relatively to its current quantity value or just totally replace the value
|
||||
if (is_array($value)) {
|
||||
if (isset($value['relative'])) {
|
||||
if ((bool) $value['relative']) {
|
||||
$item = $this->updateQuantityRelative($item, $key, $value['value']);
|
||||
} else {
|
||||
$item = $this->updateQuantityNotRelative($item, $key, $value['value']);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$item = $this->updateQuantityRelative($item, $key, $value);
|
||||
}
|
||||
} elseif ($key == 'attributes') {
|
||||
$item[$key] = new ItemAttributeCollection($value);
|
||||
} else {
|
||||
$item[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$cart->put($id, $item);
|
||||
|
||||
$this->save($cart);
|
||||
|
||||
$this->fireEvent('updated', $item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the config
|
||||
*
|
||||
* @param array $config
|
||||
* @return void
|
||||
*/
|
||||
public function setConfig($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* add condition on an existing item on the cart
|
||||
*
|
||||
* @param int|string $productId
|
||||
* @param CartCondition $itemCondition
|
||||
* @return $this
|
||||
*/
|
||||
public function addItemCondition($productId, $itemCondition)
|
||||
{
|
||||
if ($product = $this->get($productId)) {
|
||||
$conditionInstance = '\\Wearepixel\\Cart\\CartCondition';
|
||||
|
||||
if ($itemCondition instanceof $conditionInstance) {
|
||||
// we need to copy first to a temporary variable to hold the conditions
|
||||
// to avoid hitting this error "Indirect modification of overloaded element of Wearepixel\Cart\ItemCollection has no effect"
|
||||
// this is due to laravel Collection instance that implements Array Access
|
||||
// // see link for more info: http://stackoverflow.com/questions/20053269/indirect-modification-of-overloaded-element-of-splfixedarray-has-no-effect
|
||||
$itemConditionTempHolder = $product['conditions'];
|
||||
|
||||
if (is_array($itemConditionTempHolder)) {
|
||||
array_push($itemConditionTempHolder, $itemCondition);
|
||||
} else {
|
||||
$itemConditionTempHolder = $itemCondition;
|
||||
}
|
||||
|
||||
$this->update($productId, [
|
||||
'conditions' => $itemConditionTempHolder, // the newly updated conditions
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* removes an item on cart by item ID
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function remove($id)
|
||||
{
|
||||
$cart = $this->getContent();
|
||||
|
||||
if ($this->fireEvent('removing', $id) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cart->forget($id);
|
||||
|
||||
$this->save($cart);
|
||||
|
||||
$this->fireEvent('removed', $id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* clear cart
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
if ($this->fireEvent('clearing') === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->session->put(
|
||||
$this->sessionKeyCartItems,
|
||||
[]
|
||||
);
|
||||
|
||||
$this->fireEvent('cleared');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* add a condition on the cart
|
||||
*
|
||||
* @param CartCondition|array $condition
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidConditionException
|
||||
*/
|
||||
public function condition($condition)
|
||||
{
|
||||
if (is_array($condition)) {
|
||||
foreach ($condition as $c) {
|
||||
$this->condition($c);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
if (! $condition instanceof CartCondition) {
|
||||
throw new InvalidConditionException('Argument 1 must be an instance of \'Wearepixel\Cart\CartCondition\'');
|
||||
}
|
||||
|
||||
$conditions = $this->getConditions();
|
||||
|
||||
// Check if order has been applied
|
||||
if ($condition->getOrder() == 0) {
|
||||
$last = $conditions->last();
|
||||
$condition->setOrder(! is_null($last) ? $last->getOrder() + 1 : 1);
|
||||
}
|
||||
|
||||
$conditions->put($condition->getName(), $condition);
|
||||
|
||||
$conditions = $conditions->sortBy(function ($condition, $key) {
|
||||
return $condition->getOrder();
|
||||
});
|
||||
|
||||
$this->saveConditions($conditions);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* get conditions applied on the cart
|
||||
*
|
||||
* @return CartConditionCollection
|
||||
*/
|
||||
public function getConditions()
|
||||
{
|
||||
return new CartConditionCollection($this->session->get($this->sessionKeyCartConditions));
|
||||
}
|
||||
|
||||
/**
|
||||
* get condition applied on the cart by its name
|
||||
*
|
||||
* @return CartCondition
|
||||
*/
|
||||
public function getCondition($conditionName)
|
||||
{
|
||||
return $this->getConditions()->get($conditionName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the condition filtered by Type
|
||||
* Please Note that this will only return condition added on cart bases, not those conditions added
|
||||
* specifically on an per item bases
|
||||
*
|
||||
* @return CartConditionCollection
|
||||
*/
|
||||
public function getConditionsByType($type)
|
||||
{
|
||||
return $this->getConditions()->filter(function (CartCondition $condition) use ($type) {
|
||||
return $condition->getType() == $type;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all the condition with the $type specified
|
||||
* Please Note that this will only remove condition added on cart bases, not those conditions added
|
||||
* specifically on an per item bases
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeConditionsByType($type)
|
||||
{
|
||||
$this->getConditionsByType($type)->each(function ($condition) {
|
||||
$this->removeCartCondition($condition->getName());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* removes a condition on a cart by condition name,
|
||||
* this can only remove conditions that are added on cart bases not conditions that are added on an item/product.
|
||||
* If you wish to remove a condition that has been added for a specific item/product, you may
|
||||
* use the removeItemCondition(itemId, conditionName) method instead.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeCartCondition($conditionName)
|
||||
{
|
||||
$conditions = $this->getConditions();
|
||||
|
||||
$conditions->pull($conditionName);
|
||||
|
||||
$this->saveConditions($conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a condition that has been applied on an item that is already on the cart
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function removeItemCondition($itemId, $conditionName)
|
||||
{
|
||||
if (! $item = $this->getContent()->get($itemId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->itemHasConditions($item)) {
|
||||
// NOTE:
|
||||
// we do it this way, we get first conditions and store
|
||||
// it in a temp variable $originalConditions, then we will modify the array there
|
||||
// and after modification we will store it again on $item['conditions']
|
||||
// This is because of ArrayAccess implementation
|
||||
// see link for more info: http://stackoverflow.com/questions/20053269/indirect-modification-of-overloaded-element-of-splfixedarray-has-no-effect
|
||||
|
||||
$tempConditionsHolder = $item['conditions'];
|
||||
|
||||
// if the item's conditions is in array format
|
||||
// we will iterate through all of it and check if the name matches
|
||||
// to the given name the user wants to remove, if so, remove it
|
||||
if (is_array($tempConditionsHolder)) {
|
||||
foreach ($tempConditionsHolder as $k => $condition) {
|
||||
if ($condition->getName() == $conditionName) {
|
||||
unset($tempConditionsHolder[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
$item['conditions'] = $tempConditionsHolder;
|
||||
}
|
||||
|
||||
// if the item condition is not an array, we will check if it is
|
||||
// an instance of a Condition, if so, we will check if the name matches
|
||||
// on the given condition name the user wants to remove, if so,
|
||||
// lets just make $item['conditions'] an empty array as there's just 1 condition on it anyway
|
||||
else {
|
||||
$conditionInstance = 'Wearepixel\\Cart\\CartCondition';
|
||||
|
||||
if ($item['conditions'] instanceof $conditionInstance) {
|
||||
if ($tempConditionsHolder->getName() == $conditionName) {
|
||||
$item['conditions'] = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->update($itemId, [
|
||||
'conditions' => $item['conditions'],
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove all conditions that has been applied on an item that is already on the cart
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clearItemConditions($itemId)
|
||||
{
|
||||
if (! $item = $this->getContent()->get($itemId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->update($itemId, [
|
||||
'conditions' => [],
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* clears all conditions on a cart,
|
||||
* this does not remove conditions that has been added specifically to an item/product.
|
||||
* If you wish to remove a specific condition to a product, you may use the method: removeItemCondition($itemId, $conditionName)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function clearCartConditions()
|
||||
{
|
||||
$this->session->put(
|
||||
$this->sessionKeyCartConditions,
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* get cart sub total without conditions
|
||||
*
|
||||
* @param bool $formatted
|
||||
* @return float
|
||||
*/
|
||||
public function getSubTotalWithoutConditions($formatted = true)
|
||||
{
|
||||
$cart = $this->getContent();
|
||||
|
||||
$sum = $cart->sum(function ($item) {
|
||||
return $item->getPriceSum();
|
||||
});
|
||||
|
||||
return Helpers::formatValue(floatval($sum), $formatted, $this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* get cart sub total
|
||||
*
|
||||
* @param bool $formatted
|
||||
* @return float
|
||||
*/
|
||||
public function getSubTotal($formatted = true)
|
||||
{
|
||||
$cart = $this->getContent();
|
||||
|
||||
$sum = $cart->sum(function (ItemCollection $item) {
|
||||
return $item->getPriceSumWithConditions(false);
|
||||
});
|
||||
|
||||
// get the conditions that are meant to be applied
|
||||
// on the subtotal and apply it here before returning the subtotal
|
||||
$conditions = $this
|
||||
->getConditions()
|
||||
->filter(function (CartCondition $cond) {
|
||||
return $cond->getTarget() === 'subtotal';
|
||||
});
|
||||
|
||||
// if there is no conditions, lets just return the sum
|
||||
if (! $conditions->count()) {
|
||||
return Helpers::formatValue($sum, $formatted, $this->config);
|
||||
}
|
||||
|
||||
// there are conditions, lets apply it
|
||||
$newTotal = 0.00;
|
||||
$process = 0;
|
||||
|
||||
$conditions->each(function (CartCondition $cond) use ($sum, &$newTotal, &$process) {
|
||||
|
||||
// if this is the first iteration, the toBeCalculated
|
||||
// should be the sum as initial point of value.
|
||||
$toBeCalculated = ($process > 0) ? $newTotal : $sum;
|
||||
|
||||
$newTotal = $cond->applyCondition($toBeCalculated);
|
||||
|
||||
$process++;
|
||||
});
|
||||
|
||||
return Helpers::formatValue((float) $newTotal, $formatted, $this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* the new total in which conditions are already applied
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getTotal()
|
||||
{
|
||||
$subTotal = $this->getSubTotal(false);
|
||||
|
||||
$newTotal = 0.00;
|
||||
|
||||
$process = 0;
|
||||
|
||||
$conditions = $this
|
||||
->getConditions()
|
||||
->filter(function (CartCondition $cond) {
|
||||
return $cond->getTarget() === 'total';
|
||||
});
|
||||
|
||||
// if no conditions were added, just return the sub total
|
||||
if (! $conditions->count()) {
|
||||
return Helpers::formatValue($subTotal, $this->config['format_numbers'], $this->config);
|
||||
}
|
||||
|
||||
$conditions
|
||||
->each(function (CartCondition $cond) use ($subTotal, &$newTotal, &$process) {
|
||||
$toBeCalculated = ($process > 0) ? $newTotal : $subTotal;
|
||||
|
||||
$newTotal = $cond->applyCondition($toBeCalculated);
|
||||
|
||||
$process++;
|
||||
});
|
||||
|
||||
return Helpers::formatValue($newTotal, $this->config['format_numbers'], $this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* get total quantity of items in the cart
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTotalQuantity()
|
||||
{
|
||||
$items = $this->getContent();
|
||||
|
||||
if ($items->isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$count = $items->sum(function ($item) {
|
||||
return $item['quantity'];
|
||||
});
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the cart
|
||||
*
|
||||
* @return CartCollection
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return (new CartCollection($this->session->get($this->sessionKeyCartItems)))->reject(function ($item) {
|
||||
return ! ($item instanceof ItemCollection);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* check if cart is empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmpty()
|
||||
{
|
||||
return $this->getContent()->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* validate Item data
|
||||
*
|
||||
* @return array $item;
|
||||
*
|
||||
* @throws InvalidItemException
|
||||
*/
|
||||
protected function validate($item)
|
||||
{
|
||||
$rules = [
|
||||
'id' => 'required',
|
||||
'price' => 'required|numeric',
|
||||
'quantity' => 'required|numeric|min:0.1',
|
||||
'name' => 'required',
|
||||
];
|
||||
|
||||
$validator = CartItemValidator::make($item, $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
throw new InvalidItemException($validator->messages()->first());
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* add row to cart collection
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function addRow($id, $item)
|
||||
{
|
||||
if ($this->fireEvent('adding', $item) === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cart = $this->getContent();
|
||||
|
||||
$cart->put($id, new ItemCollection($item, $this->config));
|
||||
|
||||
$this->save($cart);
|
||||
|
||||
$this->fireEvent('added', $item);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* save the cart
|
||||
*
|
||||
* @param $cart CartCollection
|
||||
*/
|
||||
protected function save($cart)
|
||||
{
|
||||
$this->session->put($this->sessionKeyCartItems, $cart);
|
||||
}
|
||||
|
||||
/**
|
||||
* save the cart conditions
|
||||
*/
|
||||
protected function saveConditions($conditions)
|
||||
{
|
||||
$this->session->put($this->sessionKeyCartConditions, $conditions);
|
||||
}
|
||||
|
||||
/**
|
||||
* check if an item has condition
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function itemHasConditions($item)
|
||||
{
|
||||
if (! isset($item['conditions'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (is_array($item['conditions'])) {
|
||||
return count($item['conditions']) > 0;
|
||||
}
|
||||
|
||||
$conditionInstance = 'Wearepixel\\Cart\\CartCondition';
|
||||
|
||||
if ($item['conditions'] instanceof $conditionInstance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* update a cart item quantity relative to its current quantity
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function updateQuantityRelative($item, $key, $value)
|
||||
{
|
||||
if (preg_match('/\-/', $value) == 1) {
|
||||
$value = (int) str_replace('-', '', $value);
|
||||
|
||||
// we will not allowed to reduced quantity to 0, so if the given value
|
||||
// would result to item quantity of 0, we will not do it.
|
||||
if (($item[$key] - $value) > 0) {
|
||||
$item[$key] -= $value;
|
||||
}
|
||||
} elseif (preg_match('/\+/', $value) == 1) {
|
||||
$item[$key] += (int) str_replace('+', '', $value);
|
||||
} else {
|
||||
$item[$key] += (int) $value;
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* update cart item quantity not relative to its current quantity value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function updateQuantityNotRelative($item, $key, $value)
|
||||
{
|
||||
$item[$key] = (int) $value;
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for decimals. Change value on demand.
|
||||
*/
|
||||
public function setDecimals($decimals)
|
||||
{
|
||||
$this->decimals = $decimals;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
protected function fireEvent($name, $value = [])
|
||||
{
|
||||
return $this->events->dispatch($this->getInstanceName() . '.' . $name, array_values([$value, $this]), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the cart item with the given id with the given model.
|
||||
*
|
||||
* @param string $id
|
||||
* @param mixed $model
|
||||
* @return void
|
||||
*/
|
||||
public function associate($model)
|
||||
{
|
||||
if (is_string($model) && ! class_exists($model)) {
|
||||
throw new UnknownModelException("The supplied model {$model} does not exist.");
|
||||
}
|
||||
|
||||
$cart = $this->getContent();
|
||||
|
||||
$item = $cart->pull($this->currentItemId);
|
||||
|
||||
$item['associatedModel'] = $model;
|
||||
|
||||
$cart->put($this->currentItemId, new ItemCollection($item, $this->config));
|
||||
|
||||
$this->save($cart);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class CartCollection extends Collection
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart;
|
||||
|
||||
use Wearepixel\Cart\Helpers\Helpers;
|
||||
use Wearepixel\Cart\Validators\CartConditionValidator;
|
||||
use Wearepixel\Cart\Exceptions\InvalidConditionException;
|
||||
|
||||
class CartCondition
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $args;
|
||||
|
||||
/**
|
||||
* the parsed raw value of the condition
|
||||
*/
|
||||
public $parsedRawValue;
|
||||
|
||||
/**
|
||||
* @param array $args (name, type, target, value)
|
||||
*
|
||||
* @throws InvalidConditionException
|
||||
*/
|
||||
public function __construct(array $args)
|
||||
{
|
||||
$this->args = $args;
|
||||
|
||||
if (Helpers::isMultiArray($args)) {
|
||||
throw new InvalidConditionException('Multi dimensional array is not supported.');
|
||||
} else {
|
||||
$this->validate($this->args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* the target of where the condition is applied.
|
||||
* NOTE: On conditions added to per item bases, target is not needed.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getTarget()
|
||||
{
|
||||
return (isset($this->args['target'])) ? $this->args['target'] : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* the name of the condition
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->args['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* the type of the condition
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->args['type'];
|
||||
}
|
||||
|
||||
/**
|
||||
* get the additional attributes of a condition
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes()
|
||||
{
|
||||
return (isset($this->args['attributes'])) ? $this->args['attributes'] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* the value of this the condition
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->args['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order to apply this condition. If no argument order is applied we return 0 as
|
||||
* indicator that no assignment has been made
|
||||
*
|
||||
* @param int $order
|
||||
* @return int
|
||||
*/
|
||||
public function setOrder($order = 1)
|
||||
{
|
||||
$this->args['order'] = $order;
|
||||
}
|
||||
|
||||
/**
|
||||
* the order to apply this condition. If no argument order is applied we return 0 as
|
||||
* indicator that no assignment has been made
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getOrder()
|
||||
{
|
||||
return isset($this->args['order']) && is_numeric($this->args['order']) ? (int) $this->args['order'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* apply condition to total or subtotal
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function applyCondition($totalOrSubTotalOrPrice)
|
||||
{
|
||||
return $this->apply($totalOrSubTotalOrPrice, $this->getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* get the calculated value of this condition supplied by the subtotal|price
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCalculatedValue($totalOrSubTotalOrPrice)
|
||||
{
|
||||
$this->apply($totalOrSubTotalOrPrice, $this->getValue());
|
||||
|
||||
return $this->parsedRawValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* apply condition
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
protected function apply($totalOrSubTotalOrPrice, $conditionValue)
|
||||
{
|
||||
// if value has a percentage sign on it, we will get first
|
||||
// its percentage then we will evaluate again if the value
|
||||
// has a minus or plus sign so we can decide what to do with the
|
||||
// percentage, whether to add or subtract it to the total/subtotal/price
|
||||
// if we can't find any plus/minus sign, we will assume it as plus sign
|
||||
if ($this->valueIsPercentage($conditionValue)) {
|
||||
if ($this->valueIsToBeSubtracted($conditionValue)) {
|
||||
$value = Helpers::normalizePrice($this->cleanValue($conditionValue));
|
||||
|
||||
$this->parsedRawValue = $totalOrSubTotalOrPrice * ($value / 100);
|
||||
|
||||
$result = floatval($totalOrSubTotalOrPrice - $this->parsedRawValue);
|
||||
} elseif ($this->valueIsToBeAdded($conditionValue)) {
|
||||
$value = Helpers::normalizePrice($this->cleanValue($conditionValue));
|
||||
|
||||
$this->parsedRawValue = $totalOrSubTotalOrPrice * ($value / 100);
|
||||
|
||||
$result = floatval($totalOrSubTotalOrPrice + $this->parsedRawValue);
|
||||
} else {
|
||||
$value = Helpers::normalizePrice($conditionValue);
|
||||
|
||||
$this->parsedRawValue = $totalOrSubTotalOrPrice * ($value / 100);
|
||||
|
||||
$result = floatval($totalOrSubTotalOrPrice + $this->parsedRawValue);
|
||||
}
|
||||
}
|
||||
|
||||
// if the value has no percent sign on it, the operation will not be a percentage
|
||||
// next is we will check if it has a minus/plus sign so then we can just deduct it to total/subtotal/price
|
||||
else {
|
||||
if ($this->valueIsToBeSubtracted($conditionValue)) {
|
||||
$this->parsedRawValue = Helpers::normalizePrice($this->cleanValue($conditionValue));
|
||||
|
||||
$result = floatval($totalOrSubTotalOrPrice - $this->parsedRawValue);
|
||||
} elseif ($this->valueIsToBeAdded($conditionValue)) {
|
||||
$this->parsedRawValue = Helpers::normalizePrice($this->cleanValue($conditionValue));
|
||||
|
||||
$result = floatval($totalOrSubTotalOrPrice + $this->parsedRawValue);
|
||||
} else {
|
||||
$this->parsedRawValue = Helpers::normalizePrice($conditionValue);
|
||||
|
||||
$result = floatval($totalOrSubTotalOrPrice + $this->parsedRawValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow items with negative prices.
|
||||
return $result < 0 ? 0.00 : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if value is a percentage
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function valueIsPercentage($value)
|
||||
{
|
||||
return preg_match('/%/', $value) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if value is a subtract
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function valueIsToBeSubtracted($value)
|
||||
{
|
||||
return preg_match('/\-/', $value) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if value is to be added
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function valueIsToBeAdded($value)
|
||||
{
|
||||
return preg_match('/\+/', $value) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* removes some arithmetic signs (%,+,-) only
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function cleanValue($value)
|
||||
{
|
||||
return str_replace(['%', '-', '+'], '', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* validates condition arguments
|
||||
*
|
||||
* @throws InvalidConditionException
|
||||
*/
|
||||
protected function validate($args)
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required',
|
||||
'type' => 'required',
|
||||
'value' => 'required',
|
||||
];
|
||||
|
||||
$validator = CartConditionValidator::make($args, $rules);
|
||||
|
||||
if ($validator->fails()) {
|
||||
throw new InvalidConditionException($validator->messages()->first());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class CartConditionCollection extends Collection {}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class CartServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* Boot the service provider.
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->publishes([
|
||||
__DIR__ . '/../config/config.php' => config_path('cart.php'),
|
||||
], 'config');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->mergeConfigFrom(__DIR__ . '/../config/config.php', 'cart');
|
||||
|
||||
$this->app->singleton('cart', function ($app) {
|
||||
$storageClass = config('cart.storage');
|
||||
$eventsClass = config('cart.events');
|
||||
|
||||
$storage = $storageClass ? new $storageClass : $app['session'];
|
||||
$events = $eventsClass ? new $eventsClass : $app['events'];
|
||||
$instanceName = 'cart';
|
||||
|
||||
// default session or cart identifier. This will be overridden when calling Cart::session($sessionKey)->add() etc..
|
||||
// like when adding a cart for a specific user name. Session Key can be string or maybe a unique identifier to bind a cart
|
||||
// to a specific user, this can also be a user ID
|
||||
$session_key = '4yTlTDKu3oJOfzD';
|
||||
|
||||
return new Cart(
|
||||
$storage,
|
||||
$events,
|
||||
$instanceName,
|
||||
$session_key,
|
||||
config('cart')
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the services provided by the provider.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function provides()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Exceptions;
|
||||
|
||||
class InvalidConditionException extends \Exception {}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Exceptions;
|
||||
|
||||
class InvalidItemException extends \Exception {}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Exceptions;
|
||||
|
||||
class UnknownModelException extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class CartFacade extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return 'cart';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Helpers;
|
||||
|
||||
class Helpers
|
||||
{
|
||||
/**
|
||||
* normalize price
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public static function normalizePrice($price)
|
||||
{
|
||||
return (is_string($price)) ? floatval($price) : $price;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if array is multi dimensional array
|
||||
* This will only check the first element of the array if it is still an array
|
||||
* to decide that it is a multi dimensional, if you want to check the array strictly
|
||||
* with all on its element, flag the second argument as true
|
||||
*
|
||||
* @param bool $recursive
|
||||
* @return bool
|
||||
*/
|
||||
public static function isMultiArray($array, $recursive = false)
|
||||
{
|
||||
if ($recursive) {
|
||||
return (count($array) == count($array, COUNT_RECURSIVE)) ? false : true;
|
||||
} else {
|
||||
foreach ($array as $k => $v) {
|
||||
if (is_array($v)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check if variable is set and has value, return a default value
|
||||
*
|
||||
* @param bool|mixed $default
|
||||
* @return bool|mixed
|
||||
*/
|
||||
public static function issetAndHasValueOrAssignDefault(&$var, $default = false)
|
||||
{
|
||||
if ((isset($var)) && ($var != '')) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
public static function formatValue($value, $formatNumber, $config)
|
||||
{
|
||||
if ($formatNumber || $config['format_numbers']) {
|
||||
return floatval(number_format($value, $config['decimals'], $config['dec_point'], $config['thousands_sep']));
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class ItemAttributeCollection extends Collection
|
||||
{
|
||||
public function __get($name)
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
return $this->get($name);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Wearepixel\Cart\Helpers\Helpers;
|
||||
|
||||
class ItemCollection extends Collection
|
||||
{
|
||||
/**
|
||||
* Sets the config parameters.
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* ItemCollection constructor.
|
||||
*
|
||||
* @param array|mixed $items
|
||||
*/
|
||||
public function __construct($items, $config = [])
|
||||
{
|
||||
parent::__construct($items);
|
||||
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the sum of price
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getPriceSum()
|
||||
{
|
||||
return Helpers::formatValue($this->price * $this->quantity, $this->config['format_numbers'], $this->config);
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if ($this->has($name) || $name == 'model') {
|
||||
return ! is_null($this->get($name)) ? $this->get($name) : $this->getAssociatedModel();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* return the associated model of an item
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function getAssociatedModel()
|
||||
{
|
||||
if (! $this->has('associatedModel')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$associatedModel = $this->get('associatedModel');
|
||||
|
||||
return with(new $associatedModel)->find($this->get('id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* check if item has conditions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasConditions()
|
||||
{
|
||||
if (! isset($this['conditions'])) {
|
||||
return false;
|
||||
}
|
||||
if (is_array($this['conditions'])) {
|
||||
return count($this['conditions']) > 0;
|
||||
}
|
||||
$conditionInstance = 'Wearepixel\\Cart\\CartCondition';
|
||||
if ($this['conditions'] instanceof $conditionInstance) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if item has conditions
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getConditions()
|
||||
{
|
||||
if (! $this->hasConditions()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $this['conditions'];
|
||||
}
|
||||
|
||||
/**
|
||||
* get the single price in which conditions are already applied
|
||||
*
|
||||
* @param bool $formatted
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getPriceWithConditions($formatted = true)
|
||||
{
|
||||
$originalPrice = $this->price;
|
||||
|
||||
$newPrice = 0.00;
|
||||
$processed = 0;
|
||||
|
||||
if ($this->hasConditions()) {
|
||||
if (is_array($this->conditions)) {
|
||||
foreach ($this->conditions as $condition) {
|
||||
($processed > 0) ? $toBeCalculated = $newPrice : $toBeCalculated = $originalPrice;
|
||||
$newPrice = $condition->applyCondition($toBeCalculated);
|
||||
$processed++;
|
||||
}
|
||||
} else {
|
||||
$newPrice = $this['conditions']->applyCondition($originalPrice);
|
||||
}
|
||||
|
||||
return Helpers::formatValue($newPrice, $formatted, $this->config);
|
||||
}
|
||||
|
||||
return Helpers::formatValue($originalPrice, $formatted, $this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* get the sum of price in which conditions are already applied
|
||||
*
|
||||
* @param bool $formatted
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function getPriceSumWithConditions($formatted = true)
|
||||
{
|
||||
return Helpers::formatValue($this->getPriceWithConditions(false) * $this->quantity, $formatted, $this->config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Validators;
|
||||
|
||||
class CartConditionValidator extends Validator {}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Validators;
|
||||
|
||||
class CartItemValidator extends Validator {}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Validators;
|
||||
|
||||
use Illuminate\Validation\Factory;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Translation\FileLoader;
|
||||
use Illuminate\Translation\Translator;
|
||||
|
||||
abstract class Validator
|
||||
{
|
||||
protected static $factory;
|
||||
|
||||
public static function instance()
|
||||
{
|
||||
if (! static::$factory) {
|
||||
$loader = new FileLoader(
|
||||
new Filesystem,
|
||||
'/Translations'
|
||||
);
|
||||
|
||||
$translator = new Translator($loader, 'en');
|
||||
static::$factory = new Factory($translator);
|
||||
}
|
||||
|
||||
return static::$factory;
|
||||
}
|
||||
|
||||
public static function __callStatic($method, $args)
|
||||
{
|
||||
$instance = static::instance();
|
||||
|
||||
switch (count($args)) {
|
||||
case 0:
|
||||
return $instance->$method();
|
||||
|
||||
case 1:
|
||||
return $instance->$method($args[0]);
|
||||
|
||||
case 2:
|
||||
return $instance->$method($args[0], $args[1]);
|
||||
|
||||
case 3:
|
||||
return $instance->$method($args[0], $args[1], $args[2]);
|
||||
|
||||
case 4:
|
||||
return $instance->$method($args[0], $args[1], $args[2], $args[3]);
|
||||
|
||||
default:
|
||||
return call_user_func_array([$instance, $method], $args);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'format_numbers' => false,
|
||||
'decimals' => 2,
|
||||
'dec_point' => '.',
|
||||
'thousands_sep' => ',',
|
||||
];
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'format_numbers' => false,
|
||||
'decimals' => 3,
|
||||
'dec_point' => ',',
|
||||
'thousands_sep' => '.',
|
||||
];
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Tests\Helpers;
|
||||
|
||||
use Illuminate\Contracts\Database\ModelIdentifier;
|
||||
|
||||
class MockProduct extends ModelIdentifier
|
||||
{
|
||||
/**
|
||||
* @var int|string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $price;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $weight;
|
||||
|
||||
/**
|
||||
* Product constructor.
|
||||
*
|
||||
* @param int|string $id
|
||||
* @param string $name
|
||||
* @param float $price
|
||||
*/
|
||||
public function __construct($id = 455, $name = 'Sample Item', $price = 100.99, $weight = 0)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->price = $price;
|
||||
$this->weight = $weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identifier of the item.
|
||||
*
|
||||
* @return int|string
|
||||
*/
|
||||
public function getIdentifier($options = null)
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description or title of the item.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescription($options = null)
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the price of the item.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getPrice($options = null)
|
||||
{
|
||||
return $this->price;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the price of the item.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public function getWeight($options = null)
|
||||
{
|
||||
return $this->weight;
|
||||
}
|
||||
|
||||
public function find($id)
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Wearepixel\Cart\Tests\Helpers;
|
||||
|
||||
class SessionMock
|
||||
{
|
||||
protected $session = [];
|
||||
|
||||
public function has($key)
|
||||
{
|
||||
return isset($this->session[$key]);
|
||||
}
|
||||
|
||||
public function get($key)
|
||||
{
|
||||
return (isset($this->session[$key])) ? $this->session[$key] : null;
|
||||
}
|
||||
|
||||
public function put($key, $value)
|
||||
{
|
||||
$this->session[$key] = $value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Test Case
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The closure you provide to your test functions is always bound to a specific PHPUnit test
|
||||
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
|
||||
| need to change it using the "pest()" function to bind a different classes or traits.
|
||||
|
|
||||
*/
|
||||
|
||||
// pest()->extend(Tests\TestCase::class)->in('Feature');
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expectations
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When you're writing tests, you often need to check that values meet certain conditions. The
|
||||
| "expect()" function gives you access to a set of "expectations" methods that you can use
|
||||
| to assert different things. Of course, you may extend the Expectation API at any time.
|
||||
|
|
||||
*/
|
||||
|
||||
expect()->extend('toBeOne', function () {
|
||||
return $this->toBe(1);
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Functions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
|
||||
| project that you don't want to repeat in every file. Here you can also expose helpers as
|
||||
| global functions to help you to reduce the number of lines of code in your test files.
|
||||
|
|
||||
*/
|
||||
|
||||
function fillCart()
|
||||
{
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase as BaseTestCase;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,862 @@
|
||||
<?php
|
||||
|
||||
use Wearepixel\Cart\Cart;
|
||||
use Wearepixel\Cart\CartCondition;
|
||||
use Wearepixel\Cart\Tests\Helpers\SessionMock;
|
||||
|
||||
beforeEach(function () {
|
||||
$events = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch');
|
||||
|
||||
$this->cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'cart',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
test('subtotal', function () {
|
||||
// add condition to subtotal
|
||||
$condition = new CartCondition([
|
||||
'name' => 'VAT 12.5%',
|
||||
'type' => 'tax',
|
||||
'target' => 'subtotal',
|
||||
'value' => '-5',
|
||||
]);
|
||||
|
||||
$this->cart->condition($condition);
|
||||
|
||||
expect($this->cart->getSubTotal())->toEqual(182.49);
|
||||
|
||||
// the total is also should be the same with sub total since our getTotal
|
||||
// also depends on what is the value of subtotal
|
||||
expect($this->cart->getTotal())->toEqual(182.49);
|
||||
});
|
||||
|
||||
test('total without condition', function () {
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// no changes in subtotal as the condition's target added was for total
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// total should be the same as subtotal
|
||||
expect($this->cart->getTotal())->toEqual(187.49, 'Cart should have a total of 187.49');
|
||||
});
|
||||
|
||||
test('total with condition', function () {
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// add condition
|
||||
$condition = new CartCondition([
|
||||
'name' => 'VAT 12.5%',
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => '12.5%',
|
||||
]);
|
||||
|
||||
$this->cart->condition($condition);
|
||||
|
||||
// no changes in subtotal as the condition's target added was for total
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// total should be changed
|
||||
$this->cart->setDecimals(5);
|
||||
|
||||
expect($this->cart->getTotal())->toEqual(210.92625, 'Cart should have a total of 210.92625');
|
||||
});
|
||||
|
||||
test('total with multiple conditions added scenario one', function () {
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// add condition
|
||||
$condition1 = new CartCondition([
|
||||
'name' => 'VAT 12.5%',
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => '12.5%',
|
||||
]);
|
||||
|
||||
$condition2 = new CartCondition([
|
||||
'name' => 'Express Shipping $15',
|
||||
'type' => 'shipping',
|
||||
'target' => 'total',
|
||||
'value' => '+15',
|
||||
]);
|
||||
|
||||
$this->cart->condition($condition1);
|
||||
$this->cart->condition($condition2);
|
||||
|
||||
// no changes in subtotal as the condition's target added was for subtotal
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// total should be changed
|
||||
$this->cart->setDecimals(5);
|
||||
expect($this->cart->getTotal())->toEqual(225.92625, 'Cart should have a total of 225.92625');
|
||||
});
|
||||
|
||||
test('total with multiple conditions added scenario two', function () {
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// add condition
|
||||
$condition1 = new CartCondition([
|
||||
'name' => 'VAT 12.5%',
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => '12.5%',
|
||||
]);
|
||||
$condition2 = new CartCondition([
|
||||
'name' => 'Express Shipping $15',
|
||||
'type' => 'shipping',
|
||||
'target' => 'total',
|
||||
'value' => '-15',
|
||||
]);
|
||||
|
||||
$this->cart->condition($condition1);
|
||||
$this->cart->condition($condition2);
|
||||
|
||||
// no changes in subtotal as the condition's target added was for subtotal
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// total should be changed
|
||||
$this->cart->setDecimals(5);
|
||||
expect($this->cart->getTotal())->toEqual(195.92625, 'Cart should have a total of 195.92625');
|
||||
});
|
||||
|
||||
test('total with multiple conditions added scenario three', function () {
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// add condition
|
||||
$condition1 = new CartCondition([
|
||||
'name' => 'VAT 12.5%',
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => '-12.5%',
|
||||
]);
|
||||
$condition2 = new CartCondition([
|
||||
'name' => 'Express Shipping $15',
|
||||
'type' => 'shipping',
|
||||
'target' => 'total',
|
||||
'value' => '-15',
|
||||
]);
|
||||
|
||||
$this->cart->condition($condition1);
|
||||
$this->cart->condition($condition2);
|
||||
|
||||
// no changes in subtotal as the condition's target added was for total
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// total should be changed
|
||||
$this->cart->setDecimals(5);
|
||||
expect($this->cart->getTotal())->toEqual(149.05375, 'Cart should have a total of 149.05375');
|
||||
});
|
||||
|
||||
test('cart multiple conditions can be added once by array', function () {
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// add condition
|
||||
$condition1 = new CartCondition([
|
||||
'name' => 'VAT 12.5%',
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => '-12.5%',
|
||||
]);
|
||||
$condition2 = new CartCondition([
|
||||
'name' => 'Express Shipping $15',
|
||||
'type' => 'shipping',
|
||||
'target' => 'total',
|
||||
'value' => '-15',
|
||||
]);
|
||||
|
||||
$this->cart->condition([$condition1, $condition2]);
|
||||
|
||||
// no changes in subtotal as the condition's target added was for total
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// total should be changed
|
||||
$this->cart->setDecimals(5);
|
||||
expect($this->cart->getTotal())->toEqual(149.05375, 'Cart should have a total of 149.05375');
|
||||
});
|
||||
|
||||
test('total with multiple conditions added scenario four', function () {
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// add condition
|
||||
$condition1 = new CartCondition([
|
||||
'name' => 'COUPON LESS 12.5%',
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => '-12.5%',
|
||||
]);
|
||||
$condition2 = new CartCondition([
|
||||
'name' => 'Express Shipping $15',
|
||||
'type' => 'shipping',
|
||||
'target' => 'total',
|
||||
'value' => '+15',
|
||||
]);
|
||||
|
||||
$this->cart->condition($condition1);
|
||||
$this->cart->condition($condition2);
|
||||
|
||||
// no changes in subtotal as the condition's target added was for total
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// total should be changed
|
||||
$this->cart->setDecimals(5);
|
||||
expect($this->cart->getTotal())->toEqual(179.05375, 'Cart should have a total of 179.05375');
|
||||
});
|
||||
|
||||
test('add item with condition', function () {
|
||||
$this->cart->clear();
|
||||
|
||||
$condition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'tax',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => $condition1,
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
expect($this->cart->get(456)->getPriceSumWithConditions())->toEqual(95);
|
||||
expect($this->cart->getSubTotal())->toEqual(95);
|
||||
});
|
||||
|
||||
test('add item with multiple item conditions in multiple condition instance', function () {
|
||||
$this->cart->clear();
|
||||
|
||||
$itemCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'value' => '-25',
|
||||
]);
|
||||
$itemCondition3 = new CartCondition([
|
||||
'name' => 'MISC',
|
||||
'type' => 'misc',
|
||||
'value' => '+10',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => [$itemCondition1, $itemCondition2, $itemCondition3],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
expect($this->cart->get(456)->getPriceSumWithConditions())->toEqual(80.00, 'Item subtotal with 1 item should be 80');
|
||||
expect($this->cart->getSubTotal())->toEqual(80.00, 'Cart subtotal with 1 item should be 80');
|
||||
});
|
||||
|
||||
test('add item with multiple item conditions with target omitted', function () {
|
||||
$this->cart->clear();
|
||||
|
||||
// NOTE:
|
||||
// $condition1 and $condition4 should not be included in calculation
|
||||
// as the target is not for item, remember that when adding
|
||||
// conditions in per-item bases, the condition's target should
|
||||
// have a value of item
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'value' => '-25',
|
||||
]);
|
||||
$itemCondition3 = new CartCondition([
|
||||
'name' => 'MISC',
|
||||
'type' => 'misc',
|
||||
'value' => '+10',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => [$itemCondition2, $itemCondition3],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
expect($this->cart->get(456)->getPriceSumWithConditions())->toEqual(85.00, 'Cart subtotal with 1 item should be 85');
|
||||
expect($this->cart->getSubTotal())->toEqual(85.00, 'Cart subtotal with 1 item should be 85');
|
||||
});
|
||||
|
||||
test('add item condition', function () {
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'value' => '-25',
|
||||
]);
|
||||
$coupon101 = new CartCondition([
|
||||
'name' => 'COUPON 101',
|
||||
'type' => 'coupon',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => [$itemCondition2],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
// let's prove first we have 1 condition on this item
|
||||
expect($this->cart->get($item['id'])['conditions'])->toHaveCount(1, 'Item should have 1 condition');
|
||||
|
||||
// now let's insert a condition on an existing item on the cart
|
||||
$this->cart->addItemCondition($item['id'], $coupon101);
|
||||
|
||||
expect($this->cart->get($item['id'])['conditions'])->toHaveCount(2, 'Item should have 2 conditions');
|
||||
});
|
||||
|
||||
test('add item condition restrict negative price', function () {
|
||||
$condition = new CartCondition([
|
||||
'name' => 'Substract amount but prevent negative value',
|
||||
'type' => 'promo',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 789,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 20,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => [
|
||||
$condition,
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
// Since the product price is 20 and the condition reduces it by 25,
|
||||
// check that the item's price has been prevented from dropping below zero.
|
||||
expect($this->cart->get($item['id'])->getPriceSumWithConditions())->toEqual(0.00, "The item's price should be prevented from going below zero.");
|
||||
});
|
||||
|
||||
test('get cart condition by condition name', function () {
|
||||
$itemCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$this->cart->condition([$itemCondition1, $itemCondition2]);
|
||||
|
||||
// get a condition applied on cart by condition name
|
||||
$condition = $this->cart->getCondition($itemCondition1->getName());
|
||||
|
||||
expect('SALE 5%')->toEqual($condition->getName());
|
||||
expect('total')->toEqual($condition->getTarget());
|
||||
expect('sale')->toEqual($condition->getType());
|
||||
expect('-5%')->toEqual($condition->getValue());
|
||||
});
|
||||
|
||||
test('remove cart condition by condition name', function () {
|
||||
$itemCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$this->cart->condition([$itemCondition1, $itemCondition2]);
|
||||
|
||||
// let's prove first we have now two conditions in the cart
|
||||
expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions');
|
||||
|
||||
// now let's remove a specific condition by condition name
|
||||
$this->cart->removeCartCondition('SALE 5%');
|
||||
|
||||
// cart should have now only 1 condition
|
||||
expect($this->cart->getConditions()->count())->toEqual(1, 'Cart should have one condition');
|
||||
expect($this->cart->getConditions()->first()->getName())->toEqual('Item Gift Pack 25.00');
|
||||
});
|
||||
|
||||
test('remove item condition by condition name', function () {
|
||||
$itemCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => [$itemCondition1, $itemCondition2],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
// let's very first the item has 2 conditions in it
|
||||
expect($this->cart->get(456)['conditions'])->toHaveCount(2, 'Item should have two conditions');
|
||||
|
||||
// now let's remove a condition on that item using the condition name
|
||||
$this->cart->removeItemCondition(456, 'SALE 5%');
|
||||
|
||||
// now we should have only 1 condition left on that item
|
||||
expect($this->cart->get(456)['conditions'])->toHaveCount(1, 'Item should have one condition left');
|
||||
});
|
||||
|
||||
test('remove item condition by condition name scenario two', function () {
|
||||
// NOTE: in this scenario, we will add the conditions not in array format
|
||||
$itemCondition = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => $itemCondition, // <--not in array format
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
// let's very first the item has 2 conditions in it
|
||||
expect($this->cart->get(456)['conditions'])->not->toBeEmpty('Item should have one condition in it.');
|
||||
|
||||
// now let's remove a condition on that item using the condition name
|
||||
$this->cart->removeItemCondition(456, 'SALE 5%');
|
||||
|
||||
// now we should have only 1 condition left on that item
|
||||
expect($this->cart->get(456)['conditions'])->toBeEmpty('Item should have no condition now');
|
||||
});
|
||||
|
||||
test('clear item conditions', function () {
|
||||
$itemCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
'conditions' => [$itemCondition1, $itemCondition2],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
// let's very first the item has 2 conditions in it
|
||||
expect($this->cart->get(456)['conditions'])->toHaveCount(2, 'Item should have two conditions');
|
||||
|
||||
// now let's remove all condition on that item
|
||||
$this->cart->clearItemConditions(456);
|
||||
|
||||
// now we should have only 0 condition left on that item
|
||||
expect($this->cart->get(456)['conditions'])->toHaveCount(0, 'Item should have no conditions now');
|
||||
});
|
||||
|
||||
test('clear cart conditions', function () {
|
||||
// NOTE:
|
||||
// This only clears all conditions that has been added in a cart bases
|
||||
// this does not remove conditions on per item bases
|
||||
$itemCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$this->cart->condition([$itemCondition1, $itemCondition2]);
|
||||
|
||||
// let's prove first we have now two conditions in the cart
|
||||
expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions');
|
||||
|
||||
// now let's clear cart conditions
|
||||
$this->cart->clearCartConditions();
|
||||
|
||||
// cart should have now only 1 condition
|
||||
expect($this->cart->getConditions()->count())->toEqual(0, 'Cart should have no conditions now');
|
||||
});
|
||||
|
||||
test('get calculated value of a condition', function () {
|
||||
$this->cart->clear();
|
||||
|
||||
$cartCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$cartCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$this->cart->condition([$cartCondition1, $cartCondition2]);
|
||||
|
||||
$subTotal = $this->cart->getSubTotal();
|
||||
|
||||
expect($subTotal)->toEqual(100, 'Subtotal should be 100');
|
||||
|
||||
// way 1
|
||||
// now we will get the calculated value of the condition 1
|
||||
$cond1 = $this->cart->getCondition('SALE 5%');
|
||||
expect($cond1->getCalculatedValue($subTotal))->toEqual(5, 'The calculated value must be 5');
|
||||
|
||||
// way 2
|
||||
// get all cart conditions and get their calculated values
|
||||
$conditions = $this->cart->getConditions();
|
||||
expect($conditions['SALE 5%']->getCalculatedValue($subTotal))->toEqual(5, 'First condition calculated value must be 5');
|
||||
expect($conditions['Item Gift Pack 25.00']->getCalculatedValue($subTotal))->toEqual(25, 'First condition calculated value must be 5');
|
||||
});
|
||||
|
||||
test('get conditions by type', function () {
|
||||
$cartCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$cartCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-25',
|
||||
]);
|
||||
$cartCondition3 = new CartCondition([
|
||||
'name' => 'Item Less 8%',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-8%',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$this->cart->condition([$cartCondition1, $cartCondition2, $cartCondition3]);
|
||||
|
||||
// now lets get all conditions added in the cart with the type "promo"
|
||||
$promoConditions = $this->cart->getConditionsByType('promo');
|
||||
|
||||
expect($promoConditions->count())->toEqual(2, 'We should have 2 items as promo condition type.');
|
||||
});
|
||||
|
||||
test('remove conditions by type', function () {
|
||||
// NOTE:
|
||||
// when add a new condition, the condition's name will be the key to be use
|
||||
// to access the condition. For some reasons, if the condition name contains
|
||||
// a "dot" on it ("."), for example adding a condition with name "SALE 35.00"
|
||||
// this will cause issues when removing this condition by name, this will not be removed
|
||||
// so when adding a condition, the condition name should not contain any "period" (.)
|
||||
// to avoid any issues removing it using remove method: removeCartCondition($conditionName);
|
||||
$cartCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
$cartCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 20',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-25',
|
||||
]);
|
||||
$cartCondition3 = new CartCondition([
|
||||
'name' => 'Item Less 8%',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-8%',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$this->cart->condition([$cartCondition1, $cartCondition2, $cartCondition3]);
|
||||
|
||||
// now lets remove all conditions added in the cart with the type "promo"
|
||||
$this->cart->removeConditionsByType('promo');
|
||||
|
||||
expect($this->cart->getConditions()->count())->toEqual(1, 'We should have 1 condition remaining as promo conditions type has been removed.');
|
||||
});
|
||||
|
||||
test('add cart condition without condition attributes', function () {
|
||||
$cartCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$this->cart->condition([$cartCondition1]);
|
||||
|
||||
// prove first we have now the condition on the cart
|
||||
$contition = $this->cart->getCondition('SALE 5%');
|
||||
expect($contition->getName())->toEqual('SALE 5%');
|
||||
|
||||
// when get attribute is called and there is no attributes added,
|
||||
// it should return an empty array
|
||||
$conditionAttribute = $contition->getAttributes();
|
||||
expect($conditionAttribute)->toBeArray();
|
||||
});
|
||||
|
||||
test('add cart condition with condition attributes', function () {
|
||||
$cartCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
'attributes' => [
|
||||
'description' => 'october fest promo sale',
|
||||
'sale_start_date' => '2015-01-20',
|
||||
'sale_end_date' => '2015-01-30',
|
||||
],
|
||||
]);
|
||||
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 100,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$this->cart->condition([$cartCondition1]);
|
||||
|
||||
// prove first we have now the condition on the cart
|
||||
$contition = $this->cart->getCondition('SALE 5%');
|
||||
expect($contition->getName())->toEqual('SALE 5%');
|
||||
|
||||
// when get attribute is called and there is no attributes added,
|
||||
// it should return an empty array
|
||||
$conditionAttributes = $contition->getAttributes();
|
||||
expect($conditionAttributes)->toBeArray();
|
||||
expect($conditionAttributes)->toHaveKey('description');
|
||||
expect($conditionAttributes)->toHaveKey('sale_start_date');
|
||||
expect($conditionAttributes)->toHaveKey('sale_end_date');
|
||||
expect($conditionAttributes['description'])->toEqual('october fest promo sale');
|
||||
expect($conditionAttributes['sale_start_date'])->toEqual('2015-01-20');
|
||||
expect($conditionAttributes['sale_end_date'])->toEqual('2015-01-30');
|
||||
});
|
||||
|
||||
test('get order from condition', function () {
|
||||
$cartCondition1 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
'order' => 2,
|
||||
]);
|
||||
$cartCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 20',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-25',
|
||||
'order' => '3',
|
||||
]);
|
||||
$cartCondition3 = new CartCondition([
|
||||
'name' => 'Item Less 8%',
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => '-8%',
|
||||
'order' => 'first',
|
||||
]);
|
||||
|
||||
expect($cartCondition1->getOrder())->toEqual(2);
|
||||
expect($cartCondition2->getOrder())->toEqual(3);
|
||||
// numeric string is converted to integer
|
||||
expect($cartCondition3->getOrder())->toEqual(0);
|
||||
|
||||
// no numeric string is converted to 0
|
||||
$this->cart->condition($cartCondition1);
|
||||
$this->cart->condition($cartCondition2);
|
||||
$this->cart->condition($cartCondition3);
|
||||
|
||||
$conditions = $this->cart->getConditions();
|
||||
|
||||
expect($conditions->shift()->getType())->toEqual('sale');
|
||||
expect($conditions->shift()->getType())->toEqual('promo');
|
||||
expect($conditions->shift()->getType())->toEqual('tax');
|
||||
});
|
||||
|
||||
test('condition ordering', function () {
|
||||
$cartCondition1 = new CartCondition([
|
||||
'name' => 'TAX',
|
||||
'type' => 'tax',
|
||||
'target' => 'total',
|
||||
'value' => '-8%',
|
||||
'order' => 5,
|
||||
]);
|
||||
$cartCondition2 = new CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'total',
|
||||
'value' => '-5%',
|
||||
'order' => 2,
|
||||
]);
|
||||
$cartCondition3 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 20',
|
||||
'type' => 'promo',
|
||||
'target' => 'total',
|
||||
'value' => '-25',
|
||||
'order' => 1,
|
||||
]);
|
||||
|
||||
$this->cart->condition($cartCondition1);
|
||||
$this->cart->condition($cartCondition2);
|
||||
$this->cart->condition($cartCondition3);
|
||||
|
||||
expect($this->cart->getConditions()->first()->getName())->toEqual('Item Gift Pack 20');
|
||||
expect($this->cart->getConditions()->last()->getName())->toEqual('TAX');
|
||||
});
|
||||
@@ -0,0 +1,531 @@
|
||||
<?php
|
||||
|
||||
use Wearepixel\Cart\Cart;
|
||||
use Wearepixel\Cart\Tests\Helpers\MockProduct;
|
||||
use Wearepixel\Cart\Tests\Helpers\SessionMock;
|
||||
|
||||
beforeEach(function () {
|
||||
$events = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch');
|
||||
|
||||
$this->cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'shopping',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
test('cart can add item', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, []);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart->getContent()->count())->toEqual(1, 'Cart content should be 1');
|
||||
expect($this->cart->getContent()->first()['id'])->toEqual(455, 'Item added has ID of 455 so first content ID should be 455');
|
||||
expect($this->cart->getContent()->first()['price'])->toEqual(100.99, 'Item added has price of 100.99 so first content price should be 100.99');
|
||||
});
|
||||
|
||||
test('cart can add item with no price', function () {
|
||||
$this->cart->add(455, 'Sample Item', 0, 2, []);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart->getContent()->count())->toEqual(1, 'Cart content should be 1');
|
||||
expect($this->cart->getContent()->first()['id'])->toEqual(455, 'Item added has ID of 455 so first content ID should be 455');
|
||||
expect($this->cart->getTotal())->toEqual(0, 'Total should be 0');
|
||||
});
|
||||
|
||||
test('cart can add items as array', function () {
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart->getContent()->count())->toEqual(1, 'Cart should have 1 item on it');
|
||||
expect($this->cart->getContent()->first()['id'])->toEqual(456, 'The first content must have ID of 456');
|
||||
expect($this->cart->getContent()->first()['name'])->toEqual('Sample Item', 'The first content must have name of "Sample Item"');
|
||||
});
|
||||
|
||||
test('cart can add items with multidimensional array', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart->getContent()->toArray())->toHaveCount(3, 'Cart should have 3 items');
|
||||
});
|
||||
|
||||
test('cart can add item without attributes', function () {
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
});
|
||||
|
||||
test('cart update with attribute then attributes should be still instance of item attribute collection', function () {
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [
|
||||
'product_id' => '145',
|
||||
'color' => 'red',
|
||||
],
|
||||
];
|
||||
$this->cart->add($item);
|
||||
|
||||
// lets get the attribute and prove first its an instance of
|
||||
// ItemAttributeCollection
|
||||
$item = $this->cart->get(456);
|
||||
|
||||
expect($item->attributes)->toBeInstanceOf('Wearepixel\Cart\ItemAttributeCollection');
|
||||
|
||||
// now lets update the item with its new attributes
|
||||
// when we get that item from cart, it should still be an instance of ItemAttributeCollection
|
||||
$updatedItem = [
|
||||
'attributes' => [
|
||||
'product_id' => '145',
|
||||
'color' => 'red',
|
||||
],
|
||||
];
|
||||
$this->cart->update(456, $updatedItem);
|
||||
|
||||
expect($item->attributes)->toBeInstanceOf('Wearepixel\Cart\ItemAttributeCollection');
|
||||
});
|
||||
|
||||
test('cart items attributes', function () {
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [
|
||||
'size' => 'L',
|
||||
'color' => 'blue',
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart->getContent()->first()['attributes'])->toHaveCount(2, 'Item\'s attribute should have two');
|
||||
expect($this->cart->getContent()->first()->attributes->size)->toEqual('L', 'Item should have attribute size of L');
|
||||
expect($this->cart->getContent()->first()->attributes->color)->toEqual('blue', 'Item should have attribute color of blue');
|
||||
expect($this->cart->get(456)->has('attributes'))->toBeTrue('Item should have attributes');
|
||||
expect($this->cart->get(456)->get('attributes')->size)->toEqual('L');
|
||||
});
|
||||
|
||||
test('cart update existing item', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 3,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
$itemIdToEvaluate = 456;
|
||||
|
||||
$item = $this->cart->get($itemIdToEvaluate);
|
||||
expect($item['name'])->toEqual('Sample Item 1', 'Item name should be "Sample Item 1"');
|
||||
expect($item['price'])->toEqual(67.99, 'Item price should be "67.99"');
|
||||
expect($item['quantity'])->toEqual(3, 'Item quantity should be 3');
|
||||
|
||||
// when cart's item quantity is updated, the subtotal should be updated as well
|
||||
$this->cart->update(456, [
|
||||
'name' => 'Renamed',
|
||||
'quantity' => 2,
|
||||
'price' => 105,
|
||||
]);
|
||||
|
||||
$item = $this->cart->get($itemIdToEvaluate);
|
||||
expect($item['name'])->toEqual('Renamed', 'Item name should be "Renamed"');
|
||||
expect($item['price'])->toEqual(105, 'Item price should be 105');
|
||||
expect($item['quantity'])->toEqual(5, 'Item quantity should be 2');
|
||||
});
|
||||
|
||||
test('cart update existing item with quantity as array and not relative', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 3,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
$itemIdToEvaluate = 456;
|
||||
$item = $this->cart->get($itemIdToEvaluate);
|
||||
expect($item['quantity'])->toEqual(3, 'Item quantity should be 3');
|
||||
|
||||
// now by default when an update takes place and the quantity attribute
|
||||
// is present, it will evaluate for arithmetic operation if the quantity
|
||||
// should be incremented or decremented, we should also allow the quantity
|
||||
// value to be in array format and provide a field if the quantity should not be
|
||||
// treated as relative to Item quantity current value
|
||||
$this->cart->update($itemIdToEvaluate, ['quantity' => ['relative' => false, 'value' => 5]]);
|
||||
|
||||
$item = $this->cart->get($itemIdToEvaluate);
|
||||
expect($item['quantity'])->toEqual(5, 'Item quantity should be 5');
|
||||
});
|
||||
|
||||
test('item price should be normalized when added to cart', function () {
|
||||
// add a price in a string format should be converted to float
|
||||
$this->cart->add(455, 'Sample Item', '100.99', 2, []);
|
||||
|
||||
expect($this->cart->getContent()->first()['price'])->toBeFloat('Cart price should be a float');
|
||||
});
|
||||
|
||||
test('it removes an item on cart by item id', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
$removeItemId = 456;
|
||||
|
||||
$this->cart->remove($removeItemId);
|
||||
|
||||
expect($this->cart->getContent()->toArray())->toHaveCount(2, 'Cart must have 2 items left');
|
||||
expect($this->cart->getContent()->has($removeItemId))->toBeFalse('Cart must have not contain the remove item anymore');
|
||||
});
|
||||
|
||||
test('cart sub total', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
expect($this->cart->getSubTotal())->toEqual(187.49, 'Cart should have sub total of 187.49');
|
||||
|
||||
// if we remove an item, the sub total should be updated as well
|
||||
$this->cart->remove(456);
|
||||
|
||||
expect($this->cart->getSubTotal())->toEqual(119.5, 'Cart should have sub total of 119.5');
|
||||
});
|
||||
|
||||
test('sub total when item quantity is updated', function () {
|
||||
$this->cart->clear();
|
||||
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 3,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
expect($this->cart->getSubTotal())
|
||||
->toBe(273.22, 'Cart should have sub total of 273.22');
|
||||
|
||||
// when cart's item quantity is updated, the subtotal should be updated as well
|
||||
$this->cart->update(456, ['quantity' => 2]);
|
||||
|
||||
expect($this->cart->getSubTotal())->toEqual(409.2, 'Cart should have sub total of 409.2');
|
||||
});
|
||||
|
||||
test('sub total when item quantity is updated by reduced', function () {
|
||||
// $this->cart->setConfig(['format_numbers' => true, 'decimals' => 2, 'dec_point' => '.', 'thousands_sep' => ',']);
|
||||
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 3,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
expect($this->cart->getSubTotal())
|
||||
->toBe(273.22, 'Cart should have sub total of 273.22');
|
||||
|
||||
// when cart's item quantity is updated, the subtotal should be updated as well
|
||||
$this->cart->update(456, ['quantity' => -1]);
|
||||
|
||||
// get the item to be evaluated
|
||||
$item = $this->cart->get(456);
|
||||
|
||||
expect($item['quantity'])->toEqual(2, 'Item quantity of with item ID of 456 should now be reduced to 2');
|
||||
expect($this->cart->getSubTotal())->toEqual(205.23, 'Cart should have sub total of 205.23');
|
||||
});
|
||||
|
||||
test('item quantity update by reduced should not reduce if quantity will result to zero', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 3,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
// get the item to be evaluated
|
||||
$item = $this->cart->get(456);
|
||||
|
||||
// prove first we have quantity of 3
|
||||
expect($item['quantity'])->toEqual(3, 'Item quantity of with item ID of 456 should be reduced to 3');
|
||||
|
||||
// when cart's item quantity is updated, and reduced to more than the current quantity
|
||||
// this should not work
|
||||
$this->cart->update(456, ['quantity' => -3]);
|
||||
|
||||
expect($item['quantity'])->toEqual(3, 'Item quantity of with item ID of 456 should now be reduced to 2');
|
||||
});
|
||||
|
||||
test('should throw exception when provided invalid values scenario one', function () {
|
||||
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 0, []);
|
||||
});
|
||||
|
||||
test('should throw exception when provided invalid values scenario two', function () {
|
||||
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
|
||||
$this->cart->add('', 'Sample Item', 100.99, 2, []);
|
||||
});
|
||||
|
||||
test('should throw exception when provided invalid values scenario three', function () {
|
||||
$this->expectException('Wearepixel\Cart\Exceptions\InvalidItemException');
|
||||
$this->cart->add(523, '', 100.99, 2, []);
|
||||
});
|
||||
|
||||
test('clearing cart', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 3,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('prove first cart is not empty');
|
||||
|
||||
// now let's clear cart
|
||||
$this->cart->clear();
|
||||
|
||||
expect($this->cart->isEmpty())->toBeTrue('cart should now be empty');
|
||||
});
|
||||
|
||||
test('cart get total quantity', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 3,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 1,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('prove first cart is not empty');
|
||||
|
||||
// now let's count the cart's quantity
|
||||
expect($this->cart->getTotalQuantity())->toBeInt('Return type should be INT');
|
||||
expect($this->cart->getTotalQuantity())->toEqual(4, 'Cart\'s quantity should be 4.');
|
||||
});
|
||||
|
||||
test('cart can add items as array with associated model', function () {
|
||||
$item = [
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
'associatedModel' => MockProduct::class,
|
||||
];
|
||||
|
||||
$this->cart->add($item);
|
||||
|
||||
$addedItem = $this->cart->get($item['id']);
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart->getContent()->count())->toEqual(1, 'Cart should have 1 item on it');
|
||||
expect($this->cart->getContent()->first()['id'])->toEqual(456, 'The first content must have ID of 456');
|
||||
expect($this->cart->getContent()->first()['name'])->toEqual('Sample Item', 'The first content must have name of "Sample Item"');
|
||||
expect($addedItem->model)->toBeInstanceOf('Wearepixel\Cart\Tests\Helpers\MockProduct');
|
||||
});
|
||||
|
||||
test('cart can add items with multidimensional array with associated model', function () {
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
'associatedModel' => MockProduct::class,
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
'associatedModel' => MockProduct::class,
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
'associatedModel' => MockProduct::class,
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart->add($items);
|
||||
|
||||
$content = $this->cart->getContent();
|
||||
foreach ($content as $item) {
|
||||
expect($item->model)->toBeInstanceOf('Wearepixel\Cart\Tests\Helpers\MockProduct');
|
||||
}
|
||||
|
||||
expect($this->cart->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart->getContent()->toArray())->toHaveCount(3, 'Cart should have 3 items');
|
||||
expect($this->cart->getTotalQuantity())->toBeInt('Return type should be INT');
|
||||
expect($this->cart->getTotalQuantity())->toEqual(12, 'Cart\'s quantity should be 4.');
|
||||
});
|
||||
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
use Wearepixel\Cart\Cart;
|
||||
use Wearepixel\Cart\Tests\Helpers\SessionMock;
|
||||
|
||||
beforeEach(function () {});
|
||||
|
||||
afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
test('event cart created', function () {
|
||||
$events = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true);
|
||||
|
||||
$cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'cart',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('event cart adding', function () {
|
||||
$events = Mockery::mock('Illuminate\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.adding', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.added', Mockery::type('array'), true);
|
||||
|
||||
$cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'cart',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
|
||||
$cart->add(455, 'Sample Item', 100.99, 2, []);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('event cart adding multiple times', function () {
|
||||
$events = Mockery::mock('Illuminate\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(2)->with('cart.adding', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(2)->with('cart.added', Mockery::type('array'), true);
|
||||
|
||||
$cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'cart',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
|
||||
$cart->add(455, 'Sample Item 1', 100.99, 2, []);
|
||||
$cart->add(562, 'Sample Item 2', 100.99, 2, []);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('event cart adding multiple times scenario two', function () {
|
||||
$events = Mockery::mock('Illuminate\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(3)->with('cart.adding', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(3)->with('cart.added', Mockery::type('array'), true);
|
||||
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'cart',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
|
||||
$cart->add($items);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('event cart remove item', function () {
|
||||
$events = Mockery::mock('Illuminate\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(3)->with('cart.adding', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(3)->with('cart.added', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(1)->with('cart.removing', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(1)->with('cart.removed', Mockery::type('array'), true);
|
||||
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'cart',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
|
||||
$cart->add($items);
|
||||
|
||||
$cart->remove(456);
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
|
||||
test('event cart clear', function () {
|
||||
$events = Mockery::mock('Illuminate\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.created', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(3)->with('cart.adding', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->times(3)->with('cart.added', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.clearing', Mockery::type('array'), true);
|
||||
$events->shouldReceive('dispatch')->once()->with('cart.cleared', Mockery::type('array'), true);
|
||||
|
||||
$items = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'cart',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
|
||||
$cart->add($items);
|
||||
|
||||
$cart->clear();
|
||||
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
use Wearepixel\Cart\Cart;
|
||||
use Wearepixel\Cart\Tests\Helpers\SessionMock;
|
||||
|
||||
beforeEach(function () {
|
||||
$events = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch');
|
||||
|
||||
$this->cart1 = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'shopping',
|
||||
'uniquesessionkey123',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
|
||||
$this->cart2 = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'wishlist',
|
||||
'uniquesessionkey456',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
test('cart multiple instances', function () {
|
||||
// add 3 items on cart 1
|
||||
$itemsForCart1 = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 568,
|
||||
'name' => 'Sample Item 2',
|
||||
'price' => 69.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
[
|
||||
'id' => 856,
|
||||
'name' => 'Sample Item 3',
|
||||
'price' => 50.25,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart1->add($itemsForCart1);
|
||||
|
||||
expect($this->cart1->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart1->getContent()->toArray())->toHaveCount(3, 'Cart should have 3 items');
|
||||
expect($this->cart1->getInstanceName())->toEqual('shopping', 'Cart 1 should have instance name of "shopping"');
|
||||
|
||||
// add 1 item on cart 2
|
||||
$itemsForCart2 = [
|
||||
[
|
||||
'id' => 456,
|
||||
'name' => 'Sample Item 1',
|
||||
'price' => 67.99,
|
||||
'quantity' => 4,
|
||||
'attributes' => [],
|
||||
],
|
||||
];
|
||||
|
||||
$this->cart2->add($itemsForCart2);
|
||||
|
||||
expect($this->cart2->isEmpty())->toBeFalse('Cart should not be empty');
|
||||
expect($this->cart2->getContent()->toArray())->toHaveCount(1, 'Cart should have 3 items');
|
||||
expect($this->cart2->getInstanceName())->toEqual('wishlist', 'Cart 2 should have instance name of "wishlist"');
|
||||
});
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
use Wearepixel\Cart\Cart;
|
||||
use Wearepixel\Cart\CartCondition;
|
||||
use Wearepixel\Cart\Tests\Helpers\MockProduct;
|
||||
use Wearepixel\Cart\Tests\Helpers\SessionMock;
|
||||
|
||||
beforeEach(function () {
|
||||
$events = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch');
|
||||
|
||||
$this->cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'shopping',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMock.php')
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
test('item get sum price using property', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, []);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->getPriceSum())->toEqual(201.98, 'Item summed price should be 201.98');
|
||||
});
|
||||
|
||||
test('item get sum price using array style', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, []);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->getPriceSum())->toEqual(201.98, 'Item summed price should be 201.98');
|
||||
});
|
||||
|
||||
test('item get conditions empty', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, []);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->getConditions())->toBeEmpty('Item should have no conditions');
|
||||
});
|
||||
|
||||
test('item get conditions with conditions', function () {
|
||||
$itemCondition1 = new \Wearepixel\Cart\CartCondition([
|
||||
'name' => 'SALE 5%',
|
||||
'type' => 'sale',
|
||||
'target' => 'item',
|
||||
'value' => '-5%',
|
||||
]);
|
||||
|
||||
$itemCondition2 = new CartCondition([
|
||||
'name' => 'Item Gift Pack 25.00',
|
||||
'type' => 'promo',
|
||||
'target' => 'item',
|
||||
'value' => '-25',
|
||||
]);
|
||||
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, [], [$itemCondition1, $itemCondition2]);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->getConditions())->toHaveCount(2, 'Item should have two conditions');
|
||||
});
|
||||
|
||||
test('item associate model', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, [])->associate(MockProduct::class);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->associatedModel)->toEqual(MockProduct::class, 'Item assocaited model should be ' . MockProduct::class);
|
||||
});
|
||||
|
||||
test('it will throw an exception when a non existing model is being associated', function () {
|
||||
$this->expectException(\Wearepixel\Cart\Exceptions\UnknownModelException::class);
|
||||
$this->expectExceptionMessage('The supplied model SomeModel does not exist.');
|
||||
|
||||
$this->cart->add(1, 'Test item', 1, 10.00)->associate('SomeModel');
|
||||
});
|
||||
|
||||
test('item get model', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, [])->associate(MockProduct::class);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->model)->toBeInstanceOf(MockProduct::class);
|
||||
expect($item->model->name)->toEqual('Sample Item');
|
||||
expect($item->model->id)->toEqual(455);
|
||||
});
|
||||
|
||||
test('item get model will return null if it has no model', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, []);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->model)->toEqual(null);
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Wearepixel\Cart\Cart;
|
||||
use Wearepixel\Cart\Tests\Helpers\SessionMock;
|
||||
|
||||
beforeEach(function () {
|
||||
$events = Mockery::mock('Illuminate\Contracts\Events\Dispatcher');
|
||||
$events->shouldReceive('dispatch');
|
||||
|
||||
$this->cart = new Cart(
|
||||
new SessionMock,
|
||||
$events,
|
||||
'shopping',
|
||||
'SAMPLESESSIONKEY',
|
||||
require (__DIR__ . '/../Helpers/ConfigMockOtherFormat.php')
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
test('item get sum price using property', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, []);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->getPriceSum())->toEqual('201.98', 'Item summed price should be 201.98');
|
||||
});
|
||||
|
||||
test('item get sum price using array style', function () {
|
||||
$this->cart->add(455, 'Sample Item', 100.99, 2, []);
|
||||
|
||||
$item = $this->cart->get(455);
|
||||
|
||||
expect($item->getPriceSum())->toEqual('201.98', 'Item summed price should be 201.98');
|
||||
});
|
||||
Reference in New Issue
Block a user