Completed
Push — taxon-parent-form ( 63b2c2 )
by Kamil
21:22
created

ProductContext::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 33
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 31
nc 1
nop 15

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
namespace Sylius\Behat\Context\Setup;
13
14
use Behat\Behat\Context\Context;
15
use Behat\Gherkin\Node\TableNode;
16
use Behat\Mink\Element\NodeElement;
17
use Doctrine\Common\Persistence\ObjectManager;
18
use Sylius\Behat\Service\SharedStorageInterface;
19
use Sylius\Component\Attribute\Factory\AttributeFactoryInterface;
20
use Sylius\Component\Core\Formatter\StringInflector;
21
use Sylius\Component\Core\Model\ChannelInterface;
22
use Sylius\Component\Core\Model\ChannelPricingInterface;
23
use Sylius\Component\Core\Model\ImageInterface;
24
use Sylius\Component\Core\Model\ProductInterface;
25
use Sylius\Component\Core\Model\ProductTranslationInterface;
26
use Sylius\Component\Core\Model\ProductVariantInterface;
27
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
28
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
29
use Sylius\Component\Product\Factory\ProductFactoryInterface;
30
use Sylius\Component\Product\Generator\SlugGeneratorInterface;
31
use Sylius\Component\Product\Model\ProductOptionInterface;
32
use Sylius\Component\Product\Model\ProductOptionValueInterface;
33
use Sylius\Component\Product\Model\ProductVariantTranslationInterface;
34
use Sylius\Component\Product\Resolver\ProductVariantResolverInterface;
35
use Sylius\Component\Resource\Factory\FactoryInterface;
36
use Sylius\Component\Resource\Model\TranslationInterface;
37
use Sylius\Component\Resource\Repository\RepositoryInterface;
38
use Sylius\Component\Shipping\Model\ShippingCategoryInterface;
39
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
40
use Symfony\Component\HttpFoundation\File\UploadedFile;
41
use Webmozart\Assert\Assert;
42
43
/**
44
 * @author Arkadiusz Krakowiak <[email protected]>
45
 * @author Mateusz Zalewski <[email protected]>
46
 * @author Magdalena Banasiak <[email protected]>
47
 */
