Completed
Push — 1.0-community-org ( 019488 )
by Kamil
24:49
created

ProductSpec   B

Complexity

Total Complexity 42

Size/Duplication

Total Lines 421
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 5

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 42
lcom 0
cbo 5
dl 0
loc 421
rs 8.295
c 2
b 1
f 0

42 Methods

Rating   Name   Duplication   Size   Complexity  
A let() 0 5 1
A it_implements_product_interface() 0 4 1
A it_implements_toggleable_interface() 0 4 1
A it_has_no_id_by_default() 0 4 1
A it_has_no_name_by_default() 0 4 1
A its_name_is_mutable() 0 5 1
A it_has_no_slug_by_default() 0 4 1
A its_slug_is_mutable() 0 5 1
A it_has_no_description_by_default() 0 4 1
A its_description_is_mutable() 0 5 1
A it_initializes_attribute_collection_by_default() 0 4 1
A it_adds_attribute() 0 7 1
A it_removes_attribute() 0 12 1
A it_refuses_to_add_non_product_attribute() 0 5 1
A it_refuses_to_remove_non_product_attribute() 0 4 1
B it_returns_attributes_by_a_locale_without_a_base_locale() 0 27 1
B it_returns_attributes_by_a_locale_with_a_base_locale() 0 35 1
A it_returns_attributes_by_a_fallback_locale_when_there_is_no_value_for_a_given_locale() 0 19 1
B it_returns_attributes_by_a_fallback_locale_when_there_is_an_empty_value_for_a_given_locale() 0 27 1
A it_returns_attributes_by_a_base_locale_when_there_is_no_value_for_a_given_locale_or_a_fallback_locale() 0 19 1
B it_returns_attributes_by_a_base_locale_when_there_is_an_empty_value_for_a_given_locale_or_a_fallback_locale() 0 35 1
A it_has_no_variants_by_default() 0 4 1
A its_says_it_has_variants_only_if_multiple_variants_are_defined() 0 11 1
A it_initializes_variants_collection_by_default() 0 4 1
A it_does_not_include_unavailable_variants_in_available_variants() 0 6 1
A it_returns_available_variants() 0 10 1
A it_initializes_options_collection_by_default() 0 4 1
A it_has_no_options_by_default() 0 4 1
A its_says_it_has_options_only_if_any_option_defined() 0 5 1
A it_adds_option_properly() 0 5 1
A it_removes_option_properly() 0 8 1
A it_initializes_creation_date_by_default() 0 4 1
A its_creation_date_is_mutable() 0 5 1
A it_has_no_last_update_date_by_default() 0 4 1
A its_last_update_date_is_mutable() 0 5 1
A it_is_enabled_by_default() 0 4 1
A it_is_toggleable() 0 8 1
A it_adds_association() 0 7 1
A it_allows_to_remove_association() 0 10 1
A it_is_simple_if_it_has_one_variant_and_no_options() 0 8 1
A it_is_configurable_if_it_has_at_least_two_variants() 0 12 1
A it_is_configurable_if_it_has_one_variant_and_at_least_one_option() 0 11 1

How to fix   Complexity   

Complex Class

Complex classes like ProductSpec 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 ProductSpec, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace spec\Sylius\Component\Product\Model;
15
16
use Doctrine\Common\Collections\Collection;
17
use PhpSpec\ObjectBehavior;
18
use Sylius\Component\Attribute\Model\AttributeValueInterface;
19
use Sylius\Component\Product\Model\ProductAssociationInterface;
20
use Sylius\Component\Product\Model\ProductAttributeInterface;
21
use Sylius\Component\Product\Model\ProductAttributeValueInterface;
22
use Sylius\Component\Product\Model\ProductInterface;
23
use Sylius\Component\Product\Model\ProductOptionInterface;
24
use Sylius\Component\Product\Model\ProductVariantInterface;
25
use Sylius\Component\Resource\Model\ToggleableInterface;
26
27
/**
28
 * @author Paweł Jędrzejewski <[email protected]>
29
 * @author Gonzalo Vilaseca <[email protected]>
30
 */
