-
Notifications
You must be signed in to change notification settings - Fork 11
HP-2419 extended functional of BillingRegistry for easier use #99
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
a9e3868
055c135
02f0958
d7937d3
8fad103
d993cd0
405f064
d7b0e2a
f48a47a
3cf9e05
f062de0
be55965
0856451
167436f
27bec10
8eba047
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace hiqdev\php\billing\product\Application; | ||
|
||
use hiqdev\php\billing\product\AggregateInterface; | ||
use hiqdev\php\billing\product\behavior\BehaviorInterface; | ||
use hiqdev\php\billing\product\behavior\BehaviorNotFoundException; | ||
use hiqdev\php\billing\product\behavior\InvalidBehaviorException; | ||
use hiqdev\php\billing\product\BillingRegistryInterface; | ||
use hiqdev\php\billing\product\Exception\AggregateNotFoundException; | ||
use hiqdev\php\billing\product\Exception\TariffTypeDefinitionNotFoundException; | ||
use hiqdev\php\billing\product\invoice\InvalidRepresentationException; | ||
use hiqdev\php\billing\product\invoice\RepresentationInterface; | ||
use hiqdev\php\billing\product\price\PriceTypeDefinitionInterface; | ||
use hiqdev\php\billing\product\quantity\FractionQuantityData; | ||
use hiqdev\php\billing\product\quantity\QuantityFormatterInterface; | ||
use hiqdev\php\billing\product\quantity\QuantityFormatterNotFoundException; | ||
use hiqdev\php\billing\product\TariffTypeDefinitionInterface; | ||
use hiqdev\php\billing\type\Type; | ||
use hiqdev\php\billing\type\TypeInterface; | ||
|
||
final class BillingRegistryService implements BillingRegistryServiceInterface | ||
{ | ||
public function __construct(private readonly BillingRegistryInterface $registry) | ||
{ | ||
} | ||
|
||
public function getRepresentationsByType(string $representationClass): array | ||
{ | ||
if (!class_exists($representationClass)) { | ||
throw new InvalidRepresentationException("Class '$representationClass' does not exist"); | ||
} | ||
|
||
if (!is_subclass_of($representationClass, RepresentationInterface::class)) { | ||
throw new InvalidBehaviorException( | ||
sprintf('Representation class "%s" does not implement RepresentationInterface', $representationClass) | ||
); | ||
} | ||
|
||
$representations = []; | ||
foreach ($this->registry->priceTypes() as $priceTypeDefinition) { | ||
foreach ($priceTypeDefinition->documentRepresentation() as $representation) { | ||
if ($representation instanceof $representationClass) { | ||
$representations[] = $representation; | ||
} | ||
} | ||
} | ||
|
||
return $representations; | ||
} | ||
|
||
public function createQuantityFormatter(string $type, FractionQuantityData $data): QuantityFormatterInterface { | ||
$type = $this->convertStringTypeToType($type); | ||
|
||
foreach ($this->registry->priceTypes() as $priceTypeDefinition) { | ||
if ($priceTypeDefinition->hasType($type)) { | ||
return $priceTypeDefinition->createQuantityFormatter($data); | ||
} | ||
} | ||
|
||
throw new QuantityFormatterNotFoundException('Quantity formatter not found'); | ||
} | ||
|
||
private function convertStringTypeToType(string $type): TypeInterface | ||
{ | ||
return Type::anyId($type); | ||
} | ||
|
||
public function getBehavior(string $type, string $behaviorClassWrapper): BehaviorInterface | ||
{ | ||
if (!class_exists($behaviorClassWrapper)) { | ||
throw new InvalidBehaviorException( | ||
sprintf('Behavior class "%s" does not exist', $behaviorClassWrapper) | ||
); | ||
} | ||
|
||
if (!is_subclass_of($behaviorClassWrapper, BehaviorInterface::class)) { | ||
throw new InvalidBehaviorException( | ||
sprintf('Behavior class "%s" does not implement BehaviorInterface', $behaviorClassWrapper) | ||
); | ||
} | ||
Comment on lines
+71
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Validation logic duplicated & missing in sibling methods Excellent job validating Extract the validation into a small private helper and reuse it. |
||
|
||
$billingType = $this->convertStringTypeToType($type); | ||
|
||
foreach ($this->registry->priceTypes() as $priceTypeDefinition) { | ||
if ($priceTypeDefinition->hasType($billingType)) { | ||
$behavior = $this->findBehaviorInPriceType($priceTypeDefinition, $behaviorClassWrapper); | ||
|
||
if ($behavior) { | ||
return $behavior; | ||
} | ||
} | ||
} | ||
|
||
throw new BehaviorNotFoundException( | ||
sprintf('Behavior of class "%s" not found for type "%s"', $behaviorClassWrapper, $type), | ||
); | ||
} | ||
|
||
private function findBehaviorInPriceType( | ||
PriceTypeDefinitionInterface $priceTypeDefinition, | ||
string $behaviorClassWrapper | ||
): ?BehaviorInterface { | ||
foreach ($priceTypeDefinition->withBehaviors() as $behavior) { | ||
if ($behavior instanceof $behaviorClassWrapper) { | ||
return $behavior; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
public function getBehaviors(string $behaviorClassWrapper): \Generator | ||
{ | ||
foreach ($this->registry->getTariffTypeDefinitions() as $tariffTypeDefinition) { | ||
foreach ($tariffTypeDefinition->withBehaviors() as $behavior) { | ||
if ($behavior instanceof $behaviorClassWrapper) { | ||
yield $behavior; | ||
} | ||
} | ||
} | ||
|
||
foreach ($this->registry->priceTypes() as $priceTypeDefinition) { | ||
foreach ($priceTypeDefinition->withBehaviors() as $behavior) { | ||
if ($behavior instanceof $behaviorClassWrapper) { | ||
yield $behavior; | ||
} | ||
} | ||
} | ||
} | ||
Comment on lines
+113
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add class existence / interface checks to prevent fatal errors See previous comment. Before the two nested loops, add: if (!class_exists($behaviorClassWrapper) ||
!is_subclass_of($behaviorClassWrapper, BehaviorInterface::class)) {
throw new InvalidBehaviorException(
sprintf('Behavior class "%s" is invalid or does not implement %s',
$behaviorClassWrapper,
BehaviorInterface::class
)
);
} |
||
|
||
public function getAggregate(string $type): AggregateInterface | ||
{ | ||
$type = $this->convertStringTypeToType($type); | ||
|
||
foreach ($this->registry->priceTypes() as $priceTypeDefinition) { | ||
if ($priceTypeDefinition->hasType($type)) { | ||
return $priceTypeDefinition->getAggregate(); | ||
} | ||
} | ||
|
||
throw new AggregateNotFoundException('Aggregate was not found'); | ||
} | ||
|
||
public function findTariffTypeDefinitionByBehavior(BehaviorInterface $behavior): TariffTypeDefinitionInterface | ||
{ | ||
$tariffType = $behavior->getTariffType(); | ||
|
||
foreach ($this->registry->getTariffTypeDefinitions() as $tariffTypeDefinition) { | ||
if ($tariffTypeDefinition->belongToTariffType($tariffType)) { | ||
return $tariffTypeDefinition; | ||
} | ||
} | ||
|
||
throw new TariffTypeDefinitionNotFoundException('Tariff type definition was not found'); | ||
} | ||
|
||
public function findPriceTypeDefinitionsByBehavior(string $behaviorClassWrapper): \Generator | ||
{ | ||
foreach ($this->registry->priceTypes() as $priceTypeDefinition) { | ||
if ($priceTypeDefinition->hasBehavior($behaviorClassWrapper)) { | ||
yield $priceTypeDefinition; | ||
} | ||
} | ||
} | ||
Comment on lines
+158
to
+165
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Same validation gap as in Add the |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace hiqdev\php\billing\product\Application; | ||
|
||
use Generator; | ||
use hiqdev\php\billing\product\AggregateInterface; | ||
use hiqdev\php\billing\product\behavior\BehaviorInterface; | ||
use hiqdev\php\billing\product\behavior\BehaviorNotFoundException; | ||
use hiqdev\php\billing\product\behavior\InvalidBehaviorException; | ||
use hiqdev\php\billing\product\invoice\RepresentationInterface; | ||
use hiqdev\php\billing\product\price\PriceTypeDefinitionInterface; | ||
use hiqdev\php\billing\product\quantity\FractionQuantityData; | ||
use hiqdev\php\billing\product\quantity\QuantityFormatterInterface; | ||
use hiqdev\php\billing\product\TariffTypeDefinitionInterface; | ||
|
||
interface BillingRegistryServiceInterface | ||
{ | ||
/** | ||
* @param string $representationClass | ||
* @return RepresentationInterface[] | ||
*/ | ||
public function getRepresentationsByType(string $representationClass): array; | ||
|
||
public function createQuantityFormatter(string $type, FractionQuantityData $data): QuantityFormatterInterface; | ||
|
||
/** | ||
* @param string $type - full type like 'overuse,lb_capacity_unit' | ||
* @param string $behaviorClassWrapper | ||
* @return BehaviorInterface | ||
* @throws BehaviorNotFoundException | ||
* @throws InvalidBehaviorException | ||
*/ | ||
public function getBehavior(string $type, string $behaviorClassWrapper): BehaviorInterface; | ||
|
||
/** | ||
* Find all behaviors attached to any TariffType or PriceType by specified Behavior class. | ||
* | ||
* @param string $behaviorClassWrapper | ||
* @return Generator<BehaviorInterface> | ||
*/ | ||
public function getBehaviors(string $behaviorClassWrapper): Generator; | ||
|
||
public function getAggregate(string $type): AggregateInterface; | ||
|
||
public function findTariffTypeDefinitionByBehavior(BehaviorInterface $behavior): TariffTypeDefinitionInterface; | ||
|
||
/** | ||
* Find all PriceTypeDefinition in registry by specified Behavior class. | ||
* | ||
* @param string $behaviorClassWrapper | ||
* @return Generator<PriceTypeDefinitionInterface> | ||
*/ | ||
public function findPriceTypeDefinitionsByBehavior(string $behaviorClassWrapper): Generator; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect exception type for representation-interface validation
When the provided class is not a
RepresentationInterface
,InvalidBehaviorException
is thrown.That is confusing, because the error is about representation misuse, not behavior misuse, and clients relying on the more specific
InvalidRepresentationException
will silently miss it.📝 Committable suggestion