48
final class ProductContext implements Context
49
{
50
    /**
51
     * @var SharedStorageInterface
52
     */
53
    private $sharedStorage;
54
55
    /**
56
     * @var ProductRepositoryInterface
57
     */
58
    private $productRepository;
59
60
    /**
61
     * @var ProductFactoryInterface
62
     */
63
    private $productFactory;
64
65
    /**
66
     * @var FactoryInterface
67
     */
68
    private $productTranslationFactory;
69
70
    /**
71
     * @var FactoryInterface
72
     */
73
    private $productVariantFactory;
74
75
    /**
76
     * @var FactoryInterface
77
     */
78
    private $productVariantTranslationFactory;
79
80
    /**
81
     * @var FactoryInterface
82
     */
83
    private $channelPricingFactory;
84
85
    /**
86
     * @var FactoryInterface
87
     */
88
    private $productOptionFactory;
89
90
    /**
91
     * @var FactoryInterface
92
     */
93
    private $productOptionValueFactory;
94
95
    /**
96
     * @var FactoryInterface
97
     */
98
    private $productImageFactory;
99
100
    /**
101
     * @var ObjectManager
102
     */
103
    private $objectManager;
104
105
    /**
106
     * @var ProductVariantResolverInterface
107
     */
108
    private $defaultVariantResolver;
109
110
    /**
111
     * @var ImageUploaderInterface
112
     */
113
    private $imageUploader;
114
115
    /**
116
     * @var SlugGeneratorInterface
117
     */
118
    private $slugGenerator;
119
120
    /**
121
     * @var array
122
     */
123
    private $minkParameters;
124
125
    /**
126
     * @param SharedStorageInterface $sharedStorage
127
     * @param ProductRepositoryInterface $productRepository
128
     * @param ProductFactoryInterface $productFactory
129
     * @param FactoryInterface $productTranslationFactory
130
     * @param FactoryInterface $productVariantFactory
131
     * @param FactoryInterface $productVariantTranslationFactory
132
     * @param FactoryInterface $channelPricingFactory
133
     * @param FactoryInterface $productOptionFactory
134
     * @param FactoryInterface $productOptionValueFactory
135
     * @param FactoryInterface $productImageFactory
136
     * @param ObjectManager $objectManager
137
     * @param ProductVariantResolverInterface $defaultVariantResolver
138
     * @param ImageUploaderInterface $imageUploader
139
     * @param SlugGeneratorInterface $slugGenerator
140
     * @param array $minkParameters
141
     */
142
    public function __construct(
143
        SharedStorageInterface $sharedStorage,
144
        ProductRepositoryInterface $productRepository,
145
        ProductFactoryInterface $productFactory,
146
        FactoryInterface $productTranslationFactory,
147
        FactoryInterface $productVariantFactory,
148
        FactoryInterface $productVariantTranslationFactory,
149
        FactoryInterface $channelPricingFactory,
150
        FactoryInterface $productOptionFactory,
151
        FactoryInterface $productOptionValueFactory,
152
        FactoryInterface $productImageFactory,
153
        ObjectManager $objectManager,
154
        ProductVariantResolverInterface $defaultVariantResolver,
155
        ImageUploaderInterface $imageUploader,
156
        SlugGeneratorInterface $slugGenerator,
157
        array $minkParameters
158
    ) {
159
        $this->sharedStorage = $sharedStorage;
160
        $this->productRepository = $productRepository;
161
        $this->productFactory = $productFactory;
162
        $this->productTranslationFactory = $productTranslationFactory;
163
        $this->productVariantFactory = $productVariantFactory;
164
        $this->productVariantTranslationFactory = $productVariantTranslationFactory;
165
        $this->channelPricingFactory = $channelPricingFactory;
166
        $this->productOptionFactory = $productOptionFactory;
167
        $this->productOptionValueFactory = $productOptionValueFactory;
168
        $this->productImageFactory = $productImageFactory;
169
        $this->objectManager = $objectManager;
170
        $this->defaultVariantResolver = $defaultVariantResolver;
171
        $this->imageUploader = $imageUploader;
172
        $this->slugGenerator = $slugGenerator;
173
        $this->minkParameters = $minkParameters;
174
    }
175
176
    /**
177
     * @Given the store has a product :productName
178
     * @Given the store has a :productName product
179
     * @Given I added a product :productName
180
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+")$/
181
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") in ("[^"]+" channel)$/
182
     */
183
    public function storeHasAProductPricedAt($productName, $price = 100, ChannelInterface $channel = null)
184
    {
185
        $product = $this->createProduct($productName, $price, $channel);
186
187
        $this->saveProduct($product);
188
    }
189
190
    /**
191
     * @Given /^(this product) is also priced at ("[^"]+") in ("[^"]+" channel)$/
192
     */
193
    public function thisProductIsAlsoPricedAtInChannel(ProductInterface $product, $price, ChannelInterface $channel)
194
    {
195
        $product->addChannel($channel);
196
197
        /** @var ProductVariantInterface $productVariant */
198
        $productVariant = $this->defaultVariantResolver->getVariant($product);
199
        $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
200
201
        $this->objectManager->flush();
202
    }
203
204
    /**
205
     * @Given the store( also) has a product :productName with code :code
206
     * @Given the store( also) has a product :productName with code :code, created at :date
207
     */
208
    public function storeHasProductWithCode($productName, $code, $date = null)
209
    {
210
        $product = $this->createProduct($productName);
211
        $product->setCreatedAt(new \DateTime($date));
212
        $product->setCode($code);
213
214
        $this->saveProduct($product);
215
    }
216
217
    /**
218
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") available in (channel "[^"]+") and (channel "[^"]+")$/
219
     */
220
    public function storeHasAProductPricedAtAvailableInChannels($productName, $price = 100, ...$channels)
221
    {
222
        $product = $this->createProduct($productName, $price);
223
        /** @var ProductVariantInterface $productVariant */
224
        $productVariant = $this->defaultVariantResolver->getVariant($product);
225
226
        foreach ($channels as $channel) {
227
            $product->addChannel($channel);
228
            if (!$productVariant->hasChannelPricingForChannel($channel)) {
229
                $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
230
            }
231
        }
232
233
        $this->saveProduct($product);
234
    }
235
236
    /**
237
     * @Given /^(this product) is named "([^"]+)" (in the "([^"]+)" locale)$/
238
     * @Given /^the (product "[^"]+") is named "([^"]+)" (in the "([^"]+)" locale)$/
239
     */
240
    public function thisProductIsNamedIn(ProductInterface $product, $name, $locale)
241
    {
242
        $this->addProductTranslation($product, $name, $locale);
243
244
        $this->objectManager->flush();
245
    }
246
247
    /**
248
     * @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
249
     */
250
    public function theStoreHasProductNamedInAndIn($firstName, $firstLocale, $secondName, $secondLocale)
251
    {
252
        $product = $this->createProduct($firstName);
253
254
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
255
        foreach ($names as $name => $locale) {
256
            $this->addProductTranslation($product, $name, $locale);
257
        }
258
259
        $this->saveProduct($product);
260
    }
261
262
    /**
263
     * @Given /^the store has(?:| a| an) "([^"]+)" configurable product$/
264
     * @Given /^the store has(?:| a| an) "([^"]+)" configurable product with "([^"]+)" slug$/
265
     */
266
    public function storeHasAConfigurableProduct($productName, $slug = null)
267
    {
268
        /** @var ChannelInterface|null $channel */
269
        $channel = null;
270
        if ($this->sharedStorage->has('channel')) {
271
            $channel = $this->sharedStorage->get('channel');
272
        }
273
274
        /** @var ProductInterface $product */
275
        $product = $this->productFactory->createNew();
276
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
277
278
        if (null !== $channel) {
279
            $product->addChannel($channel);
280
281
            foreach ($channel->getLocales() as $locale) {
282
                $product->setFallbackLocale($locale->getCode());
283
                $product->setCurrentLocale($locale->getCode());
284
285
                $product->setName($productName);
286
                $product->setSlug($slug ?: $this->slugGenerator->generate($productName));
287
            }
288
        }
289
290
        $this->saveProduct($product);
291
    }
292
293
    /**
294
     * @Given the store has( also) :firstProductName and :secondProductName products
295
     * @Given the store has( also) :firstProductName, :secondProductName and :thirdProductName products
296
     * @Given the store has( also) :firstProductName, :secondProductName, :thirdProductName and :fourthProductName products
297
     */
298
    public function theStoreHasProducts(...$productsNames)
299
    {
300
        foreach ($productsNames as $productName) {
301
            $this->saveProduct($this->createProduct($productName));
302
        }
303
    }
304
305
    /**
306
     * @Given /^(this channel) has "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" products$/
307
     */
308
    public function thisChannelHasProducts(ChannelInterface $channel, ...$productsNames)
309
    {
310
        foreach ($productsNames as $productName) {
311
            $product = $this->createProduct($productName, 0, $channel);
312
313
            $this->saveProduct($product);
314
        }
315
    }
316
317
    /**
318
     * @Given /^the (product "[^"]+") has(?:| a) "([^"]+)" variant priced at ("[^"]+")$/
319
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+")$/
320
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") in ("([^"]+)" channel)$/
321
     */
322
    public function theProductHasVariantPricedAt(
323
        ProductInterface $product,
324
        $productVariantName,
325
        $price,
326
        ChannelInterface $channel = null
327
    ) {
328
        $this->createProductVariant(
329
            $product,
330
            $productVariantName,
331
            $price,
332
            StringInflector::nameToUppercaseCode($productVariantName),
333
            (null !== $channel) ? $channel : $this->sharedStorage->get('channel')
334
        );
335
    }
336
337
    /**
338
     * @Given /^the (product "[^"]+") has(?:| a| an) "([^"]+)" variant$/
339
     * @Given /^(this product) has(?:| a| an) "([^"]+)" variant$/
340
     * @Given /^(this product) has "([^"]+)", "([^"]+)" and "([^"]+)" variants$/
341
     */
342
    public function theProductHasVariants(ProductInterface $product, ...$variantNames)
343
    {
344
        $channel = $this->sharedStorage->get('channel');
345
346
        foreach ($variantNames as $name) {
347
            $this->createProductVariant(
348
                $product,
349
                $name,
350
                0,
351
                StringInflector::nameToUppercaseCode($name),
352
                $channel
353
            );
354
        }
355
    }
356
357
    /**
358
     * @Given /^the (product "[^"]+")(?:| also) has a nameless variant with code "([^"]+)"$/
359
     * @Given /^(this product)(?:| also) has a nameless variant with code "([^"]+)"$/
360
     * @Given /^(it)(?:| also) has a nameless variant with code "([^"]+)"$/
361
     */
362
    public function theProductHasNamelessVariantWithCode(ProductInterface $product, $variantCode)
363
    {
364
        $channel = $this->sharedStorage->get('channel');
365
366
        $this->createProductVariant($product, null, 0, $variantCode, $channel);
367
    }
368
369
    /**
370
     * @Given /^the (product "[^"]+")(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
371
     * @Given /^(this product)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
372
     * @Given /^(it)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
373
     */
374
    public function theProductHasVariantWithCode(ProductInterface $product, $variantName, $variantCode)
375
    {
376
        $channel = $this->sharedStorage->get('channel');
377
378
        $this->createProductVariant($product, $variantName, 0, $variantCode, $channel);
379
    }
380
381
    /**
382
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") which does not require shipping$/
383
     */
384
    public function theProductHasVariantWhichDoesNotRequireShipping(
385
        ProductInterface $product,
386
        $productVariantName,
387
        $price
388
    ) {
389
        $this->createProductVariant(
390
            $product,
391
            $productVariantName,
392
            $price,
393
            StringInflector::nameToUppercaseCode($productVariantName),
394
            $this->sharedStorage->get('channel'),
395
            null,
396
            false
397
        );
398
    }
399
400
    /**
401
     * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant$/
402
     * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/
403
     * @Given /^(this product) has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/
404
     */
405
    public function theProductHasVariantAtPosition(
406
        ProductInterface $product,
407
        $productVariantName,
408
        $position = null
409
    ) {
410
        $this->createProductVariant(
411
            $product,
412
            $productVariantName,
413
            0,
414
            StringInflector::nameToUppercaseCode($productVariantName),
415
            $this->sharedStorage->get('channel'),
416
            $position
417
        );
418
    }
419
420
    /**
421
     * @Given /^(this variant) is also priced at ("[^"]+") in ("([^"]+)" channel)$/
422
     */
423
    public function thisVariantIsAlsoPricedAtInChannel(ProductVariantInterface $productVariant, $price, ChannelInterface $channel)
424
    {
425
        $productVariant->addChannelPricing($this->createChannelPricingForChannel(
426
            $this->getPriceFromString(str_replace(['$', '€', '£'], '', $price)),
427
            $channel
428
        ));
429
430
        $this->objectManager->flush();
431
    }
432
433
    /**
434
     * @Given /^(it|this product) has(?:| also) variant named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
435
     */
436
    public function itHasVariantNamedInAndIn(ProductInterface $product, $firstName, $firstLocale, $secondName, $secondLocale)
437
    {
438
        $productVariant = $this->createProductVariant(
439
            $product,
440
            $firstName,
441
            100,
442
            StringInflector::nameToUppercaseCode($firstName),
443
            $this->sharedStorage->get('channel')
444
        );
445
446
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
447
        foreach ($names as $name => $locale) {
448
            $this->addProductVariantTranslation($productVariant, $name, $locale);
449
        }
450
451
        $this->objectManager->flush();
452
    }
453
454
    /**
455
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") identified by "([^"]+)"$/
456
     */
457
    public function theProductHasVariantPricedAtIdentifiedBy(
458
        ProductInterface $product,
459
        $productVariantName,
460
        $price,
461
        $code
462
    ) {
463
        $this->createProductVariant($product, $productVariantName, $price, $code, $this->sharedStorage->get('channel'));
464
    }
465
466
    /**
467
     * @Given /^there is product "([^"]+)" available in ((?:this|that|"[^"]+") channel)$/
468
     * @Given /^the store has a product "([^"]+)" available in ("([^"]+)" channel)$/
469
     */
470
    public function thereIsProductAvailableInGivenChannel($productName, ChannelInterface $channel)
471
    {
472
        $product = $this->createProduct($productName, 0, $channel);
473
474
        $this->saveProduct($product);
475
    }
476
477
    /**
478
     * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/
479
     */
480
    public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory)
