init commit :rocket

This commit is contained in:
Joel Male
2024-08-14 10:46:23 +10:00
commit 77fa8601b9
39 changed files with 9915 additions and 0 deletions
+17
View File
@@ -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}}
+25
View File
@@ -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>"
+40
View File
@@ -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
+5
View File
@@ -0,0 +1,5 @@
/vendor
.DS_Store
.idea
.phpunit.result.cache
READMETODO.md
+21
View File
@@ -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.
+491
View File
@@ -0,0 +1,491 @@
[![Latest Version on Packagist](https://img.shields.io/packagist/v/wearepixel/laravel-cart.svg?style=flat-square)](https://packagist.org/packages/wearepixel/laravel-cart)
[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/wearepixel/laravel-cart/tests.yml?branch=master&label=Tests)](https://github.com/wearepixel/laravel-cart/actions?query=workflow%3ATests+branch%3Amaster)
[![Total Downloads](https://img.shields.io/packagist/dt/wearepixel/laravel-cart.svg?style=flat-square)](https://packagist.org/packages/wearepixel/laravel-cart)
[![License](https://poser.pugx.org/wearepixel/laravel-cart/license.svg)](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.
+53
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+36
View File
@@ -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
View File
@@ -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>
+12
View File
@@ -0,0 +1,12 @@
{
"preset": "laravel",
"rules": {
"concat_space": {
"spacing": "one"
},
"class_attributes_separation": false,
"ordered_imports": {
"sort_algorithm": "length"
}
}
}
+856
View File
@@ -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;
}
}
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace Wearepixel\Cart;
use Illuminate\Support\Collection;
class CartCollection extends Collection
{
}
+248
View File
@@ -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());
}
}
}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace Wearepixel\Cart;
use Illuminate\Support\Collection;
class CartConditionCollection extends Collection {}
+69
View File
@@ -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 {}
+5
View File
@@ -0,0 +1,5 @@
<?php
namespace Wearepixel\Cart\Exceptions;
class InvalidItemException extends \Exception {}
+7
View File
@@ -0,0 +1,7 @@
<?php
namespace Wearepixel\Cart\Exceptions;
class UnknownModelException extends \Exception
{
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace Wearepixel\Cart\Facades;
use Illuminate\Support\Facades\Facade;
class CartFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'cart';
}
}
+65
View File
@@ -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;
}
}
}
+17
View File
@@ -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;
}
}
+137
View File
@@ -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 {}
+5
View File
@@ -0,0 +1,5 @@
<?php
namespace Wearepixel\Cart\Validators;
class CartItemValidator extends Validator {}
+53
View File
@@ -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);
}
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
return [
'format_numbers' => false,
'decimals' => 2,
'dec_point' => '.',
'thousands_sep' => ',',
];
+8
View File
@@ -0,0 +1,8 @@
<?php
return [
'format_numbers' => false,
'decimals' => 3,
'dec_point' => ',',
'thousands_sep' => '.',
];
+88
View File
@@ -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;
}
}
+23
View File
@@ -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;
}
}
+69
View File
@@ -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);
}
+10
View File
@@ -0,0 +1,10 @@
<?php
namespace Tests;
use PHPUnit\Framework\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
//
}
+862
View File
@@ -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');
});
+531
View File
@@ -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.');
});
+201
View File
@@ -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"');
});
+102
View File
@@ -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);
});
+37
View File
@@ -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');
});