Merge pull request #12 from wearepixel/feature/targets-to-activate-cart-conditions

Feature: Add targets to cart conditions
This commit is contained in:
Joel Male
2024-09-04 09:38:34 +10:00
committed by GitHub
7 changed files with 559 additions and 348 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
strategy:
fail-fast: true
matrix:
php: ['8.1','8.2', '8.3']
php: ['8.1', '8.2', '8.3']
stability: [prefer-stable]
steps:
+88 -7
View File
@@ -76,9 +76,9 @@ ### Round Mode
'round_mode' => env('LARAVEL_CART_ROUND_MODE', 'down'),
```
## HOW TO USE
## Usage
- [Usage](#usage)
- [Basic Usage](#basic-usage)
- [Conditions](#conditions)
- [Items](#items)
- [Associating Models](#associating-models)
@@ -288,12 +288,14 @@ ### Clearing the Cart: **Cart::clear()**
## Conditions
Conditions are very useful in terms for adding things like discounts, taxes, shipping costs, etc.
Conditions are very useful for adding things like discounts, taxes, shipping, etc.
Conditions can be added on the entire cart or on individual items.
Conditions can be added on the entire cart or on individual items, and can even be applied only at certain cart values.
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 provide an optional `minimum` value which should be the dollar value in which the target (subtotal or total) needs to be for the condition to be active and impact the cart.
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
@@ -362,18 +364,59 @@ #### Getting conditions on the cart: **Cart::getConditions()**
$condition->getType(); // the type
$condition->getValue(); // the value of the condition
$condition->getOrder(); // the order of the condition
$condition->getMinimum(); // the minimum dollar amount of the target, needed to activate the condition
$condition->getMaximum(); // the maximum dollar amount of the target, needed to keep the condition active
$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 = Cart::getCondition('GST');
$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->getMinimum(); // the minimum dollar amount of the target, needed to activate the condition
$condition->getMaximum(); // the maximum dollar amount of the target, needed to keep the condition active
$condition->getAttributes(); // the attributes of the condition, returns an empty [] if no attributes added
```
#### Getting active conditions on the cart: **Cart::getConditions(active: true)**
You can get only active conditions by passsing `active: true` to the `getConditions()` method.
This will return conditions that are actively being applied to the cart (i.e if they meet their minimum or maximum value)
```php
$tenPercentOff = new CartCondition([
'name' => '10% Off',
'type' => 'discount',
'target' => 'subtotal',
'value' => '-10%',
'minimum' => 120,
'order' => 1,
]);
Cart::getConditions(active: true);
// will return "10% Off" if the subtotal of the cart is $200.
// will return no conditions if the subtotal is $100.
$shipping = new CartCondition([
'name' => 'Shipping',
'type' => 'discount',
'target' => 'subtotal',
'value' => '10',
'maximum' => 200,
'order' => 1,
]);
Cart::getConditions(active: true);
// will return "Shipping" if the subtotal of the cart is less than or equal 200
// will return no conditions if the subtotal is $210
```
### Calculating condition value
There are 2 ways to calculate the value of a condition:
@@ -385,7 +428,7 @@ #### Using the `getCalculatedValue` method on the condition instance
```php
$subTotal = Cart::getSubTotal();
$condition = Cart::getCondition('VAT 12.5%');
$condition = Cart::getCondition('10% GST');
$conditionCalculatedValue = $condition->getCalculatedValue($subTotal);
```
@@ -422,6 +465,44 @@ #### Using the `getCalculatedValueForCondition` method on the cart instance
Cart::getCalculatedValueForCondition('Gift Card'); // returns 0 as the coupon discount is applied first and brings the subtotal to 0
```
### Adding conditions that activate once a minimum value is met
You can add a `minimum` amount required for a condition to activate.
This is useful for applying discounts only after certain cart values, i.e: 10% off for any purchases over $120.
```php
$tenPercentOff = new CartCondition([
'name' => '10% Off',
'type' => 'discount',
'target' => 'subtotal',
'value' => '-10%',
'minimum' => 120,
'order' => 1,
]);
Cart::condition($tenPercentOff)
```
### Adding conditions that only activate up to a maximum value
You can add a `maximum` amount required for a condition to activate.
This is useful for applying discounts up until amounts, i.e shipping for anything below $200, and free shipping above.
```php
$shipping = new CartCondition([
'name' => 'Shipping',
'type' => 'discount',
'target' => 'subtotal',
'value' => '12',
'maximum' => 200,
'order' => 1,
]);
Cart::condition($shipping)
```
### 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.
@@ -483,7 +564,7 @@ ### Condition on items
$cartSubTotal = Cart::getSubTotal();
```
#### Add condition to existing Item on the cart: **Cart::addItemCondition($productId, $itemCondition)**
#### Add a condition to an existing item on the cart: **Cart::addItemCondition($productId, $itemCondition)**
Adding Condition to an existing Item on the cart is simple as well.
+2 -2
View File
@@ -26,9 +26,9 @@
},
"require-dev": {
"mockery/mockery": "^1.4.2",
"laravel/pint": "dev-main",
"laravel/pint": "^1.17",
"pestphp/pest": "2.35.1",
"larapack/dd": "dev-master"
"larapack/dd": "1.1"
},
"autoload": {
"psr-4": {
Generated
+140 -141
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "726c11e0474359fea45e3d6790b14426",
"content-hash": "bf8d3b76695f06430eaf04e5eeacb58c",
"packages": [
{
"name": "brick/math",
@@ -378,12 +378,12 @@
"source": {
"type": "git",
"url": "https://github.com/illuminate/collections.git",
"reference": "dc68a7ccad93f3c2baa0bc8f559431c06391aa75"
"reference": "66d2c9bdf5641599735d402c1c504b54734a9cca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/collections/zipball/dc68a7ccad93f3c2baa0bc8f559431c06391aa75",
"reference": "dc68a7ccad93f3c2baa0bc8f559431c06391aa75",
"url": "https://api.github.com/repos/illuminate/collections/zipball/66d2c9bdf5641599735d402c1c504b54734a9cca",
"reference": "66d2c9bdf5641599735d402c1c504b54734a9cca",
"shasum": ""
},
"require": {
@@ -425,7 +425,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-08-01T18:54:27+00:00"
"time": "2024-08-23T18:49:36+00:00"
},
{
"name": "illuminate/conditionable",
@@ -479,12 +479,12 @@
"source": {
"type": "git",
"url": "https://github.com/illuminate/container.git",
"reference": "f47be671981a4438257c4fbfc3ad257f4e3e929a"
"reference": "85e3396fde3139eae3f37128222c9a7746ca4bbb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/container/zipball/f47be671981a4438257c4fbfc3ad257f4e3e929a",
"reference": "f47be671981a4438257c4fbfc3ad257f4e3e929a",
"url": "https://api.github.com/repos/illuminate/container/zipball/85e3396fde3139eae3f37128222c9a7746ca4bbb",
"reference": "85e3396fde3139eae3f37128222c9a7746ca4bbb",
"shasum": ""
},
"require": {
@@ -522,7 +522,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-08-05T15:04:01+00:00"
"time": "2024-08-21T16:01:30+00:00"
},
{
"name": "illuminate/contracts",
@@ -530,12 +530,12 @@
"source": {
"type": "git",
"url": "https://github.com/illuminate/contracts.git",
"reference": "34ead9385e0eab7e947807d77da66faf9bdf95ff"
"reference": "af9b459f195d57f279ec30a45446ddaeb3337bcb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/contracts/zipball/34ead9385e0eab7e947807d77da66faf9bdf95ff",
"reference": "34ead9385e0eab7e947807d77da66faf9bdf95ff",
"url": "https://api.github.com/repos/illuminate/contracts/zipball/af9b459f195d57f279ec30a45446ddaeb3337bcb",
"reference": "af9b459f195d57f279ec30a45446ddaeb3337bcb",
"shasum": ""
},
"require": {
@@ -570,7 +570,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-08-01T19:08:33+00:00"
"time": "2024-08-21T16:02:16+00:00"
},
{
"name": "illuminate/filesystem",
@@ -578,12 +578,12 @@
"source": {
"type": "git",
"url": "https://github.com/illuminate/filesystem.git",
"reference": "43b8d2af61dd6ca882e192fa4ca803d2294d4507"
"reference": "7e44057ac57635465c91c3fc6247c5d87bd1feed"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/filesystem/zipball/43b8d2af61dd6ca882e192fa4ca803d2294d4507",
"reference": "43b8d2af61dd6ca882e192fa4ca803d2294d4507",
"url": "https://api.github.com/repos/illuminate/filesystem/zipball/7e44057ac57635465c91c3fc6247c5d87bd1feed",
"reference": "7e44057ac57635465c91c3fc6247c5d87bd1feed",
"shasum": ""
},
"require": {
@@ -637,7 +637,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-06-28T20:10:30+00:00"
"time": "2024-09-03T13:20:49+00:00"
},
{
"name": "illuminate/macroable",
@@ -691,12 +691,12 @@
"source": {
"type": "git",
"url": "https://github.com/illuminate/support.git",
"reference": "089c989beac69bd2633466d72d6ed07068b623ac"
"reference": "51efc0516526413b4be641a1731d15d4394de2ff"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/illuminate/support/zipball/089c989beac69bd2633466d72d6ed07068b623ac",
"reference": "089c989beac69bd2633466d72d6ed07068b623ac",
"url": "https://api.github.com/repos/illuminate/support/zipball/51efc0516526413b4be641a1731d15d4394de2ff",
"reference": "51efc0516526413b4be641a1731d15d4394de2ff",
"shasum": ""
},
"require": {
@@ -757,7 +757,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2024-08-07T14:43:54+00:00"
"time": "2024-09-03T13:21:20+00:00"
},
{
"name": "illuminate/translation",
@@ -877,12 +877,12 @@
"source": {
"type": "git",
"url": "https://github.com/briannesbitt/Carbon.git",
"reference": "cb4374784c87d0a0294e8513a52eb63c0aff3139"
"reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cb4374784c87d0a0294e8513a52eb63c0aff3139",
"reference": "cb4374784c87d0a0294e8513a52eb63c0aff3139",
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/bbd3eef89af8ba66a3aa7952b5439168fbcc529f",
"reference": "bbd3eef89af8ba66a3aa7952b5439168fbcc529f",
"shasum": ""
},
"require": {
@@ -976,7 +976,7 @@
"type": "tidelift"
}
],
"time": "2024-07-16T22:29:20+00:00"
"time": "2024-08-19T06:22:39+00:00"
},
{
"name": "psr/clock",
@@ -1206,18 +1206,86 @@
],
"time": "2024-08-06T10:13:52+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"default-branch": true,
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/finder",
"version": "7.2.x-dev",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "0470b8dc10ca08d23aeaabbed5b961c928e4d89d"
"reference": "afa87bce0d224a744963ecc8db07b1f0f96a3518"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/0470b8dc10ca08d23aeaabbed5b961c928e4d89d",
"reference": "0470b8dc10ca08d23aeaabbed5b961c928e4d89d",
"url": "https://api.github.com/repos/symfony/finder/zipball/afa87bce0d224a744963ecc8db07b1f0f96a3518",
"reference": "afa87bce0d224a744963ecc8db07b1f0f96a3518",
"shasum": ""
},
"require": {
@@ -1268,7 +1336,7 @@
"type": "tidelift"
}
],
"time": "2024-08-13T14:29:53+00:00"
"time": "2024-09-03T13:22:29+00:00"
},
{
"name": "symfony/http-foundation",
@@ -1276,12 +1344,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "79d0023c23d92d49c43be47c726e92366b4d3a81"
"reference": "5a92e38016224d35a68ded60297b94d87b21a494"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/79d0023c23d92d49c43be47c726e92366b4d3a81",
"reference": "79d0023c23d92d49c43be47c726e92366b4d3a81",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/5a92e38016224d35a68ded60297b94d87b21a494",
"reference": "5a92e38016224d35a68ded60297b94d87b21a494",
"shasum": ""
},
"require": {
@@ -1345,7 +1413,7 @@
"type": "tidelift"
}
],
"time": "2024-08-13T12:32:31+00:00"
"time": "2024-09-03T14:09:19+00:00"
},
{
"name": "symfony/mime",
@@ -1836,16 +1904,17 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "a50ed69ace45368e096bc3981f456089253ed6cb"
"reference": "a1c4be3a5885b9785b131c5a30d93e611848cb1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/a50ed69ace45368e096bc3981f456089253ed6cb",
"reference": "a50ed69ace45368e096bc3981f456089253ed6cb",
"url": "https://api.github.com/repos/symfony/translation/zipball/a1c4be3a5885b9785b131c5a30d93e611848cb1d",
"reference": "a1c4be3a5885b9785b131c5a30d93e611848cb1d",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-mbstring": "~1.0",
"symfony/translation-contracts": "^2.5|^3.0"
},
@@ -1922,7 +1991,7 @@
"type": "tidelift"
}
],
"time": "2024-08-05T08:22:14+00:00"
"time": "2024-08-19T08:28:32+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -2223,16 +2292,16 @@
},
{
"name": "fidry/cpu-core-counter",
"version": "1.1.0",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/theofidry/cpu-core-counter.git",
"reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42"
"reference": "8520451a140d3f46ac33042715115e290cf5785f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42",
"reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42",
"url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f",
"reference": "8520451a140d3f46ac33042715115e290cf5785f",
"shasum": ""
},
"require": {
@@ -2272,7 +2341,7 @@
],
"support": {
"issues": "https://github.com/theofidry/cpu-core-counter/issues",
"source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0"
"source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0"
},
"funding": [
{
@@ -2280,7 +2349,7 @@
"type": "github"
}
],
"time": "2024-02-07T09:43:46+00:00"
"time": "2024-08-06T10:04:20+00:00"
},
{
"name": "filp/whoops",
@@ -2508,16 +2577,16 @@
},
{
"name": "laravel/pint",
"version": "dev-main",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "e24a6aa83b23c0b64ab3486d68fbc88ab266e03f"
"reference": "e0a8cef58b74662f27355be9cdea0e726bbac362"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/e24a6aa83b23c0b64ab3486d68fbc88ab266e03f",
"reference": "e24a6aa83b23c0b64ab3486d68fbc88ab266e03f",
"url": "https://api.github.com/repos/laravel/pint/zipball/e0a8cef58b74662f27355be9cdea0e726bbac362",
"reference": "e0a8cef58b74662f27355be9cdea0e726bbac362",
"shasum": ""
},
"require": {
@@ -2525,18 +2594,17 @@
"ext-mbstring": "*",
"ext-tokenizer": "*",
"ext-xml": "*",
"php": "^8.1.0"
"php": "^8.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.61.1",
"illuminate/view": "^10.48.18",
"larastan/larastan": "^2.9.8",
"laravel-zero/framework": "^10.4.0",
"mockery/mockery": "^1.6.12",
"friendsofphp/php-cs-fixer": "^3.14.4",
"illuminate/view": "^9.51.0",
"laravel-zero/framework": "^9.2.0",
"mockery/mockery": "^1.5.1",
"nunomaduro/larastan": "^2.4.0",
"nunomaduro/termwind": "^1.15.1",
"pestphp/pest": "^2.35.0"
"pestphp/pest": "^1.22.4"
},
"default-branch": true,
"bin": [
"builds/pint"
],
@@ -2571,7 +2639,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2024-08-06T15:12:57+00:00"
"time": "2023-02-14T16:31:02+00:00"
},
{
"name": "mockery/mockery",
@@ -3510,16 +3578,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.29.1",
"version": "1.30.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4"
"reference": "5ceb0e384997db59f38774bf79c2a6134252c08f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4",
"reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/5ceb0e384997db59f38774bf79c2a6134252c08f",
"reference": "5ceb0e384997db59f38774bf79c2a6134252c08f",
"shasum": ""
},
"require": {
@@ -3551,9 +3619,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.0"
},
"time": "2024-05-31T08:52:43+00:00"
"time": "2024-08-29T09:54:52+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -4959,12 +5027,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "cc2dd55c49e04b10cec4aa6e47e099a2d1656845"
"reference": "ee7ee22b1dabcff731b7d79d3633c96251ad8404"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/cc2dd55c49e04b10cec4aa6e47e099a2d1656845",
"reference": "cc2dd55c49e04b10cec4aa6e47e099a2d1656845",
"url": "https://api.github.com/repos/symfony/console/zipball/ee7ee22b1dabcff731b7d79d3633c96251ad8404",
"reference": "ee7ee22b1dabcff731b7d79d3633c96251ad8404",
"shasum": ""
},
"require": {
@@ -5044,75 +5112,7 @@
"type": "tidelift"
}
],
"time": "2024-08-15T22:49:18+00:00"
},
{
"name": "symfony/deprecation-contracts",
"version": "dev-main",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"default-branch": true,
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-04-18T09:32:20+00:00"
"time": "2024-08-30T15:40:32+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -5511,12 +5511,12 @@
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "122ba59267fc82caa8f1f509332d4b94a223013b"
"reference": "f74e6c4199357f1b566a67bbe834ebd7de658839"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/122ba59267fc82caa8f1f509332d4b94a223013b",
"reference": "122ba59267fc82caa8f1f509332d4b94a223013b",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/f74e6c4199357f1b566a67bbe834ebd7de658839",
"reference": "f74e6c4199357f1b566a67bbe834ebd7de658839",
"shasum": ""
},
"require": {
@@ -5532,7 +5532,7 @@
"symfony/http-kernel": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/uid": "^6.4|^7.0",
"twig/twig": "^3.0.4"
"twig/twig": "^3.12|^4.0"
},
"bin": [
"Resources/bin/var-dump-server"
@@ -5586,7 +5586,7 @@
"type": "tidelift"
}
],
"time": "2024-08-13T14:29:53+00:00"
"time": "2024-09-02T15:30:47+00:00"
},
{
"name": "ta-tikoma/phpunit-architecture-test",
@@ -5759,13 +5759,12 @@
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {
"laravel/pint": 20,
"larapack/dd": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=8.1"
"php": ">=8.0"
},
"platform-dev": [],
"plugin-api-version": "2.6.0"
+47 -65
View File
@@ -10,59 +10,22 @@
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;
@@ -73,6 +36,7 @@ public function __construct($session, $events, $instanceName, $session_key, $con
$this->sessionKeyCartConditions = $this->sessionKey . '_cart_conditions';
$this->config = $config;
$this->currentItemId = null;
$this->fireEvent('created');
}
@@ -109,20 +73,16 @@ public function getInstanceName()
/**
* get an item on a cart by item ID
*
* @return mixed
*/
public function get($itemId)
public function get($itemId): ?ItemCollection
{
return $this->getContent()->get($itemId);
}
/**
* check if an item exists by item ID
*
* @return bool
* check if we have this item in the cart by item ID
*/
public function has($itemId)
public function has($itemId): bool
{
return $this->getContent()->has($itemId);
}
@@ -144,10 +104,9 @@ public function add(
array|CartCondition $conditions = [],
?string $associatedModel = null
): self {
// check if its an array of items
// check if we are adding an array of items
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
// check if we are adding a multi dimensional array
if (Helpers::isMultiArray($id)) {
foreach ($id as $item) {
$this->add(
@@ -175,6 +134,7 @@ public function add(
return $this;
}
// if we are here, we are adding a single item
$data = [
'id' => $id,
'name' => $name,
@@ -191,11 +151,8 @@ public function add(
// validate data
$item = $this->validate($data);
// get the cart
$inCart = $this->get($id);
// if the item is already in the cart we will just update it
if ($inCart) {
if ($this->get($id)) {
$this->update($id, $item);
} else {
$this->addItem($id, $item);
@@ -337,6 +294,9 @@ public function clear()
return true;
}
/**
* Adds a condition to the cart
*/
/**
* Adds a condition to the cart
*
@@ -378,11 +338,27 @@ public function condition(array|CartCondition $condition): self
/**
* get conditions applied on the cart
*
* @return CartConditionCollection
* @param bool $active - If true, only conditions actually applied on the cart are returned
*/
public function getConditions()
public function getConditions(bool $active = false): CartConditionCollection
{
return new CartConditionCollection($this->session->get($this->sessionKeyCartConditions));
$conditions = new CartConditionCollection(
$this->session->get($this->sessionKeyCartConditions)
);
if ($active) {
return $conditions->filter(function (CartCondition $condition) {
$amount = $condition->getTarget() == 'subtotal' ? $this->getSubTotal(false) : $this->getTotal();
return $condition->getMinimum() === null || $condition->getMinimum() <= $amount;
})->filter(function (CartCondition $condition) {
$amount = $condition->getTarget() == 'subtotal' ? $this->getSubTotal(false) : $this->getTotal();
return $condition->getMaximum() === null || $condition->getMaximum() >= $amount;
});
}
return $conditions;
}
/**
@@ -411,12 +387,8 @@ public function getConditionsByType($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)
public function removeConditionsByType(string $type)
{
$this->getConditionsByType($type)->each(function ($condition) {
$this->removeCartCondition($condition->getName());
@@ -424,12 +396,7 @@ public function removeConditionsByType($type)
}
/**
* 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
* Remove a condition from the cart
*/
public function removeCartCondition($conditionName)
{
@@ -575,6 +542,12 @@ public function getSubTotal($formatted = true): int|float|string
->filter(function (CartCondition $condition) {
return $condition->getTarget() === 'subtotal';
})
->filter(function (CartCondition $condition) use ($subTotalSum) {
return $condition->getMinimum() === null || $condition->getMinimum() <= $subTotalSum;
})
->filter(function (CartCondition $condition) use ($subTotalSum) {
return $condition->getMaximum() === null || $condition->getMaximum() >= $subTotalSum;
})
->sortBy(function (CartCondition $condition) {
return $condition->getOrder();
});
@@ -607,6 +580,15 @@ public function getTotal(): int|float
$conditionsForTotal = $this->getConditions()
->filter(function (CartCondition $condition) {
return $condition->getTarget() === 'total';
})
->filter(function (CartCondition $condition) use ($subTotal) {
return $condition->getMinimum() === null || $condition->getMinimum() <= $subTotal;
})
->filter(function (CartCondition $condition) use ($subTotal) {
return $condition->getMaximum() === null || $condition->getMaximum() >= $subTotal;
})
->sortBy(function (CartCondition $condition) {
return $condition->getOrder();
});
// if no conditions were added, just return the sub total
+16
View File
@@ -65,6 +65,22 @@ public function getType()
return $this->args['type'];
}
/**
* Get the minimum value of this condition
*/
public function getMinimum(): int|float|null
{
return $this->args['minimum'] ?? null;
}
/**
* Get the maximum value of this condition
*/
public function getMaximum(): int|float|null
{
return $this->args['maximum'] ?? null;
}
/**
* get the additional attributes of a condition
*
+265 -132
View File
@@ -792,40 +792,6 @@
expect($giftCard->getCalculatedValue())->toEqual(0, 'Gift Card value should be 0');
});
test('can be cleared', function () {
$siteWideDiscount = new CartCondition([
'name' => 'Site Wide Discount',
'type' => 'discount',
'target' => 'total',
'value' => '-5%',
]);
$giftCard = new CartCondition([
'name' => 'Gift Card',
'type' => 'gift_card',
'target' => 'total',
'value' => '-25',
]);
$item = [
'id' => 1,
'name' => 'Backpack',
'price' => 168.72,
'quantity' => 1,
'attributes' => [],
];
$this->cart->add($item);
$this->cart->condition([$siteWideDiscount, $giftCard]);
expect($this->cart->getConditions()->count())->toEqual(2, 'Cart should have two conditions');
$this->cart->clearCartConditions();
expect($this->cart->getConditions()->count())->toEqual(0, 'Cart should have no conditions now');
});
test('calculate the subtotal correctly when one condition makes it $0, and the other adds $10', function () {
$this->cart->add([
[
@@ -915,6 +881,214 @@
expect($this->cart->getSubTotal())->toEqual(10.00, 'Cart should have sub total of 10.00');
expect($this->cart->getTotal())->toEqual(10.00, 'Cart should have a total of 10.00');
});
test('can activate a dollar discount when a minimum amount is met', function () {
$condition = new CartCondition([
'name' => 'Get $10 off when you spend $100',
'type' => 'target',
'target' => 'total',
'value' => '-10',
'minimum' => '100',
]);
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 1,
'attributes' => [],
]);
$this->cart->condition($condition);
expect($this->cart->getTotal())->toEqual(90, 'Cart should have total of 90');
$this->cart->clear();
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 2,
'attributes' => [],
]);
expect($this->cart->getTotal())->toEqual(170, 'Cart should have total of 170');
});
test('can activate a percentage discount when a minimum amount is met', function () {
$condition = new CartCondition([
'name' => 'Get 10% off when you spend $100',
'type' => 'target',
'target' => 'total',
'value' => '-10%',
'minimum' => '100',
]);
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 1,
'attributes' => [],
]);
$this->cart->condition($condition);
expect($this->cart->getTotal())->toEqual(90, 'Cart should have total of 90');
$this->cart->clear();
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 2,
'attributes' => [],
]);
expect($this->cart->getTotal())->toEqual(162, 'Cart should have total of 162');
});
test('can remove a dollar value condition when a maximum is met', function () {
$condition = new CartCondition([
'name' => 'Shipping',
'type' => 'target',
'target' => 'total',
'value' => '18',
'maximum' => '160',
]);
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 1,
'attributes' => [],
]);
$this->cart->condition($condition);
expect($this->cart->getTotal())->toEqual(108, 'Cart should have total of 108');
$this->cart->clear();
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 2,
'attributes' => [],
]);
expect($this->cart->getTotal())->toEqual(180, 'Cart should have total of 180');
});
test('can remove a percentage condition when a maximum is met', function () {
$condition = new CartCondition([
'name' => 'Low Cart Tax',
'type' => 'target',
'target' => 'total',
'value' => '10%',
'maximum' => '140',
]);
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 1,
'attributes' => [],
]);
$this->cart->condition($condition);
expect($this->cart->getTotal())->toEqual(99, 'Cart should have total of 99');
$this->cart->clear();
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 2,
'attributes' => [],
]);
expect($this->cart->getTotal())->toEqual(180, 'Cart should have total of 180');
});
test('return cart conditions only when they are active', function () {
$condition = new CartCondition([
'name' => 'Get $10 off when you spend $100',
'type' => 'target',
'target' => 'total',
'value' => '-10',
'minimum' => '100',
]);
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 1,
'attributes' => [],
]);
$this->cart->condition($condition);
expect($this->cart->getConditions(active: true))->toHaveCount(0, 'Cart should have 0 conditions');
$this->cart->clear();
$this->cart->add([
'id' => 1,
'name' => 'Apple iPhone 15',
'price' => 90,
'quantity' => 2,
'attributes' => [],
]);
expect($this->cart->getConditions(active: true))->toHaveCount(1, 'Cart should have 1 condition');
});
test('can be removed by condition name', function () {
$shipping = new CartCondition([
'name' => 'Shipping',
'type' => 'shipping',
'target' => 'total',
'value' => '12',
]);
$tax = new CartCondition([
'name' => 'Tax',
'type' => 'tax',
'target' => 'total',
'value' => '2.50',
]);
$item = [
'id' => 456,
'name' => 'Sample Item 1',
'price' => 100,
'quantity' => 1,
'attributes' => [],
];
$this->cart->add($item);
$this->cart->condition([$shipping, $tax]);
// 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('Tax');
// 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('Shipping');
});
});
describe('item level conditions', function () {
@@ -1082,110 +1256,36 @@
expect($this->cart->get(1)->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',
// ]);
test('can be removed by condition name', function () {
$heavyItemSurcharge = new CartCondition([
'name' => 'Heavy Item Surcharge',
'type' => 'surcharge',
'value' => '10',
]);
// $item = [
// 'id' => 456,
// 'name' => 'Sample Item 1',
// 'price' => 100,
// 'quantity' => 1,
// 'attributes' => [],
// ];
$tax = new CartCondition([
'name' => 'Tax',
'type' => 'tax',
'value' => '10%',
]);
// $this->cart->add($item);
$item = [
'id' => 12,
'name' => 'Beer (1 box)',
'price' => 410.60,
'quantity' => 1,
'attributes' => [],
'conditions' => [$heavyItemSurcharge, $tax],
];
// $this->cart->condition([$itemCondition1, $itemCondition2]);
$this->cart->add($item);
// // get a condition applied on cart by condition name
// $condition = $this->cart->getCondition($itemCondition1->getName());
expect($this->cart->get(12)['conditions'])->toHaveCount(2, 'Item should have two conditions');
// expect('SALE 5%')->toEqual($condition->getName());
// expect('total')->toEqual($condition->getTarget());
// expect('sale')->toEqual($condition->getType());
// expect('-5%')->toEqual($condition->getValue());
// });
$this->cart->removeItemCondition(12, 'Tax');
// 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');
// });
expect($this->cart->get(12)['conditions'])->toHaveCount(1, 'Item should have one condition');
});
// test('remove item condition by condition name scenario two', function () {
// // NOTE: in this scenario, we will add the conditions not in array format
@@ -1602,4 +1702,37 @@
expect($this->cart->getConditions()->count())->toEqual(0, 'Cart should have no conditions now');
expect($this->cart->get(1)['conditions'])->toHaveCount(0, 'Cart items should have no conditions now');
});
// 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');
// });
});