481
    {
482
        /** @var ProductVariantInterface $variant */
483
        $variant = $this->defaultVariantResolver->getVariant($product);
484
        $variant->setTaxCategory($taxCategory);
485
486
        $this->objectManager->flush();
487
    }
488
489
    /**
490
     * @Given /^(it) comes in the following variations:$/
491
     */
492
    public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
493
    {
494
        $channel = $this->sharedStorage->get('channel');
495
496
        foreach ($table->getHash() as $variantHash) {
497
            /** @var ProductVariantInterface $variant */
498
            $variant = $this->productVariantFactory->createNew();
499
500
            $variant->setName($variantHash['name']);
501
            $variant->setCode(StringInflector::nameToUppercaseCode($variantHash['name']));
502
            $variant->addChannelPricing($this->createChannelPricingForChannel(
503
                $this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])),
504
                $channel
505
            ));
506
507
            $variant->setProduct($product);
508
            $product->addVariant($variant);
509
        }
510
511
        $this->objectManager->flush();
512
    }
513
514
    /**
515
     * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/
516
     */
517
    public function productVariantBelongsToTaxCategory(
518
        ProductVariantInterface $productVariant,
519
        TaxCategoryInterface $taxCategory
520
    ) {
521
        $productVariant->setTaxCategory($taxCategory);
522
        $this->objectManager->flush($productVariant);
523
    }
