From aff7240c165a47b7ddad4c5ac8749534ac9e6fe8 Mon Sep 17 00:00:00 2001 From: Attila Fulop <1162360+fulopattila122@users.noreply.github.com> Date: Mon, 15 Jul 2024 17:41:30 +0300 Subject: [PATCH] Made `PromotionRuleType` `Schematized` instead of `Configurable` - Upgrade Xtend to v1.2 - code relies on its `getIdOf()` method --- composer.json | 2 +- src/Promotion/Contracts/Promotion.php | 2 +- src/Promotion/Contracts/PromotionRule.php | 2 +- src/Promotion/Contracts/PromotionRuleType.php | 15 ++---- src/Promotion/Models/Promotion.php | 13 +++-- src/Promotion/Models/PromotionRule.php | 14 +++-- src/Promotion/PromotionRuleTypes.php | 6 +-- .../Providers/ModuleServiceProvider.php | 2 +- src/Promotion/Rules/CartQuantity.php | 51 +++++-------------- .../Tests/Examples/CartTotalRule.php | 18 ++----- src/Promotion/Tests/Examples/DummyCart.php | 7 ++- src/Promotion/Tests/Examples/NthOrderRule.php | 18 ++----- src/Promotion/Tests/PromotionRuleTest.php | 12 ++--- src/Promotion/Tests/PromotionTest.php | 4 +- .../Tests/Rules/CartQuantityTest.php | 19 +++---- src/Promotion/composer.json | 2 +- 16 files changed, 75 insertions(+), 112 deletions(-) diff --git a/composer.json b/composer.json index 1040a6a7..bb48f4fd 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "laravel/framework": "^10.43|^11.0", "konekt/enum": "^4.2", "konekt/concord": "^1.13", - "konekt/xtend": "^1.1", + "konekt/xtend": "^1.2", "spatie/laravel-medialibrary": "^11.0", "cviebrock/eloquent-sluggable": "^10.0|^11.0", "konekt/laravel-migration-compatibility": "^1.6", diff --git a/src/Promotion/Contracts/Promotion.php b/src/Promotion/Contracts/Promotion.php index af949681..d4649370 100644 --- a/src/Promotion/Contracts/Promotion.php +++ b/src/Promotion/Contracts/Promotion.php @@ -18,5 +18,5 @@ interface Promotion { public function isValid(?\DateTimeInterface $at = null): bool; - public function addRule(PromotionRuleType $ruleType): self; + public function addRule(PromotionRuleType|string $type, array $configuration): self; } diff --git a/src/Promotion/Contracts/PromotionRule.php b/src/Promotion/Contracts/PromotionRule.php index 8c0d7419..b322a3eb 100644 --- a/src/Promotion/Contracts/PromotionRule.php +++ b/src/Promotion/Contracts/PromotionRule.php @@ -10,5 +10,5 @@ interface PromotionRule extends Configurable { public function getRuleType(): PromotionRuleType; - public function isRuleTypPassing(object $subject): bool; + public function isPassing(object $subject): bool; } diff --git a/src/Promotion/Contracts/PromotionRuleType.php b/src/Promotion/Contracts/PromotionRuleType.php index d4739c17..45445901 100644 --- a/src/Promotion/Contracts/PromotionRuleType.php +++ b/src/Promotion/Contracts/PromotionRuleType.php @@ -4,19 +4,12 @@ namespace Vanilo\Promotion\Contracts; -use Nette\Schema\Schema; +use Konekt\Extend\Contracts\Registerable; +use Vanilo\Contracts\Schematized; -interface PromotionRuleType +interface PromotionRuleType extends Schematized, Registerable { public static function getName(): string; - public static function getID(): string; - - public function isPassing(object $subject): bool; - - public function getSchema(): ?Schema; - - public function setConfiguration(array $configuration): self; - - public function getConfiguration(): ?array; + public function isPassing(object $subject, array $configuration): bool; } diff --git a/src/Promotion/Models/Promotion.php b/src/Promotion/Models/Promotion.php index d4e02f38..8c8314fc 100644 --- a/src/Promotion/Models/Promotion.php +++ b/src/Promotion/Models/Promotion.php @@ -10,6 +10,7 @@ use Illuminate\Support\Collection; use Vanilo\Promotion\Contracts\Promotion as PromotionContract; use Vanilo\Promotion\Contracts\PromotionRuleType; +use Vanilo\Promotion\PromotionRuleTypes; /** * @property int $id @@ -70,11 +71,17 @@ public function isValid(?\DateTimeInterface $at = null): bool return $this->ends_at->isFuture(); } - public function addRule(PromotionRuleType $ruleType): PromotionContract + public function addRule(PromotionRuleType|string $type, array $configuration): self { + $typeId = match (true) { + $type instanceof PromotionRuleType => PromotionRuleTypes::getIdOf($type::class), // $type is an object + null !== PromotionRuleTypes::getClassOf($type) => $type, // $type is the registered type ID + default => PromotionRuleTypes::getIdOf($type), // $type is the class name of the rule type + }; + $this->rules()->create([ - 'type' => $ruleType::getID(), - 'configuration' => $ruleType->getConfiguration(), + 'type' => $typeId, + 'configuration' => $configuration, ]); return $this; diff --git a/src/Promotion/Models/PromotionRule.php b/src/Promotion/Models/PromotionRule.php index b7886ec0..a1cd84b9 100644 --- a/src/Promotion/Models/PromotionRule.php +++ b/src/Promotion/Models/PromotionRule.php @@ -6,10 +6,12 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Vanilo\Contracts\Schematized; use Vanilo\Promotion\Contracts\Promotion; use Vanilo\Promotion\Contracts\PromotionRule as PromotionRuleContract; use Vanilo\Promotion\Contracts\PromotionRuleType; use Vanilo\Promotion\PromotionRuleTypes; +use Vanilo\Support\Dto\SchemaDefinition; use Vanilo\Support\Traits\ConfigurableModel; use Vanilo\Support\Traits\ConfigurationHasNoSchema; @@ -24,7 +26,6 @@ class PromotionRule extends Model implements PromotionRuleContract { use ConfigurableModel; - use ConfigurationHasNoSchema; protected $guarded = ['id', 'created_at', 'updated_at']; @@ -39,11 +40,16 @@ public function promotion(): BelongsTo public function getRuleType(): PromotionRuleType { - return PromotionRuleTypes::make($this->type)->setConfiguration($this->configuration); + return PromotionRuleTypes::make($this->type); } - public function isRuleTypPassing(object $subject): bool + public function isPassing(object $subject): bool { - return $this->getRuleType()->isPassing($subject); + return $this->getRuleType()->isPassing($subject, $this->configuration()); + } + + public function getConfigurationSchema(): ?Schematized + { + return SchemaDefinition::wrap($this->getRuleType()); } } diff --git a/src/Promotion/PromotionRuleTypes.php b/src/Promotion/PromotionRuleTypes.php index 39ee46a6..f3883d99 100644 --- a/src/Promotion/PromotionRuleTypes.php +++ b/src/Promotion/PromotionRuleTypes.php @@ -24,14 +24,14 @@ public static function register(string $id, string $class) public static function make(string $id, array $parameters = []): PromotionRuleType { - $gwClass = self::getClassOf($id); + $class = self::getClassOf($id); - if (null === $gwClass) { + if (null === $class) { throw new InexistentPromotionRuleException( "No rule is registered with the id `$id`." ); } - return app()->make($gwClass, $parameters); + return app()->make($class, $parameters); } } diff --git a/src/Promotion/Providers/ModuleServiceProvider.php b/src/Promotion/Providers/ModuleServiceProvider.php index c4033066..455807ba 100644 --- a/src/Promotion/Providers/ModuleServiceProvider.php +++ b/src/Promotion/Providers/ModuleServiceProvider.php @@ -25,6 +25,6 @@ public function boot() { parent::boot(); - PromotionRuleTypes::register(CartQuantity::getID(), CartQuantity::class); + PromotionRuleTypes::register(CartQuantity::ID, CartQuantity::class); } } diff --git a/src/Promotion/Rules/CartQuantity.php b/src/Promotion/Rules/CartQuantity.php index c21353c5..65ac39ab 100644 --- a/src/Promotion/Rules/CartQuantity.php +++ b/src/Promotion/Rules/CartQuantity.php @@ -7,60 +7,37 @@ use Nette\Schema\Expect; use Nette\Schema\Processor; use Nette\Schema\Schema; -use Vanilo\Cart\Contracts\Cart; use Vanilo\Promotion\Contracts\PromotionRuleType; class CartQuantity implements PromotionRuleType { - private ?array $configuration = null; + public const ID = 'cart_quantity'; public static function getName(): string { - return __('Cart quantity'); + return __('Cart Quantity'); } - public static function getID(): string + public function getSchema(): Schema { - return 'cart_quantity'; + return Expect::structure(['count' => Expect::int(0)->required()])->castTo('array'); } - public function setConfiguration(array $configuration): self + public function getSchemaSample(array $mergeWith = null): array { - if ($this->getSchema()) { - $configuration = (new Processor())->process($this->getSchema(), $configuration); - } - - $this->configuration = (array) $configuration; - - return $this; - } - - public function getConfiguration(): ?array - { - $configuration = $this->configuration; - - if ($this->getSchema()) { - $configuration = (new Processor())->process($this->getSchema(), $configuration); - } - - return (array) $configuration; - } - - public function getSchema(): ?Schema - { - return Expect::structure(['count' => Expect::int(0)->required()]); + return ['count' => 2]; } - public function isPassing(object $subject): bool + public function isPassing(object $subject, array $configuration): bool { - if (!$subject instanceof Cart) { - throw new \InvalidArgumentException('Subject must be an instance of Vanilo\Cart\Contracts\Cart'); - } + $count = match(true) { + method_exists($subject, 'itemCount') => $subject->itemCount(), + method_exists($subject, 'getItems') => count($subject->getItems()), + default => throw new \InvalidArgumentException('The cart quantity promotion rule requires either `itemCount()` or `getItems()` method on its subject'), + }; - if (!$this->getConfiguration()) { - return false; - } + $configuration = (new Processor())->process($this->getSchema(), $configuration); - return $subject->itemCount() <= $this->configuration['count']; + return $count >= $configuration['count']; } } diff --git a/src/Promotion/Tests/Examples/CartTotalRule.php b/src/Promotion/Tests/Examples/CartTotalRule.php index b1c6d558..9eff510b 100644 --- a/src/Promotion/Tests/Examples/CartTotalRule.php +++ b/src/Promotion/Tests/Examples/CartTotalRule.php @@ -14,28 +14,18 @@ public static function getName(): string return 'Cart Total'; } - public static function getID(): string - { - // TODO: Implement getID() method. - } - - public function isPassing(object $subject): bool + public function isPassing(object $subject, array $configuration): bool { // TODO: Implement isPassing() method. } - public function getSchema(): ?Schema + public function getSchema(): Schema { // TODO: Implement getSchema() method. } - public function setConfiguration(array $configuration): PromotionRuleType - { - // TODO: Implement setConfiguration() method. - } - - public function getConfiguration(): ?array + public function getSchemaSample(array $mergeWith = null): array { - // TODO: Implement getConfiguration() method. + // TODO: Implement getSchemaSample() method. } } diff --git a/src/Promotion/Tests/Examples/DummyCart.php b/src/Promotion/Tests/Examples/DummyCart.php index ba846fc4..166d4a83 100644 --- a/src/Promotion/Tests/Examples/DummyCart.php +++ b/src/Promotion/Tests/Examples/DummyCart.php @@ -12,6 +12,11 @@ class DummyCart implements Cart { + public function __construct( + private int $itemCount = 5 + ) { + } + public function addItem(Buyable $product, float|int $qty = 1, array $params = []): CartItem { // TODO: Implement addItem() method. @@ -34,7 +39,7 @@ public function clear(): void public function itemCount(): int { - return 5; + return $this->itemCount; } public function getUser(): ?Authenticatable diff --git a/src/Promotion/Tests/Examples/NthOrderRule.php b/src/Promotion/Tests/Examples/NthOrderRule.php index dd314cba..7cdbb0ef 100644 --- a/src/Promotion/Tests/Examples/NthOrderRule.php +++ b/src/Promotion/Tests/Examples/NthOrderRule.php @@ -14,28 +14,18 @@ public static function getName(): string return 'Nth Order'; } - public static function getID(): string - { - // TODO: Implement getID() method. - } - - public function isPassing(object $subject): bool + public function isPassing(object $subject, array $configuration): bool { // TODO: Implement isPassing() method. } - public function getSchema(): ?Schema + public function getSchema(): Schema { // TODO: Implement getSchema() method. } - public function setConfiguration(array $configuration): PromotionRuleType - { - // TODO: Implement setConfiguration() method. - } - - public function getConfiguration(): ?array + public function getSchemaSample(array $mergeWith = null): array { - // TODO: Implement getConfiguration() method. + // TODO: Implement getSchemaSample() method. } } diff --git a/src/Promotion/Tests/PromotionRuleTest.php b/src/Promotion/Tests/PromotionRuleTest.php index a93dfc2f..09affe33 100644 --- a/src/Promotion/Tests/PromotionRuleTest.php +++ b/src/Promotion/Tests/PromotionRuleTest.php @@ -44,7 +44,7 @@ public function it_can_run_the_type_passing() { $ruleA = PromotionRule::create( [ - 'type' => CartQuantity::getID(), + 'type' => CartQuantity::ID, 'promotion_id' => PromotionFactory::new()->create()->id, 'configuration' => ['count' => 10], ] @@ -52,17 +52,17 @@ public function it_can_run_the_type_passing() $ruleB = PromotionRule::create( [ - 'type' => CartQuantity::getID(), + 'type' => CartQuantity::ID, 'promotion_id' => PromotionFactory::new()->create()->id, 'configuration' => ['count' => 3], ] ); $this->assertEquals(['count' => 10], $ruleA->configuration()); - $this->assertTrue($ruleA->isRuleTypPassing(new DummyCart())); + $this->assertFalse($ruleA->isPassing(new DummyCart())); $this->assertEquals(['count' => 3], $ruleB->configuration()); - $this->assertFalse($ruleB->isRuleTypPassing(new DummyCart())); + $this->assertTrue($ruleB->isPassing(new DummyCart())); } /** @test */ @@ -71,9 +71,9 @@ public function throws_exception_if_configuration_needed_but_its_not_there() $this->expectException(ValidationException::class); $rule = PromotionRule::create( - ['type' => CartQuantity::getID(), 'promotion_id' => PromotionFactory::new()->create()->id] + ['type' => CartQuantity::ID, 'promotion_id' => PromotionFactory::new()->create()->id] ); - $rule->isRuleTypPassing(new DummyCart()); + $rule->isPassing(new DummyCart()); } } diff --git a/src/Promotion/Tests/PromotionTest.php b/src/Promotion/Tests/PromotionTest.php index 33d48d6b..a2c8eaea 100644 --- a/src/Promotion/Tests/PromotionTest.php +++ b/src/Promotion/Tests/PromotionTest.php @@ -146,10 +146,10 @@ public function can_determine_if_its_valid() public function it_can_add_rule_and_validate() { $promotion = PromotionFactory::new()->create(); - $promotion->addRule(PromotionRuleTypes::make(CartQuantity::getID())->setConfiguration(['count' => 3])); + $promotion->addRule(PromotionRuleTypes::make(CartQuantity::ID), ['count' => 3]); $this->assertEquals(1, $promotion->rules()->count()); $this->assertEquals(['count' => 3], $promotion->rules()->first()->configuration); - $this->assertEquals(CartQuantity::getID(), $promotion->rules()->first()->type); + $this->assertEquals(CartQuantity::ID, $promotion->rules()->first()->type); } } diff --git a/src/Promotion/Tests/Rules/CartQuantityTest.php b/src/Promotion/Tests/Rules/CartQuantityTest.php index 38da0541..619e56b5 100644 --- a/src/Promotion/Tests/Rules/CartQuantityTest.php +++ b/src/Promotion/Tests/Rules/CartQuantityTest.php @@ -15,31 +15,26 @@ class CartQuantityTest extends TestCase /** @test */ public function can_be_created() { - PromotionRuleTypes::register(CartQuantity::getID(), CartQuantity::class); + $ruleType = PromotionRuleTypes::make(CartQuantity::ID); - $rule = PromotionRuleTypes::make(CartQuantity::getID()); - - $this->assertInstanceOf(CartQuantity::class, $rule); + $this->assertInstanceOf(CartQuantity::class, $ruleType); } /** @test */ public function throws_exception_if_configuration_is_wrong() { $this->expectException(ValidationException::class); - PromotionRuleTypes::register(CartQuantity::getID(), CartQuantity::class); - $rule = PromotionRuleTypes::make(CartQuantity::getID()); + $cartQuantityRule = PromotionRuleTypes::make(CartQuantity::ID); - $this->assertFalse($rule->isPassing(new DummyCart())); + $this->assertFalse($cartQuantityRule->isPassing(new DummyCart(), ['wrong' => 'config'])); } /** @test */ public function passes_if_rule_is_valid() { - PromotionRuleTypes::register(CartQuantity::getID(), CartQuantity::class); - $ruleA = PromotionRuleTypes::make(CartQuantity::getID())->setConfiguration(['count' => 3]); - $ruleB = PromotionRuleTypes::make(CartQuantity::getID())->setConfiguration(['count' => 6]); + $cartQuantityRuleType = PromotionRuleTypes::make(CartQuantity::ID); - $this->assertFalse($ruleA->isPassing(new DummyCart())); - $this->assertTrue($ruleB->isPassing(new DummyCart())); + $this->assertTrue($cartQuantityRuleType->isPassing(new DummyCart(4), ['count' => 3])); + $this->assertFalse($cartQuantityRuleType->isPassing(new DummyCart(6), ['count' => 7])); } } diff --git a/src/Promotion/composer.json b/src/Promotion/composer.json index e331b6b6..3ae65375 100644 --- a/src/Promotion/composer.json +++ b/src/Promotion/composer.json @@ -23,7 +23,7 @@ "require": { "php": "^8.2", "konekt/concord": "^1.13", - "konekt/xtend": "^1.1", + "konekt/xtend": "^1.2", "laravel/framework": "^10.43|^11.0", "vanilo/support": "^4.0", "vanilo/adjustments": "^4.0"