Complex classes like UpdateSimpleProductPage 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 UpdateSimpleProductPage, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class UpdateSimpleProductPage extends BaseUpdatePage implements UpdateSimpleProductPageInterface |
||
29 | { |
||
30 | use ChecksCodeImmutability; |
||
31 | |||
32 | /** |
||
33 | * {@inheritdoc} |
||
34 | */ |
||
35 | public function nameItIn($name, $localeCode) |
||
36 | { |
||
37 | $this->getDocument()->fillField( |
||
38 | sprintf('sylius_product_translations_%s_name', $localeCode), $name |
||
39 | ); |
||
40 | |||
41 | $this->waitForSlugGenerationIfNecessary(); |
||
42 | } |
||
43 | |||
44 | /** |
||
45 | * {@inheritdoc} |
||
46 | */ |
||
47 | public function specifyPrice($price) |
||
48 | { |
||
49 | $this->getDocument()->fillField('Price', $price); |
||
50 | } |
||
51 | |||
52 | /** |
||
53 | * {@inheritdoc} |
||
54 | */ |
||
55 | public function getAttributeValue($attribute) |
||
56 | { |
||
57 | $attributesTab = $this->getElement('tab', ['%name%' => 'attributes']); |
||
58 | if (!$attributesTab->hasClass('active')) { |
||
59 | $attributesTab->click(); |
||
60 | } |
||
61 | |||
62 | return $this->getElement('attribute', ['%attribute%' => $attribute])->getValue(); |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * {@inheritdoc} |
||
67 | */ |
||
68 | public function hasAttribute($attribute) |
||
69 | { |
||
70 | return null !== $this->getDocument()->find('css', sprintf('.attribute .label:contains("%s")', $attribute)); |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * {@inheritdoc} |
||
75 | */ |
||
76 | public function selectMainTaxon(TaxonInterface $taxon) |
||
77 | { |
||
78 | $this->openTaxonBookmarks(); |
||
79 | |||
80 | Assert::isInstanceOf($this->getDriver(), Selenium2Driver::class); |
||
81 | |||
82 | $this->getDriver()->executeScript(sprintf('$(\'input.search\').val(\'%s\')', $taxon->getName())); |
||
83 | $this->getElement('search')->click(); |
||
84 | $this->getElement('search')->waitFor(10, |
||
85 | function () { |
||
86 | return $this->hasElement('search_item_selected'); |
||
87 | }); |
||
88 | $itemSelected = $this->getElement('search_item_selected'); |
||
89 | $itemSelected->click(); |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * {@inheritdoc} |
||
94 | */ |
||
95 | public function isMainTaxonChosen($taxonName) |
||
96 | { |
||
97 | $this->openTaxonBookmarks(); |
||
98 | |||
99 | return $taxonName === $this->getDocument()->find('css', '.search > .text')->getText(); |
||
100 | } |
||
101 | |||
102 | public function disableTracking() |
||
103 | { |
||
104 | $this->getElement('tracked')->uncheck(); |
||
105 | } |
||
106 | |||
107 | public function enableTracking() |
||
108 | { |
||
109 | $this->getElement('tracked')->check(); |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * {@inheritdoc} |
||
114 | */ |
||
115 | public function isTracked() |
||
116 | { |
||
117 | return $this->getElement('tracked')->isChecked(); |
||
118 | } |
||
119 | |||
120 | /** |
||
121 | * {@inheritdoc} |
||
122 | */ |
||
123 | public function isImageWithCodeDisplayed($code) |
||
124 | { |
||
125 | $imageElement = $this->getImageElementByCode($code); |
||
126 | |||
127 | if (null === $imageElement) { |
||
128 | return false; |
||
129 | } |
||
130 | |||
131 | $imageUrl = $imageElement->find('css', 'img')->getAttribute('src'); |
||
132 | $this->getDriver()->visit($imageUrl); |
||
133 | $pageText = $this->getDocument()->getText(); |
||
134 | $this->getDriver()->back(); |
||
135 | |||
136 | return false === stripos($pageText, '404 Not Found'); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * {@inheritdoc} |
||
141 | */ |
||
142 | public function attachImage($path, $code = null) |
||
143 | { |
||
144 | $this->clickTabIfItsNotActive('media'); |
||
145 | |||
146 | $filesPath = $this->getParameter('files_path'); |
||
147 | |||
148 | $this->getDocument()->clickLink('Add'); |
||
149 | |||
150 | $imageForm = $this->getLastImageElement(); |
||
151 | if (null !== $code) { |
||
152 | $imageForm->fillField('Code', $code); |
||
153 | } |
||
154 | |||
155 | $imageForm->find('css', 'input[type="file"]')->attachFile($filesPath.$path); |
||
156 | } |
||
157 | |||
158 | /** |
||
159 | * {@inheritdoc} |
||
160 | */ |
||
161 | public function changeImageWithCode($code, $path) |
||
168 | |||
169 | /** |
||
170 | * {@inheritdoc} |
||
171 | */ |
||
172 | public function removeImageWithCode($code) |
||
179 | |||
180 | public function removeFirstImage() |
||
185 | |||
186 | /** |
||
187 | * {@inheritdoc} |
||
188 | */ |
||
189 | public function countImages() |
||
195 | |||
196 | /** |
||
197 | * {@inheritdoc} |
||
198 | */ |
||
199 | public function isImageCodeDisabled() |
||
203 | |||
204 | /** |
||
205 | * {@inheritdoc} |
||
206 | */ |
||
207 | public function isSlugReadOnly() |
||
208 | { |
||
209 | return 'readonly' === $this->getElement('slug')->getAttribute('readonly'); |
||
210 | } |
||
211 | |||
212 | /** |
||
213 | * {@inheritdoc} |
||
214 | */ |
||
215 | public function getValidationMessageForImage() |
||
228 | |||
229 | /** |
||
230 | * {@inheritdoc} |
||
231 | */ |
||
232 | public function associateProducts(ProductAssociationTypeInterface $productAssociationType, array $productsNames) |
||
257 | |||
258 | /** |
||
259 | * {@inheritdoc} |
||
260 | */ |
||
261 | public function hasAssociatedProduct($productName, ProductAssociationTypeInterface $productAssociationType) |
||
270 | |||
271 | /** |
||
272 | * {@inheritdoc} |
||
273 | */ |
||
274 | public function removeAssociatedProduct($productName, ProductAssociationTypeInterface $productAssociationType) |
||
287 | |||
288 | /** |
||
289 | * {@inheritdoc} |
||
290 | */ |
||
291 | public function getPricingConfigurationForChannelAndCurrencyCalculator(ChannelInterface $channel, CurrencyInterface $currency) |
||
297 | |||
298 | /** |
||
299 | * {@inheritdoc} |
||
300 | */ |
||
301 | protected function getCodeElement() |
||
305 | |||
306 | /** |
||
307 | * {@inheritdoc} |
||
308 | */ |
||
309 | protected function getDefinedElements() |
||
329 | |||
330 | private function openTaxonBookmarks() |
||
334 | |||
335 | /** |
||
336 | * @param string $tabName |
||
337 | */ |
||
338 | private function clickTabIfItsNotActive($tabName) |
||
345 | |||
346 | /** |
||
347 | * @param string $tabName |
||
348 | */ |
||
349 | private function clickTab($tabName) |
||
354 | |||
355 | /** |
||
356 | * @param string $code |
||
357 | * |
||
358 | * @return NodeElement |
||
359 | */ |
||
360 | private function getImageElementByCode($code) |
||
371 | |||
372 | /** |
||
373 | * @return NodeElement[] |
||
374 | */ |
||
375 | private function getImageElements() |
||
381 | |||
382 | /** |
||
383 | * @return NodeElement |
||
384 | */ |
||
385 | private function getLastImageElement() |
||
393 | |||
394 | /** |
||
395 | * @return NodeElement |
||
396 | */ |
||
397 | private function getFirstImageElement() |
||
405 | |||
406 | private function waitForSlugGenerationIfNecessary() |
||
422 | } |
||
423 |
Very long variable names usually make code harder to read. It is therefore recommended not to make variable names too verbose.