524
525
    /**
526
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)" and "([^"]+)"$/
527
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)", "([^"]+)" and "([^"]+)"$/
528
     */
529
    public function thisProductHasOptionWithValues(ProductInterface $product, $optionName, ...$values)
530
    {
531
        /** @var ProductOptionInterface $option */
532
        $option = $this->productOptionFactory->createNew();
533
534
        $option->setName($optionName);
535
        $option->setCode(StringInflector::nameToUppercaseCode($optionName));
536
537
        $this->sharedStorage->set(sprintf('%s_option', $optionName), $option);
538
539
        foreach ($values as $key => $value) {
540
            $optionValue = $this->addProductOption($option, $value, StringInflector::nameToUppercaseCode($value));
541
            $this->sharedStorage->set(sprintf('%s_option_%s_value', $value, strtolower($optionName)), $optionValue);
542
        }
543
544
        $product->addOption($option);
545
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
546
547
        $this->objectManager->persist($option);
548
        $this->objectManager->flush();
549
    }
550
551
    /**
552
     * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/
553
     */
554
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
555
    {
556
        /** @var ProductVariantInterface $productVariant */
557
        $productVariant = $this->defaultVariantResolver->getVariant($product);
558
        $productVariant->setOnHand($quantity);
559
560
        $this->objectManager->flush();
561
    }