31
final class ProductSpec extends ObjectBehavior
32
{
33
    function let()
34
    {
35
        $this->setCurrentLocale('en_US');
36
        $this->setFallbackLocale('en_US');
37
    }
38
39
    function it_implements_product_interface(): void
40
    {
41
        $this->shouldImplement(ProductInterface::class);
42
    }
43
44
    function it_implements_toggleable_interface(): void
45
    {
46
        $this->shouldImplement(ToggleableInterface::class);
47
    }
48
49
    function it_has_no_id_by_default(): void
50
    {
51
        $this->getId()->shouldReturn(null);
52
    }
53
54
    function it_has_no_name_by_default(): void
55
    {
56
        $this->getName()->shouldReturn(null);
57
    }
58
59
    function its_name_is_mutable(): void
60
    {
61
        $this->setName('Super product');
62
        $this->getName()->shouldReturn('Super product');
63
    }
64
65
    function it_has_no_slug_by_default(): void
66
    {
67
        $this->getSlug()->shouldReturn(null);
68
    }
69
70
    function its_slug_is_mutable(): void
71
    {
72
        $this->setSlug('super-product');
73
        $this->getSlug()->shouldReturn('super-product');
74
    }
75
76
    function it_has_no_description_by_default(): void
77
    {
78
        $this->getDescription()->shouldReturn(null);
79
    }
80
81
    function its_description_is_mutable(): void
82
    {
83
        $this->setDescription('This product is super cool because...');
84
        $this->getDescription()->shouldReturn('This product is super cool because...');
85
    }
86
87
    function it_initializes_attribute_collection_by_default(): void
88
    {
89
        $this->getAttributes()->shouldHaveType(Collection::class);
90
    }
91
92
    function it_adds_attribute(ProductAttributeValueInterface $attribute): void
93
    {
94
        $attribute->setProduct($this)->shouldBeCalled();
95
96
        $this->addAttribute($attribute);
97
        $this->hasAttribute($attribute)->shouldReturn(true);
98
    }
99
100
    function it_removes_attribute(ProductAttributeValueInterface $attribute): void
101
    {
102
        $attribute->setProduct($this)->shouldBeCalled();
103
104
        $this->addAttribute($attribute);
105
        $this->hasAttribute($attribute)->shouldReturn(true);
106
107
        $attribute->setProduct(null)->shouldBeCalled();
108
109
        $this->removeAttribute($attribute);
110
        $this->hasAttribute($attribute)->shouldReturn(false);
111
    }
112
113
    function it_refuses_to_add_non_product_attribute(AttributeValueInterface $attribute): void
114
    {
115
        $this->shouldThrow('\InvalidArgumentException')->duringAddAttribute($attribute);
116
        $this->hasAttribute($attribute)->shouldReturn(false);
117
    }
118
119
    function it_refuses_to_remove_non_product_attribute(AttributeValueInterface $attribute): void
120
    {
121
        $this->shouldThrow('\InvalidArgumentException')->duringRemoveAttribute($attribute);
122
    }
123
124
    function it_returns_attributes_by_a_locale_without_a_base_locale(
125
        ProductAttributeInterface $attribute,
126
        ProductAttributeValueInterface $attributeValueEN,
127
        ProductAttributeValueInterface $attributeValuePL
128
    ): void {
129
        $attribute->getCode()->willReturn('colour');
130
131
        $attributeValueEN->setProduct($this)->shouldBeCalled();
132
        $attributeValueEN->getLocaleCode()->willReturn('en_US');
133
        $attributeValueEN->getAttribute()->willReturn($attribute);
134
        $attributeValueEN->getCode()->willReturn('colour');
135
        $attributeValueEN->getValue()->willReturn('Blue');
136
137
        $attributeValuePL->setProduct($this)->shouldBeCalled();
138
        $attributeValuePL->getLocaleCode()->willReturn('pl_PL');
139
        $attributeValuePL->getAttribute()->willReturn($attribute);
140
        $attributeValuePL->getCode()->willReturn('colour');
141
        $attributeValuePL->getValue()->willReturn('Niebieski');
142
143
        $this->addAttribute($attributeValueEN);
144
        $this->addAttribute($attributeValuePL);
145
146
        $this
147
            ->getAttributesByLocale('pl_PL', 'en_US')
148
            ->shouldIterateAs([$attributeValuePL->getWrappedObject()])
149
        ;
150
    }
151
152
    function it_returns_attributes_by_a_locale_with_a_base_locale(
153
        ProductAttributeInterface $attribute,
154
        ProductAttributeValueInterface $attributeValueEN,
155
        ProductAttributeValueInterface $attributeValuePL,
156
        ProductAttributeValueInterface $attributeValueFR
157
    ): void {
158
        $attribute->getCode()->willReturn('colour');
159
160
        $attributeValueEN->setProduct($this)->shouldBeCalled();
161
        $attributeValueEN->getLocaleCode()->willReturn('en_US');
162
        $attributeValueEN->getAttribute()->willReturn($attribute);
163
        $attributeValueEN->getCode()->willReturn('colour');
164
        $attributeValueEN->getValue()->willReturn('Blue');
165
166
        $attributeValuePL->setProduct($this)->shouldBeCalled();
167
        $attributeValuePL->getLocaleCode()->willReturn('pl_PL');
168
        $attributeValuePL->getAttribute()->willReturn($attribute);
169
        $attributeValuePL->getCode()->willReturn('colour');
170
        $attributeValuePL->getValue()->willReturn('Niebieski');
171
172
        $attributeValueFR->setProduct($this)->shouldBeCalled();
173
        $attributeValueFR->getLocaleCode()->willReturn('fr_FR');
174
        $attributeValueFR->getAttribute()->willReturn($attribute);
175
        $attributeValueFR->getCode()->willReturn('colour');
176
        $attributeValueFR->getValue()->willReturn('Bleu');
177
178
        $this->addAttribute($attributeValueEN);
179
        $this->addAttribute($attributeValuePL);
180
        $this->addAttribute($attributeValueFR);
181
182
        $this
183
            ->getAttributesByLocale('pl_PL', 'en_US', 'fr_FR')
184
            ->shouldIterateAs([$attributeValuePL->getWrappedObject()])
185
        ;
186
    }
187
188
    function it_returns_attributes_by_a_fallback_locale_when_there_is_no_value_for_a_given_locale(
189
        ProductAttributeInterface $attribute,
190
        ProductAttributeValueInterface $attributeValueEN
191
    ): void {
192
        $attribute->getCode()->willReturn('colour');
193
194
        $attributeValueEN->setProduct($this)->shouldBeCalled();
195
        $attributeValueEN->getLocaleCode()->willReturn('en_US');
196
        $attributeValueEN->getAttribute()->willReturn($attribute);
197
        $attributeValueEN->getCode()->willReturn('colour');
198
        $attributeValueEN->getValue()->willReturn('Blue');
199
200
        $this->addAttribute($attributeValueEN);
201
202
        $this
203
            ->getAttributesByLocale('pl_PL', 'en_US')
204
            ->shouldIterateAs([$attributeValueEN->getWrappedObject()])
205
        ;
206
    }
207
208
    function it_returns_attributes_by_a_fallback_locale_when_there_is_an_empty_value_for_a_given_locale(
209
        ProductAttributeInterface $attribute,
210
        ProductAttributeValueInterface $attributeValueEN,
211
        ProductAttributeValueInterface $attributeValuePL
212
    ): void {
213
        $attribute->getCode()->willReturn('colour');
214
215
        $attributeValueEN->setProduct($this)->shouldBeCalled();
216
        $attributeValueEN->getLocaleCode()->willReturn('en_US');
217
        $attributeValueEN->getAttribute()->willReturn($attribute);
218
        $attributeValueEN->getCode()->willReturn('colour');
219
        $attributeValueEN->getValue()->willReturn('Blue');
220
221
        $attributeValuePL->setProduct($this)->shouldBeCalled();
222
        $attributeValuePL->getLocaleCode()->willReturn('pl_PL');
223
        $attributeValuePL->getAttribute()->willReturn($attribute);
224
        $attributeValuePL->getCode()->willReturn('colour');
225
        $attributeValuePL->getValue()->willReturn('');
226
227
        $this->addAttribute($attributeValueEN);
228
        $this->addAttribute($attributeValuePL);
229
230
        $this
231
            ->getAttributesByLocale('pl_PL', 'en_US')
232
            ->shouldIterateAs([$attributeValueEN->getWrappedObject()])
233
        ;
234
    }
235
236
    function it_returns_attributes_by_a_base_locale_when_there_is_no_value_for_a_given_locale_or_a_fallback_locale(
237
        ProductAttributeInterface $attribute,
238
        ProductAttributeValueInterface $attributeValueFR
239
    ): void {
240
        $attribute->getCode()->willReturn('colour');
241
242
        $attributeValueFR->setProduct($this)->shouldBeCalled();
243
        $attributeValueFR->getLocaleCode()->willReturn('fr_FR');
244
        $attributeValueFR->getAttribute()->willReturn($attribute);
245
        $attributeValueFR->getCode()->willReturn('colour');
246
        $attributeValueFR->getValue()->willReturn('Bleu');
247
248
        $this->addAttribute($attributeValueFR);
249
250
        $this
251
            ->getAttributesByLocale('pl_PL', 'en_US', 'fr_FR')
252
            ->shouldIterateAs([$attributeValueFR->getWrappedObject()])
253
        ;
254
    }
255
256
    function it_returns_attributes_by_a_base_locale_when_there_is_an_empty_value_for_a_given_locale_or_a_fallback_locale(
257
        ProductAttributeInterface $attribute,
258
        ProductAttributeValueInterface $attributeValueEN,
259
        ProductAttributeValueInterface $attributeValuePL,
260
        ProductAttributeValueInterface $attributeValueFR
261
    ): void {
262
        $attribute->getCode()->willReturn('colour');
263
264
        $attributeValueEN->setProduct($this)->shouldBeCalled();
265
        $attributeValueEN->getLocaleCode()->willReturn('en_US');
266
        $attributeValueEN->getAttribute()->willReturn($attribute);
267
        $attributeValueEN->getCode()->willReturn('colour');
268
        $attributeValueEN->getValue()->willReturn('');
269
270
        $attributeValuePL->setProduct($this)->shouldBeCalled();
271
        $attributeValuePL->getLocaleCode()->willReturn('pl_PL');
272
        $attributeValuePL->getAttribute()->willReturn($attribute);
273
        $attributeValuePL->getCode()->willReturn('colour');
274
        $attributeValuePL->getValue()->willReturn(null);
275
276
        $attributeValueFR->setProduct($this)->shouldBeCalled();
277
        $attributeValueFR->getLocaleCode()->willReturn('fr_FR');
278
        $attributeValueFR->getAttribute()->willReturn($attribute);
279
        $attributeValueFR->getCode()->willReturn('colour');
280
        $attributeValueFR->getValue()->willReturn('Bleu');
281
282
        $this->addAttribute($attributeValueEN);
283
        $this->addAttribute($attributeValuePL);
284
        $this->addAttribute($attributeValueFR);
285
286
        $this
287
            ->getAttributesByLocale('pl_PL', 'en_US', 'fr_FR')
288
            ->shouldIterateAs([$attributeValueFR->getWrappedObject()])
289
        ;
290
    }
291
292
    function it_has_no_variants_by_default(): void
293
    {
294
        $this->hasVariants()->shouldReturn(false);
295
    }
296
297
    function its_says_it_has_variants_only_if_multiple_variants_are_defined(
298
        ProductVariantInterface $firstVariant,
299
        ProductVariantInterface $secondVariant
300
    ): void {
301
        $firstVariant->setProduct($this)->shouldBeCalled();
302
        $secondVariant->setProduct($this)->shouldBeCalled();
303
304
        $this->addVariant($firstVariant);
305
        $this->addVariant($secondVariant);
306
        $this->hasVariants()->shouldReturn(true);
307
    }
308
309
    function it_initializes_variants_collection_by_default(): void
310
    {
311
        $this->getVariants()->shouldHaveType(Collection::class);
312
    }
313
314
    function it_does_not_include_unavailable_variants_in_available_variants(ProductVariantInterface $variant): void
315
    {
316
        $variant->setProduct($this)->shouldBeCalled();
317
318
        $this->addVariant($variant);
319
    }
320
321
    function it_returns_available_variants(
322
        ProductVariantInterface $unavailableVariant,
323
        ProductVariantInterface $variant
324
    ): void {
325
        $unavailableVariant->setProduct($this)->shouldBeCalled();
326
        $variant->setProduct($this)->shouldBeCalled();
327
328
        $this->addVariant($unavailableVariant);
329
        $this->addVariant($variant);
330
    }
331
332
    function it_initializes_options_collection_by_default(): void
333
    {
334
        $this->getOptions()->shouldHaveType(Collection::class);
335
    }
336
337
    function it_has_no_options_by_default(): void
338
    {
339
        $this->hasOptions()->shouldReturn(false);
340
    }
341
342
    function its_says_it_has_options_only_if_any_option_defined(ProductOptionInterface $option): void
343
    {
344
        $this->addOption($option);
345
        $this->hasOptions()->shouldReturn(true);
346
    }
347
348
    function it_adds_option_properly(ProductOptionInterface $option): void
349
    {
350
        $this->addOption($option);
351
        $this->hasOption($option)->shouldReturn(true);
352
    }
353
354
    function it_removes_option_properly(ProductOptionInterface $option): void
355
    {
356
        $this->addOption($option);
357
        $this->hasOption($option)->shouldReturn(true);
358
359
        $this->removeOption($option);
360
        $this->hasOption($option)->shouldReturn(false);
361
    }
362
363
    function it_initializes_creation_date_by_default(): void
364
    {
365
        $this->getCreatedAt()->shouldHaveType(\DateTimeInterface::class);
366
    }
367
368
    function its_creation_date_is_mutable(\DateTime $creationDate): void
369
    {
370
        $this->setCreatedAt($creationDate);
371
        $this->getCreatedAt()->shouldReturn($creationDate);
372
    }
373
374
    function it_has_no_last_update_date_by_default(): void
375
    {
376
        $this->getUpdatedAt()->shouldReturn(null);
377
    }
378
379
    function its_last_update_date_is_mutable(\DateTime $updateDate): void
380
    {
381
        $this->setUpdatedAt($updateDate);
382
        $this->getUpdatedAt()->shouldReturn($updateDate);
383
    }
384
385
    function it_is_enabled_by_default(): void
386
    {
387
        $this->shouldBeEnabled();
388
    }
389
390
    function it_is_toggleable(): void
391
    {
392
        $this->disable();
393
        $this->shouldNotBeEnabled();
394
395
        $this->enable();
396
        $this->shouldBeEnabled();
397
    }
398
399
    function it_adds_association(ProductAssociationInterface $association): void
400
    {
401
        $association->setOwner($this)->shouldBeCalled();
402
        $this->addAssociation($association);
403
404
        $this->hasAssociation($association)->shouldReturn(true);
405
    }
406
407
    function it_allows_to_remove_association(ProductAssociationInterface $association): void
408
    {
409
        $association->setOwner($this)->shouldBeCalled();
410
        $association->setOwner(null)->shouldBeCalled();
411
412
        $this->addAssociation($association);
413
        $this->removeAssociation($association);
414
415
        $this->hasAssociation($association)->shouldReturn(false);
416
    }
417
418
    function it_is_simple_if_it_has_one_variant_and_no_options(ProductVariantInterface $variant): void
419
    {
420
        $variant->setProduct($this)->shouldBeCalled();
421
        $this->addVariant($variant);
422
423
        $this->isSimple()->shouldReturn(true);
424
        $this->isConfigurable()->shouldReturn(false);
425
    }
426
427
    function it_is_configurable_if_it_has_at_least_two_variants(
428
        ProductVariantInterface $firstVariant,
429
        ProductVariantInterface $secondVariant
430
    ): void {
431
        $firstVariant->setProduct($this)->shouldBeCalled();
432
        $this->addVariant($firstVariant);
433
        $secondVariant->setProduct($this)->shouldBeCalled();
434
        $this->addVariant($secondVariant);
435
436
        $this->isConfigurable()->shouldReturn(true);
437
        $this->isSimple()->shouldReturn(false);
438
    }
439
440
    function it_is_configurable_if_it_has_one_variant_and_at_least_one_option(
441
        ProductOptionInterface $option,
442
        ProductVariantInterface $variant
443
    ): void {
444
        $variant->setProduct($this)->shouldBeCalled();
445
        $this->addVariant($variant);
446
        $this->addOption($option);
447
448
        $this->isConfigurable()->shouldReturn(true);
449
        $this->isSimple()->shouldReturn(false);
450
    }
451
}
452