Complex classes like ProductContext often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ProductContext, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
43 | final class ProductContext implements Context |
||
44 | { |
||
45 | /** @var SharedStorageInterface */ |
||
46 | private $sharedStorage; |
||
47 | |||
48 | /** @var ProductRepositoryInterface */ |
||
49 | private $productRepository; |
||
50 | |||
51 | /** @var ProductFactoryInterface */ |
||
52 | private $productFactory; |
||
53 | |||
54 | /** @var FactoryInterface */ |
||
55 | private $productTranslationFactory; |
||
56 | |||
57 | /** @var FactoryInterface */ |
||
58 | private $productVariantFactory; |
||
59 | |||
60 | /** @var FactoryInterface */ |
||
61 | private $productVariantTranslationFactory; |
||
62 | |||
63 | /** @var FactoryInterface */ |
||
64 | private $channelPricingFactory; |
||
65 | |||
66 | /** @var FactoryInterface */ |
||
67 | private $productOptionFactory; |
||
68 | |||
69 | /** @var FactoryInterface */ |
||
70 | private $productOptionValueFactory; |
||
71 | |||
72 | /** @var FactoryInterface */ |
||
73 | private $productImageFactory; |
||
74 | |||
75 | /** @var ObjectManager */ |
||
76 | private $objectManager; |
||
77 | |||
78 | /** @var ProductVariantGeneratorInterface */ |
||
79 | private $productVariantGenerator; |
||
80 | |||
81 | /** @var ProductVariantResolverInterface */ |
||
82 | private $defaultVariantResolver; |
||
83 | |||
84 | /** @var ImageUploaderInterface */ |
||
85 | private $imageUploader; |
||
86 | |||
87 | /** @var SlugGeneratorInterface */ |
||
88 | private $slugGenerator; |
||
89 | |||
90 | /** @var array */ |
||
91 | private $minkParameters; |
||
92 | |||
93 | public function __construct( |
||
128 | |||
129 | /** |
||
130 | * @Given the store has a product :productName |
||
131 | * @Given the store has a :productName product |
||
132 | * @Given I added a product :productName |
||
133 | * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+")$/ |
||
134 | * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") in ("[^"]+" channel)$/ |
||
135 | */ |
||
136 | public function storeHasAProductPricedAt($productName, int $price = 100, ChannelInterface $channel = null) |
||
142 | |||
143 | /** |
||
144 | * @Given /^(this product) is(?:| also) priced at ("[^"]+") in ("[^"]+" channel)$/ |
||
145 | */ |
||
146 | public function thisProductIsAlsoPricedAtInChannel(ProductInterface $product, int $price, ChannelInterface $channel) |
||
156 | |||
157 | /** |
||
158 | * @Given /^(this product) is(?:| also) available in ("[^"]+" channel)$/ |
||
159 | */ |
||
160 | public function thisProductIsAlsoAvailableInChannel(ProductInterface $product, ChannelInterface $channel): void |
||
164 | |||
165 | /** |
||
166 | * @Given the store( also) has a product :productName with code :code |
||
167 | * @Given the store( also) has a product :productName with code :code, created at :date |
||
168 | */ |
||
169 | public function storeHasProductWithCode($productName, $code, $date = 'now') |
||
177 | |||
178 | /** |
||
179 | * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") available in (channel "[^"]+") and (channel "[^"]+")$/ |
||
180 | */ |
||
181 | public function storeHasAProductPricedAtAvailableInChannels($productName, int $price = 100, ...$channels) |
||
196 | |||
197 | /** |
||
198 | * @Given /^(this product) is named "([^"]+)" (in the "([^"]+)" locale)$/ |
||
199 | * @Given /^the (product "[^"]+") is named "([^"]+)" (in the "([^"]+)" locale)$/ |
||
200 | */ |
||
201 | public function thisProductIsNamedIn(ProductInterface $product, $name, $locale) |
||
207 | |||
208 | /** |
||
209 | * @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/ |
||
210 | */ |
||
211 | public function theStoreHasProductNamedInAndIn($firstName, $firstLocale, $secondName, $secondLocale) |
||
222 | |||
223 | /** |
||
224 | * @Given /^the store has(?:| a| an) "([^"]+)" configurable product$/ |
||
225 | * @Given /^the store has(?:| a| an) "([^"]+)" configurable product with "([^"]+)" slug$/ |
||
226 | */ |
||
227 | public function storeHasAConfigurableProduct($productName, $slug = null) |
||
253 | |||
254 | /** |
||
255 | * @Given the store has( also) :firstProductName and :secondProductName products |
||
256 | * @Given the store has( also) :firstProductName, :secondProductName and :thirdProductName products |
||
257 | * @Given the store has( also) :firstProductName, :secondProductName, :thirdProductName and :fourthProductName products |
||
258 | */ |
||
259 | public function theStoreHasProducts(...$productsNames) |
||
265 | |||
266 | /** |
||
267 | * @Given /^(this channel) has "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" products$/ |
||
268 | */ |
||
269 | public function thisChannelHasProducts(ChannelInterface $channel, ...$productsNames) |
||
277 | |||
278 | /** |
||
279 | * @Given /^the (product "[^"]+") has(?:| a) "([^"]+)" variant priced at ("[^"]+")$/ |
||
280 | * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+")$/ |
||
281 | * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") in ("([^"]+)" channel)$/ |
||
282 | */ |
||
283 | public function theProductHasVariantPricedAt( |
||
297 | |||
298 | /** |
||
299 | * @Given /^the (product "[^"]+") has(?:| a| an) "([^"]+)" variant$/ |
||
300 | * @Given /^(this product) has(?:| a| an) "([^"]+)" variant$/ |
||
301 | * @Given /^(this product) has "([^"]+)", "([^"]+)" and "([^"]+)" variants$/ |
||
302 | */ |
||
303 | public function theProductHasVariants(ProductInterface $product, ...$variantNames) |
||
317 | |||
318 | /** |
||
319 | * @Given /^the (product "[^"]+")(?:| also) has a nameless variant with code "([^"]+)"$/ |
||
320 | * @Given /^(this product)(?:| also) has a nameless variant with code "([^"]+)"$/ |
||
321 | * @Given /^(it)(?:| also) has a nameless variant with code "([^"]+)"$/ |
||
322 | */ |
||
323 | public function theProductHasNamelessVariantWithCode(ProductInterface $product, $variantCode) |
||
329 | |||
330 | /** |
||
331 | * @Given /^the (product "[^"]+")(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/ |
||
332 | * @Given /^(this product)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/ |
||
333 | * @Given /^(it)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/ |
||
334 | */ |
||
335 | public function theProductHasVariantWithCode(ProductInterface $product, $variantName, $variantCode) |
||
341 | |||
342 | /** |
||
343 | * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") which does not require shipping$/ |
||
344 | */ |
||
345 | public function theProductHasVariantWhichDoesNotRequireShipping( |
||
360 | |||
361 | /** |
||
362 | * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant$/ |
||
363 | * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/ |
||
364 | * @Given /^(this product) has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/ |
||
365 | */ |
||
366 | public function theProductHasVariantAtPosition( |
||
380 | |||
381 | /** |
||
382 | * @Given /^(this variant) is also priced at ("[^"]+") in ("([^"]+)" channel)$/ |
||
383 | */ |
||
384 | public function thisVariantIsAlsoPricedAtInChannel(ProductVariantInterface $productVariant, string $price, ChannelInterface $channel) |
||
393 | |||
394 | /** |
||
395 | * @Given /^(it|this product) has(?:| also) variant named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/ |
||
396 | */ |
||
397 | public function itHasVariantNamedInAndIn(ProductInterface $product, $firstName, $firstLocale, $secondName, $secondLocale) |
||
414 | |||
415 | /** |
||
416 | * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") identified by "([^"]+)"$/ |
||
417 | */ |
||
418 | public function theProductHasVariantPricedAtIdentifiedBy( |
||
426 | |||
427 | /** |
||
428 | * @Given /^(this product) only variant was renamed to "([^"]+)"$/ |
||
429 | */ |
||
430 | public function productOnlyVariantWasRenamed(ProductInterface $product, $variantName) |
||
440 | |||
441 | /** |
||
442 | * @Given /^there is product "([^"]+)" available in ((?:this|that|"[^"]+") channel)$/ |
||
443 | * @Given /^the store has a product "([^"]+)" available in ("([^"]+)" channel)$/ |
||
444 | */ |
||
445 | public function thereIsProductAvailableInGivenChannel($productName, ChannelInterface $channel) |
||
451 | |||
452 | /** |
||
453 | * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/ |
||
454 | */ |
||
455 | public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory) |
||
463 | |||
464 | /** |
||
465 | * @Given /^(it) comes in the following variations:$/ |
||
466 | */ |
||
467 | public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table) |
||
488 | |||
489 | /** |
||
490 | * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/ |
||
491 | */ |
||
492 | public function productVariantBelongsToTaxCategory( |
||
501 | |||
502 | /** |
||
503 | * @Given /^(this product) has option "([^"]+)" with values "([^"]+)" and "([^"]+)"$/ |
||
504 | * @Given /^(this product) has option "([^"]+)" with values "([^"]+)", "([^"]+)" and "([^"]+)"$/ |
||
505 | */ |
||
506 | public function thisProductHasOptionWithValues(ProductInterface $product, $optionName, ...$values): void |
||
510 | |||
511 | /** |
||
512 | * @Given /^(this product) has an option "([^"]*)" without any values$/ |
||
513 | */ |
||
514 | public function thisProductHasAnOptionWithoutAnyValues(ProductInterface $product, string $optionName): void |
||
518 | |||
519 | /** |
||
520 | * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/ |
||
521 | */ |
||
522 | public function thereIsQuantityOfProducts($quantity, ProductInterface $product) |
||
530 | |||
531 | /** |
||
532 | * @Given /^the (product "([^"]+)") is out of stock$/ |
||
533 | */ |
||
534 | public function theProductIsOutOfStock(ProductInterface $product) |
||
543 | |||
544 | /** |
||
545 | * @When other customer has bought :quantity :product products by this time |
||
546 | */ |
||
547 | public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product) |
||
555 | |||
556 | /** |
||
557 | * @Given /^(this product) is tracked by the inventory$/ |
||
558 | * @Given /^(?:|the )("[^"]+" product) is(?:| also) tracked by the inventory$/ |
||
559 | */ |
||
560 | public function thisProductIsTrackedByTheInventory(ProductInterface $product) |
||
568 | |||
569 | /** |
||
570 | * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/ |
||
571 | */ |
||
572 | public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, int $price) |
||
587 | |||
588 | /** |
||
589 | * @Given the :product product's :optionValueName size belongs to :shippingCategory shipping category |
||
590 | */ |
||
591 | public function thisProductSizeBelongsToShippingCategory(ProductInterface $product, $optionValueName, ShippingCategoryInterface $shippingCategory) |
||
604 | |||
605 | /** |
||
606 | * @Given /^(this product) has (this product option)$/ |
||
607 | * @Given /^(this product) has (?:a|an) ("[^"]+" option)$/ |
||
608 | */ |
||
609 | public function thisProductHasThisProductOption(ProductInterface $product, ProductOptionInterface $option) |
||
615 | |||
616 | /** |
||
617 | * @Given /^(this product) has all possible variants$/ |
||
618 | */ |
||
619 | public function thisProductHasAllPossibleVariants(ProductInterface $product) |
||
648 | |||
649 | /** |
||
650 | * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/ |
||
651 | */ |
||
652 | public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant) |
||
659 | |||
660 | /** |
||
661 | * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/ |
||
662 | */ |
||
663 | public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant) |
||
669 | |||
670 | /** |
||
671 | * @Given /^(this product)'s price is ("[^"]+")$/ |
||
672 | * @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/ |
||
673 | * @Given /^(this product) price has been changed to ("[^"]+")$/ |
||
674 | */ |
||
675 | public function theProductChangedItsPriceTo(ProductInterface $product, int $price) |
||
684 | |||
685 | /** |
||
686 | * @Given /^(this product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/ |
||
687 | * @Given /^the ("[^"]+" product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/ |
||
688 | * @Given /^(it)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/ |
||
689 | */ |
||
690 | public function thisProductHasAnImageWithType(ProductInterface $product, $imagePath, $imageType) |
||
705 | |||
706 | /** |
||
707 | * @Given /^(this product) belongs to ("([^"]+)" shipping category)$/ |
||
708 | * @Given product :product shipping category has been changed to :shippingCategory |
||
709 | */ |
||
710 | public function thisProductBelongsToShippingCategory(ProductInterface $product, ShippingCategoryInterface $shippingCategory) |
||
715 | |||
716 | /** |
||
717 | * @Given /^(this product) has been disabled$/ |
||
718 | */ |
||
719 | public function thisProductHasBeenDisabled(ProductInterface $product) |
||
724 | |||
725 | /** |
||
726 | * @Given the product :product was renamed to :productName |
||
727 | */ |
||
728 | public function theProductWasRenamedTo(ProductInterface $product, string $productName): void |
||
734 | |||
735 | /** |
||
736 | * @Given /^(this product) does not require shipping$/ |
||
737 | */ |
||
738 | public function thisProductDoesNotRequireShipping(ProductInterface $product): void |
||
747 | |||
748 | private function getPriceFromString(string $price): int |
||
752 | |||
753 | /** |
||
754 | * @param string $productName |
||
755 | * |
||
756 | * @return ProductInterface |
||
757 | */ |
||
758 | private function createProduct($productName, int $price = 100, ChannelInterface $channel = null) |
||
795 | |||
796 | /** |
||
797 | * @param string $value |
||
798 | * @param string $code |
||
799 | * |
||
800 | * @return ProductOptionValueInterface |
||
801 | */ |
||
802 | private function addProductOption(ProductOptionInterface $option, $value, $code) |
||
815 | |||
816 | private function saveProduct(ProductInterface $product) |
||
821 | |||
822 | /** |
||
823 | * @param string $name |
||
824 | * |
||
825 | * @return NodeElement |
||
826 | */ |
||
827 | private function getParameter($name) |
||
831 | |||
832 | /** |
||
833 | * @param string $productVariantName |
||
834 | * @param int $price |
||
835 | * @param string $code |
||
836 | * @param ChannelInterface $channel |
||
|
|||
837 | * @param int $position |
||
838 | * @param bool $shippingRequired |
||
839 | * |
||
840 | * @return ProductVariantInterface |
||
841 | */ |
||
842 | private function createProductVariant( |
||
870 | |||
871 | /** |
||
872 | * @param string $name |
||
873 | * @param string $locale |
||
874 | */ |
||
875 | private function addProductTranslation(ProductInterface $product, $name, $locale) |
||
890 | |||
891 | /** |
||
892 | * @param string $name |
||
893 | * @param string $locale |
||
894 | */ |
||
895 | private function addProductVariantTranslation(ProductVariantInterface $productVariant, $name, $locale) |
||
904 | |||
905 | /** |
||
906 | * @return ChannelPricingInterface |
||
907 | */ |
||
908 | private function createChannelPricingForChannel(int $price, ChannelInterface $channel = null) |
||
917 | |||
918 | private function addOptionToProduct(ProductInterface $product, string $optionName, array $values): void |
||
939 | } |
||
940 |
This check looks for
@param
annotations where the type inferred by our type inference engine differs from the declared type.It makes a suggestion as to what type it considers more descriptive.
Most often this is a case of a parameter that can be null in addition to its declared types.