562
563
    /**
564
     * @Given /^the (product "([^"]+)") is out of stock$/
565
     */
566
    public function theProductIsOutOfStock(ProductInterface $product)
567
    {
568
        /** @var ProductVariantInterface $productVariant */
569
        $productVariant = $this->defaultVariantResolver->getVariant($product);
570
        $productVariant->setTracked(true);
571
        $productVariant->setOnHand(0);
572
573
        $this->objectManager->flush();
574
    }
575
576
    /**
577
     * @When other customer has bought :quantity :product products by this time
578
     */
579
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
580
    {
581
        /** @var ProductVariantInterface $productVariant */
582
        $productVariant = $this->defaultVariantResolver->getVariant($product);
583
        $productVariant->setOnHand($productVariant->getOnHand() - $quantity);
584
585
        $this->objectManager->flush();
586
    }
587
588
    /**
589
     * @Given /^(this product) is tracked by the inventory$/
590
     * @Given /^(?:|the )("[^"]+" product) is(?:| also) tracked by the inventory$/
591
     */
592
    public function thisProductIsTrackedByTheInventory(ProductInterface $product)
593
    {
594
        /** @var ProductVariantInterface $productVariant */
595
        $productVariant = $this->defaultVariantResolver->getVariant($product);
596
        $productVariant->setTracked(true);
597
598
        $this->objectManager->flush();
599
    }
600
601
    /**
602
     * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/
603
     */
604
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, $price)
605
    {
606
        /** @var ProductVariantInterface $variant */
607
        $variant = $this->productVariantFactory->createNew();
608
609
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_%s_value', $optionValueName, $optionName));
610
611
        $variant->addOptionValue($optionValue);
612
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $this->sharedStorage->get('channel')));
613
        $variant->setCode(sprintf('%s_%s', $product->getCode(), $optionValueName));
614
        $variant->setName($product->getName());
615
616
        $product->addVariant($variant);
617
        $this->objectManager->flush();
618
    }
619
620
    /**
621
     * @Given the :product product's :optionValueName size belongs to :shippingCategory shipping category
622
     */
623
    public function thisProductSizeBelongsToShippingCategory(ProductInterface $product, $optionValueName, ShippingCategoryInterface $shippingCategory)
624
    {
625
        $code = sprintf('%s_%s', $product->getCode(), $optionValueName);
626
        /** @var ProductVariantInterface $productVariant */
627
        $productVariant = $product->getVariants()->filter(function ($variant) use ($code) {
628
            return $code === $variant->getCode();
629
        })->first();
630
631
        Assert::notNull($productVariant, sprintf('Product variant with given code %s not exists!', $code));
632
633
        $productVariant->setShippingCategory($shippingCategory);
634
        $this->objectManager->flush();
635
    }
636
637
    /**
638
     * @Given /^(this product) has (this product option)$/
639
     * @Given /^(this product) has (?:a|an) ("[^"]+" option)$/
640
     */
641
    public function thisProductHasThisProductOption(ProductInterface $product, ProductOptionInterface $option)
642
    {
643
        $product->addOption($option);
644
645
        $this->objectManager->flush();
646
    }
647
648
    /**
649
     * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/
650
     */
651
    public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant)
652
    {
653
        $productVariant->setTracked(true);
654
        $productVariant->setOnHand($quantity);
655
656
        $this->objectManager->flush();
657
    }
658
659
    /**
660
     * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/
661
     */
662
    public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant)
663
    {
664
        $productVariant->setTracked(true);
665
666
        $this->objectManager->flush();
667
    }
668
669
    /**
670
     * @Given /^(this product)'s price is ("[^"]+")$/
671
     * @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/
672
     * @Given /^(this product) price has been changed to ("[^"]+")$/
673
     */
674
    public function theProductChangedItsPriceTo(ProductInterface $product, $price)
675
    {
676
        /** @var ProductVariantInterface $productVariant */
677
        $productVariant = $this->defaultVariantResolver->getVariant($product);
678
        $channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
679
        $channelPricing->setPrice($price);
680
681
        $this->objectManager->flush();
682
    }
683
684
    /**
685
     * @Given /^(this product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
686
     * @Given /^the ("[^"]+" product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
687
     * @Given /^(it)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
688
     */
689
    public function thisProductHasAnImageWithType(ProductInterface $product, $imagePath, $imageType)
690
    {
691
        $filesPath = $this->getParameter('files_path');
692
693
        /** @var ImageInterface $productImage */
694
        $productImage = $this->productImageFactory->createNew();
695
        $productImage->setFile(new UploadedFile($filesPath.$imagePath, basename($imagePath)));
696
        $productImage->setType($imageType);
697
        $this->imageUploader->upload($productImage);
698
699
        $product->addImage($productImage);
700
701
        $this->objectManager->flush($product);
702
    }
703
704
    /**
705
     * @Given /^(this product) belongs to ("([^"]+)" shipping category)$/
706
     * @Given product :product shipping category has been changed to :shippingCategory
707
     */
708
    public function thisProductBelongsToShippingCategory(ProductInterface $product, ShippingCategoryInterface $shippingCategory)
709
    {
710
        $product->getVariants()->first()->setShippingCategory($shippingCategory);
711
        $this->objectManager->flush();
712
    }
713
714
    /**
715
     * @Given /^(this product) has been disabled$/
716
     */
717
    public function thisProductHasBeenDisabled(ProductInterface $product)
718
    {
719
        $product->disable();
720
        $this->objectManager->flush();
721
    }
722
723
    /**
724
     * @param string $price
725
     *
726
     * @return int
727
     */
728
    private function getPriceFromString($price)
729
    {
730
        return (int) round($price * 100, 2);
731
    }
732
733
    /**
734
     * @param string $productName
735
     * @param int $price
736
     * @param ChannelInterface|null $channel
737
     *
738
     * @return ProductInterface
739
     */
740
    private function createProduct($productName, $price = 100, ChannelInterface $channel = null)
741
    {
742
        if (null === $channel && $this->sharedStorage->has('channel')) {
743
            $channel = $this->sharedStorage->get('channel');
744
        }
745
746
        /** @var ProductInterface $product */
747
        $product = $this->productFactory->createWithVariant();
748
749
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
750
        $product->setName($productName);
751
        $product->setSlug($this->slugGenerator->generate($productName));
752
753
        if (null !== $channel) {
754
            $product->addChannel($channel);
755
756
            foreach ($channel->getLocales() as $locale) {
757
                $product->setFallbackLocale($locale->getCode());
758
                $product->setCurrentLocale($locale->getCode());
759
760
                $product->setName($productName);
761
                $product->setSlug($this->slugGenerator->generate($productName));
762
            }
763
        }
764
765
        /** @var ProductVariantInterface $productVariant */
766
        $productVariant = $this->defaultVariantResolver->getVariant($product);
767
768
        if (null !== $channel) {
769
            $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
770
        }
771
772
        $productVariant->setCode($product->getCode());
773
        $productVariant->setName($product->getName());
774
775
        return $product;
776
    }
777
778
    /**
779
     * @param ProductOptionInterface $option
780
     * @param string $value
781
     * @param string $code
782
     *
783
     * @return ProductOptionValueInterface
784
     */
785
    private function addProductOption(ProductOptionInterface $option, $value, $code)
786
    {
787
        /** @var ProductOptionValueInterface $optionValue */
788
        $optionValue = $this->productOptionValueFactory->createNew();
789
790
        $optionValue->setValue($value);
791
        $optionValue->setCode($code);
792
        $optionValue->setOption($option);
793
794
        $option->addValue($optionValue);
795
796
        return $optionValue;
797
    }
798
799
    /**
800
     * @param ProductInterface $product
801
     */
802
    private function saveProduct(ProductInterface $product)
803
    {
804
        $this->productRepository->add($product);
805
        $this->sharedStorage->set('product', $product);
806
    }
807
808
    /**
809
     * @param string $name
810
     *
811
     * @return NodeElement
812
     */
813
    private function getParameter($name)
814
    {
815
        return isset($this->minkParameters[$name]) ? $this->minkParameters[$name] : null;
816
    }
817
818
    /**
819
     * @param ProductInterface $product
820
     * @param $productVariantName
821
     * @param int $price
822
     * @param string $code
823
     * @param ChannelInterface $channel
0 ignored issues
show
Documentation introduced by
Should the type for parameter $channel not be null|ChannelInterface?

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.

Loading history...
824
     * @param int $position
0 ignored issues
show
Documentation introduced by
Should the type for parameter $position not be integer|null?

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.

Loading history...
825
     * @param bool $shippingRequired
826
     *
827
     * @return ProductVariantInterface
828
     */
829
    private function createProductVariant(
830
        ProductInterface $product,
831
        $productVariantName,
832
        $price,
833
        $code,
834
        ChannelInterface $channel = null,
835
        $position = null,
836
        $shippingRequired = true
837
    ) {
838
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_CHOICE);
839
840
        /** @var ProductVariantInterface $variant */
841
        $variant = $this->productVariantFactory->createNew();
842
843
        $variant->setName($productVariantName);
844
        $variant->setCode($code);
845
        $variant->setProduct($product);
846
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
847
        $variant->setPosition($position);
848
        $variant->setShippingRequired($shippingRequired);
849
850
        $product->addVariant($variant);
851
852
        $this->objectManager->flush();
853
        $this->sharedStorage->set('variant', $variant);
854
855
        return $variant;
856
    }
857
858
    /**
859
     * @param ProductInterface $product
860
     * @param string $name
861
     * @param string $locale
862
     */
863
    private function addProductTranslation(ProductInterface $product, $name, $locale)
864
    {
865
        /** @var ProductTranslationInterface|TranslationInterface $translation */
866
        $translation = $product->getTranslation($locale);
867
        if ($translation->getLocale() !== $locale) {
0 ignored issues
show
Bug introduced by
The method getLocale does only exist in Sylius\Component\Resourc...el\TranslationInterface, but not in Sylius\Component\Core\Mo...uctTranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
868
            $translation = $this->productTranslationFactory->createNew();
869
        }
870
871
        $translation->setLocale($locale);
872
        $translation->setName($name);
873
        $translation->setSlug($this->slugGenerator->generate($name));
874
875
        $product->addTranslation($translation);
876
    }
877
878
    /**
879
     * @param ProductVariantInterface $productVariant
880
     * @param string $name
881
     * @param string $locale
882
     */
883
    private function addProductVariantTranslation(ProductVariantInterface $productVariant, $name, $locale)
884
    {
885
        /** @var ProductVariantTranslationInterface|TranslationInterface $translation */
886
        $translation = $this->productVariantTranslationFactory->createNew();
887
        $translation->setLocale($locale);
0 ignored issues
show
Bug introduced by
The method setLocale does only exist in Sylius\Component\Resourc...el\TranslationInterface, but not in Sylius\Component\Product...antTranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
888
        $translation->setName($name);
0 ignored issues
show
Bug introduced by
The method setName does only exist in Sylius\Component\Product...antTranslationInterface, but not in Sylius\Component\Resourc...el\TranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
889
890
        $productVariant->addTranslation($translation);
0 ignored issues
show
Bug introduced by
It seems like $translation defined by $this->productVariantTra...ionFactory->createNew() on line 886 can also be of type object<Sylius\Component\...ntTranslationInterface>; however, Sylius\Component\Resourc...rface::addTranslation() does only seem to accept object<Sylius\Component\...l\TranslationInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
891
    }
892
893
    /**
894
     * @param int $price
895
     * @param ChannelInterface|null $channel
896
     *
897
     * @return ChannelPricingInterface
898
     */
899
    private function createChannelPricingForChannel($price, ChannelInterface $channel = null)
900
    {
901
        /** @var ChannelPricingInterface $channelPricing */
902
        $channelPricing = $this->channelPricingFactory->createNew();
903
        $channelPricing->setPrice($price);
904
        $channelPricing->setChannelCode($channel->getCode());
0 ignored issues
show
Bug introduced by
It seems like $channel is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
905
906
        return $channelPricing;
907
    }
908
}
909