Completed
Push — 1.3 ( 2923d8...a36271 )
by Kamil
32:20
created

ProductContext::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 9.36
c 0
b 0
f 0
cc 1
nc 1
nop 16

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
declare(strict_types=1);
13
14
namespace Sylius\Behat\Context\Setup;
15
16
use Behat\Behat\Context\Context;
17
use Behat\Gherkin\Node\TableNode;
18
use Behat\Mink\Element\NodeElement;
19
use Doctrine\Common\Persistence\ObjectManager;
20
use Sylius\Behat\Service\SharedStorageInterface;
21
use Sylius\Component\Core\Formatter\StringInflector;
22
use Sylius\Component\Core\Model\ChannelInterface;
23
use Sylius\Component\Core\Model\ChannelPricingInterface;
24
use Sylius\Component\Core\Model\ImageInterface;
25
use Sylius\Component\Core\Model\ProductInterface;
26
use Sylius\Component\Core\Model\ProductTranslationInterface;
27
use Sylius\Component\Core\Model\ProductVariantInterface;
28
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
29
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
30
use Sylius\Component\Product\Factory\ProductFactoryInterface;
31
use Sylius\Component\Product\Generator\ProductVariantGeneratorInterface;
32
use Sylius\Component\Product\Generator\SlugGeneratorInterface;
33
use Sylius\Component\Product\Model\ProductOptionInterface;
34
use Sylius\Component\Product\Model\ProductOptionValueInterface;
35
use Sylius\Component\Product\Model\ProductVariantTranslationInterface;
36
use Sylius\Component\Product\Resolver\ProductVariantResolverInterface;
37
use Sylius\Component\Resource\Factory\FactoryInterface;
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
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(
94
        SharedStorageInterface $sharedStorage,
95
        ProductRepositoryInterface $productRepository,
96
        ProductFactoryInterface $productFactory,
97
        FactoryInterface $productTranslationFactory,
98
        FactoryInterface $productVariantFactory,
99
        FactoryInterface $productVariantTranslationFactory,
100
        FactoryInterface $channelPricingFactory,
101
        FactoryInterface $productOptionFactory,
102
        FactoryInterface $productOptionValueFactory,
103
        FactoryInterface $productImageFactory,
104
        ObjectManager $objectManager,
105
        ProductVariantGeneratorInterface $productVariantGenerator,
106
        ProductVariantResolverInterface $defaultVariantResolver,
107
        ImageUploaderInterface $imageUploader,
108
        SlugGeneratorInterface $slugGenerator,
109
        array $minkParameters
110
    ) {
111
        $this->sharedStorage = $sharedStorage;
112
        $this->productRepository = $productRepository;
113
        $this->productFactory = $productFactory;
114
        $this->productTranslationFactory = $productTranslationFactory;
115
        $this->productVariantFactory = $productVariantFactory;
116
        $this->productVariantTranslationFactory = $productVariantTranslationFactory;
117
        $this->channelPricingFactory = $channelPricingFactory;
118
        $this->productOptionFactory = $productOptionFactory;
119
        $this->productOptionValueFactory = $productOptionValueFactory;
120
        $this->productImageFactory = $productImageFactory;
121
        $this->objectManager = $objectManager;
122
        $this->productVariantGenerator = $productVariantGenerator;
123
        $this->defaultVariantResolver = $defaultVariantResolver;
124
        $this->imageUploader = $imageUploader;
125
        $this->slugGenerator = $slugGenerator;
126
        $this->minkParameters = $minkParameters;
127
    }
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)
137
    {
138
        $product = $this->createProduct($productName, $price, $channel);
139
140
        $this->saveProduct($product);
141
    }
142
143
    /**
144
     * @Given /^(this product) is(?:| also) priced at ("[^"]+") in ("[^"]+" channel)$/
145
     */
146
    public function thisProductIsAlsoPricedAtInChannel(ProductInterface $product, int $price, ChannelInterface $channel)
147
    {
148
        $product->addChannel($channel);
149
150
        /** @var ProductVariantInterface $productVariant */
151
        $productVariant = $this->defaultVariantResolver->getVariant($product);
152
        $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
153
154
        $this->objectManager->flush();
155
    }
156
157
    /**
158
     * @Given /^(this product) is(?:| also) available in ("[^"]+" channel)$/
159
     */
160
    public function thisProductIsAlsoAvailableInChannel(ProductInterface $product, ChannelInterface $channel): void
161
    {
162
        $this->thisProductIsAlsoPricedAtInChannel($product, 0, $channel);
163
    }
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')
170
    {
171
        $product = $this->createProduct($productName);
172
        $product->setCreatedAt(new \DateTime($date));
173
        $product->setCode($code);
174
175
        $this->saveProduct($product);
176
    }
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)
182
    {
183
        $product = $this->createProduct($productName, $price);
184
        /** @var ProductVariantInterface $productVariant */
185
        $productVariant = $this->defaultVariantResolver->getVariant($product);
186
187
        foreach ($channels as $channel) {
188
            $product->addChannel($channel);
189
            if (!$productVariant->hasChannelPricingForChannel($channel)) {
190
                $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
191
            }
192
        }
193
194
        $this->saveProduct($product);
195
    }
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)
202
    {
203
        $this->addProductTranslation($product, $name, $locale);
204
205
        $this->objectManager->flush();
206
    }
207
208
    /**
209
     * @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
210
     */
211
    public function theStoreHasProductNamedInAndIn($firstName, $firstLocale, $secondName, $secondLocale)
212
    {
213
        $product = $this->createProduct($firstName);
214
215
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
216
        foreach ($names as $name => $locale) {
217
            $this->addProductTranslation($product, $name, $locale);
218
        }
219
220
        $this->saveProduct($product);
221
    }
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)
228
    {
229
        /** @var ChannelInterface|null $channel */
230
        $channel = null;
231
        if ($this->sharedStorage->has('channel')) {
232
            $channel = $this->sharedStorage->get('channel');
233
        }
234
235
        /** @var ProductInterface $product */
236
        $product = $this->productFactory->createNew();
237
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
238
239
        if (null !== $channel) {
240
            $product->addChannel($channel);
241
242
            foreach ($channel->getLocales() as $locale) {
243
                $product->setFallbackLocale($locale->getCode());
244
                $product->setCurrentLocale($locale->getCode());
245
246
                $product->setName($productName);
247
                $product->setSlug($slug ?: $this->slugGenerator->generate($productName));
248
            }
249
        }
250
251
        $this->saveProduct($product);
252
    }
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)
260
    {
261
        foreach ($productsNames as $productName) {
262
            $this->saveProduct($this->createProduct($productName));
263
        }
264
    }
265
266
    /**
267
     * @Given /^(this channel) has "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" products$/
268
     */
269
    public function thisChannelHasProducts(ChannelInterface $channel, ...$productsNames)
270
    {
271
        foreach ($productsNames as $productName) {
272
            $product = $this->createProduct($productName, 0, $channel);
273
274
            $this->saveProduct($product);
275
        }
276
    }
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(
284
        ProductInterface $product,
285
        $productVariantName,
286
        $price,
287
        ChannelInterface $channel = null
288
    ) {
289
        $this->createProductVariant(
290
            $product,
291
            $productVariantName,
292
            $price,
293
            StringInflector::nameToUppercaseCode($productVariantName),
294
            $channel ?? $this->sharedStorage->get('channel')
295
        );
296
    }
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)
304
    {
305
        $channel = $this->sharedStorage->get('channel');
306
307
        foreach ($variantNames as $name) {
308
            $this->createProductVariant(
309
                $product,
310
                $name,
311
                0,
312
                StringInflector::nameToUppercaseCode($name),
313
                $channel
314
            );
315
        }
316
    }
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)
324
    {
325
        $channel = $this->sharedStorage->get('channel');
326
327
        $this->createProductVariant($product, null, 0, $variantCode, $channel);
328
    }
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)
336
    {
337
        $channel = $this->sharedStorage->get('channel');
338
339
        $this->createProductVariant($product, $variantName, 0, $variantCode, $channel);
340
    }
341
342
    /**
343
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") which does not require shipping$/
344
     */
345
    public function theProductHasVariantWhichDoesNotRequireShipping(
346
        ProductInterface $product,
347
        $productVariantName,
348
        $price
349
    ) {
350
        $this->createProductVariant(
351
            $product,
352
            $productVariantName,
353
            $price,
354
            StringInflector::nameToUppercaseCode($productVariantName),
355
            $this->sharedStorage->get('channel'),
356
            null,
357
            false
358
        );
359
    }
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(
367
        ProductInterface $product,
368
        $productVariantName,
369
        $position = null
370
    ) {
371
        $this->createProductVariant(
372
            $product,
373
            $productVariantName,
374
            0,
375
            StringInflector::nameToUppercaseCode($productVariantName),
376
            $this->sharedStorage->get('channel'),
377
            $position
378
        );
379
    }
380
381
    /**
382
     * @Given /^(this variant) is also priced at ("[^"]+") in ("([^"]+)" channel)$/
383
     */
384
    public function thisVariantIsAlsoPricedAtInChannel(ProductVariantInterface $productVariant, string $price, ChannelInterface $channel)
385
    {
386
        $productVariant->addChannelPricing($this->createChannelPricingForChannel(
387
            $this->getPriceFromString(str_replace(['$', '€', '£'], '', $price)),
388
            $channel
389
        ));
390
391
        $this->objectManager->flush();
392
    }
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)
398
    {
399
        $productVariant = $this->createProductVariant(
400
            $product,
401
            $firstName,
402
            100,
403
            StringInflector::nameToUppercaseCode($firstName),
404
            $this->sharedStorage->get('channel')
405
        );
406
407
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
408
        foreach ($names as $name => $locale) {
409
            $this->addProductVariantTranslation($productVariant, $name, $locale);
410
        }
411
412
        $this->objectManager->flush();
413
    }
414
415
    /**
416
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") identified by "([^"]+)"$/
417
     */
418
    public function theProductHasVariantPricedAtIdentifiedBy(
419
        ProductInterface $product,
420
        $productVariantName,
421
        $price,
422
        $code
423
    ) {
424
        $this->createProductVariant($product, $productVariantName, $price, $code, $this->sharedStorage->get('channel'));
425
    }
426
427
    /**
428
     * @Given /^(this product) only variant was renamed to "([^"]+)"$/
429
     */
430
    public function productOnlyVariantWasRenamed(ProductInterface $product, $variantName)
431
    {
432
        Assert::true($product->isSimple());
433
434
        /** @var ProductVariantInterface $productVariant */
435
        $productVariant = $product->getVariants()->first();
436
        $productVariant->setName($variantName);
437
438
        $this->objectManager->flush();
439
    }
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)
446
    {
447
        $product = $this->createProduct($productName, 0, $channel);
448
449
        $this->saveProduct($product);
450
    }
451
452
    /**
453
     * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/
454
     */
455
    public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory)
456
    {
457
        /** @var ProductVariantInterface $variant */
458
        $variant = $this->defaultVariantResolver->getVariant($product);
459
        $variant->setTaxCategory($taxCategory);
460
461
        $this->objectManager->flush();
462
    }
463
464
    /**
465
     * @Given /^(it) comes in the following variations:$/
466
     */
467
    public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
468
    {
469
        $channel = $this->sharedStorage->get('channel');
470
471
        foreach ($table->getHash() as $variantHash) {
472
            /** @var ProductVariantInterface $variant */
473
            $variant = $this->productVariantFactory->createNew();
474
475
            $variant->setName($variantHash['name']);
476
            $variant->setCode(StringInflector::nameToUppercaseCode($variantHash['name']));
477
            $variant->addChannelPricing($this->createChannelPricingForChannel(
478
                $this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])),
479
                $channel
480
            ));
481
482
            $variant->setProduct($product);
483
            $product->addVariant($variant);
484
        }
485
486
        $this->objectManager->flush();
487
    }
488
489
    /**
490
     * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/
491
     */
492
    public function productVariantBelongsToTaxCategory(
493
        ProductVariantInterface $productVariant,
494
        TaxCategoryInterface $taxCategory
495
    ) {
496
        $productVariant->setTaxCategory($taxCategory);
497
498
        $this->objectManager->persist($productVariant);
499
        $this->objectManager->flush();
500
    }
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
507
    {
508
        $this->addOptionToProduct($product, $optionName, $values);
509
    }
510
511
    /**
512
     * @Given /^(this product) has an option "([^"]*)" without any values$/
513
     */
514
    public function thisProductHasAnOptionWithoutAnyValues(ProductInterface $product, string $optionName): void
515
    {
516
        $this->addOptionToProduct($product, $optionName, []);
517
    }
518
519
    /**
520
     * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/
521
     */
522
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
523
    {
524
        /** @var ProductVariantInterface $productVariant */
525
        $productVariant = $this->defaultVariantResolver->getVariant($product);
526
        $productVariant->setOnHand((int) $quantity);
527
528
        $this->objectManager->flush();
529
    }
530
531
    /**
532
     * @Given /^the (product "([^"]+)") is out of stock$/
533
     */
534
    public function theProductIsOutOfStock(ProductInterface $product)
535
    {
536
        /** @var ProductVariantInterface $productVariant */
537
        $productVariant = $this->defaultVariantResolver->getVariant($product);
538
        $productVariant->setTracked(true);
539
        $productVariant->setOnHand(0);
540
541
        $this->objectManager->flush();
542
    }
543
544
    /**
545
     * @When other customer has bought :quantity :product products by this time
546
     */
547
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
548
    {
549
        /** @var ProductVariantInterface $productVariant */
550
        $productVariant = $this->defaultVariantResolver->getVariant($product);
551
        $productVariant->setOnHand($productVariant->getOnHand() - $quantity);
552
553
        $this->objectManager->flush();
554
    }
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)
561
    {
562
        /** @var ProductVariantInterface $productVariant */
563
        $productVariant = $this->defaultVariantResolver->getVariant($product);
564
        $productVariant->setTracked(true);
565
566
        $this->objectManager->flush();
567
    }
568
569
    /**
570
     * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/
571
     */
572
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, int $price)
573
    {
574
        /** @var ProductVariantInterface $variant */
575
        $variant = $this->productVariantFactory->createNew();
576
577
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_%s_value', $optionValueName, $optionName));
578
579
        $variant->addOptionValue($optionValue);
580
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $this->sharedStorage->get('channel')));
581
        $variant->setCode(sprintf('%s_%s', $product->getCode(), $optionValueName));
582
        $variant->setName($product->getName());
583
584
        $product->addVariant($variant);
585
        $this->objectManager->flush();
586
    }
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)
592
    {
593
        $code = sprintf('%s_%s', $product->getCode(), $optionValueName);
594
        /** @var ProductVariantInterface $productVariant */
595
        $productVariant = $product->getVariants()->filter(function ($variant) use ($code) {
596
            return $code === $variant->getCode();
597
        })->first();
598
599
        Assert::notNull($productVariant, sprintf('Product variant with given code %s not exists!', $code));
600
601
        $productVariant->setShippingCategory($shippingCategory);
602
        $this->objectManager->flush();
603
    }
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)
610
    {
611
        $product->addOption($option);
612
613
        $this->objectManager->flush();
614
    }
615
616
    /**
617
     * @Given /^(this product) has all possible variants$/
618
     */
619
    public function thisProductHasAllPossibleVariants(ProductInterface $product)
620
    {
621
        try {
622
            foreach ($product->getVariants() as $productVariant) {
623
                $product->removeVariant($productVariant);
624
            }
625
626
            $this->productVariantGenerator->generate($product);
627
        } catch (\InvalidArgumentException $exception) {
628
            /** @var ProductVariantInterface $productVariant */
629
            $productVariant = $this->productVariantFactory->createNew();
630
631
            $product->addVariant($productVariant);
632
        }
633
634
        $i = 0;
635
        /** @var ProductVariantInterface $productVariant */
636
        foreach ($product->getVariants() as $productVariant) {
637
            $productVariant->setCode(sprintf('%s-variant-%d', $product->getCode(), $i));
638
639
            foreach ($product->getChannels() as $channel) {
640
                $productVariant->addChannelPricing($this->createChannelPricingForChannel(1000, $channel));
641
            }
642
643
            ++$i;
644
        }
645
646
        $this->objectManager->flush();
647
    }
648
649
    /**
650
     * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/
651
     */
652
    public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant)
653
    {
654
        $productVariant->setTracked(true);
655
        $productVariant->setOnHand((int) $quantity);
656
657
        $this->objectManager->flush();
658
    }
659
660
    /**
661
     * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/
662
     */
663
    public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant)
664
    {
665
        $productVariant->setTracked(true);
666
667
        $this->objectManager->flush();
668
    }
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)
676
    {
677
        /** @var ProductVariantInterface $productVariant */
678
        $productVariant = $this->defaultVariantResolver->getVariant($product);
679
        $channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
680
        $channelPricing->setPrice($price);
681
682
        $this->objectManager->flush();
683
    }
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)
691
    {
692
        $filesPath = $this->getParameter('files_path');
693
694
        /** @var ImageInterface $productImage */
695
        $productImage = $this->productImageFactory->createNew();
696
        $productImage->setFile(new UploadedFile($filesPath . $imagePath, basename($imagePath)));
697
        $productImage->setType($imageType);
698
        $this->imageUploader->upload($productImage);
699
700
        $product->addImage($productImage);
701
702
        $this->objectManager->persist($product);
703
        $this->objectManager->flush();
704
    }
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)
711
    {
712
        $product->getVariants()->first()->setShippingCategory($shippingCategory);
713
        $this->objectManager->flush();
714
    }
715
716
    /**
717
     * @Given /^(this product) has been disabled$/
718
     */
719
    public function thisProductHasBeenDisabled(ProductInterface $product)
720
    {
721
        $product->disable();
722
        $this->objectManager->flush();
723
    }
724
725
    /**
726
     * @Given the product :product was renamed to :productName
727
     */
728
    public function theProductWasRenamedTo(ProductInterface $product, string $productName): void
729
    {
730
        $product->setName($productName);
731
732
        $this->objectManager->flush();
733
    }
734
735
    /**
736
     * @Given /^(this product) does not require shipping$/
737
     */
738
    public function thisProductDoesNotRequireShipping(ProductInterface $product): void
739
    {
740
        /** @var ProductVariantInterface $variant */
741
        foreach ($product->getVariants() as $variant) {
742
            $variant->setShippingRequired(false);
743
        }
744
745
        $this->objectManager->flush();
746
    }
747
748
    private function getPriceFromString(string $price): int
749
    {
750
        return (int) round((float) str_replace(['€', '£', '$'], '', $price) * 100, 2);
751
    }
752
753
    /**
754
     * @param string $productName
755
     *
756
     * @return ProductInterface
757
     */
758
    private function createProduct($productName, int $price = 100, ChannelInterface $channel = null)
759
    {
760
        if (null === $channel && $this->sharedStorage->has('channel')) {
761
            $channel = $this->sharedStorage->get('channel');
762
        }
763
764
        /** @var ProductInterface $product */
765
        $product = $this->productFactory->createWithVariant();
766
767
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
768
        $product->setName($productName);
769
        $product->setSlug($this->slugGenerator->generate($productName));
770
771
        if (null !== $channel) {
772
            $product->addChannel($channel);
773
774
            foreach ($channel->getLocales() as $locale) {
775
                $product->setFallbackLocale($locale->getCode());
776
                $product->setCurrentLocale($locale->getCode());
777
778
                $product->setName($productName);
779
                $product->setSlug($this->slugGenerator->generate($productName));
780
            }
781
        }
782
783
        /** @var ProductVariantInterface $productVariant */
784
        $productVariant = $this->defaultVariantResolver->getVariant($product);
785
786
        if (null !== $channel) {
787
            $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
788
        }
789
790
        $productVariant->setCode($product->getCode());
791
        $productVariant->setName($product->getName());
792
793
        return $product;
794
    }
795
796
    /**
797
     * @param string $value
798
     * @param string $code
799
     *
800
     * @return ProductOptionValueInterface
801
     */
802
    private function addProductOption(ProductOptionInterface $option, $value, $code)
803
    {
804
        /** @var ProductOptionValueInterface $optionValue */
805
        $optionValue = $this->productOptionValueFactory->createNew();
806
807
        $optionValue->setValue($value);
808
        $optionValue->setCode($code);
809
        $optionValue->setOption($option);
810
811
        $option->addValue($optionValue);
812
813
        return $optionValue;
814
    }
815
816
    private function saveProduct(ProductInterface $product)
817
    {
818
        $this->productRepository->add($product);
819
        $this->sharedStorage->set('product', $product);
820
    }
821
822
    /**
823
     * @param string $name
824
     *
825
     * @return NodeElement
826
     */
827
    private function getParameter($name)
828
    {
829
        return $this->minkParameters[$name] ?? null;
830
    }
831
832
    /**
833
     * @param string $productVariantName
834
     * @param int $price
835
     * @param string $code
836
     * @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...
837
     * @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...
838
     * @param bool $shippingRequired
839
     *
840
     * @return ProductVariantInterface
841
     */
842
    private function createProductVariant(
843
        ProductInterface $product,
844
        $productVariantName,
845
        $price,
846
        $code,
847
        ChannelInterface $channel = null,
848
        $position = null,
849
        $shippingRequired = true
850
    ) {
851
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_CHOICE);
852
853
        /** @var ProductVariantInterface $variant */
854
        $variant = $this->productVariantFactory->createNew();
855
856
        $variant->setName($productVariantName);
857
        $variant->setCode($code);
858
        $variant->setProduct($product);
859
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
860
        $variant->setPosition((null === $position) ? null : (int) $position);
861
        $variant->setShippingRequired($shippingRequired);
862
863
        $product->addVariant($variant);
864
865
        $this->objectManager->flush();
866
        $this->sharedStorage->set('variant', $variant);
867
868
        return $variant;
869
    }
870
871
    /**
872
     * @param string $name
873
     * @param string $locale
874
     */
875
    private function addProductTranslation(ProductInterface $product, $name, $locale)
876
    {
877
        /** @var ProductTranslationInterface $translation */
878
        $translation = $product->getTranslation($locale);
879
        if ($translation->getLocale() !== $locale) {
880
            /** @var ProductTranslationInterface $translation */
881
            $translation = $this->productTranslationFactory->createNew();
882
        }
883
884
        $translation->setLocale($locale);
885
        $translation->setName($name);
886
        $translation->setSlug($this->slugGenerator->generate($name));
887
888
        $product->addTranslation($translation);
889
    }
890
891
    /**
892
     * @param string $name
893
     * @param string $locale
894
     */
895
    private function addProductVariantTranslation(ProductVariantInterface $productVariant, $name, $locale)
896
    {
897
        /** @var ProductVariantTranslationInterface $translation */
898
        $translation = $this->productVariantTranslationFactory->createNew();
899
        $translation->setLocale($locale);
900
        $translation->setName($name);
901
902
        $productVariant->addTranslation($translation);
903
    }
904
905
    /**
906
     * @return ChannelPricingInterface
907
     */
908
    private function createChannelPricingForChannel(int $price, ChannelInterface $channel = null)
909
    {
910
        /** @var ChannelPricingInterface $channelPricing */
911
        $channelPricing = $this->channelPricingFactory->createNew();
912
        $channelPricing->setPrice($price);
913
        $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...
914
915
        return $channelPricing;
916
    }
917
918
    private function addOptionToProduct(ProductInterface $product, string $optionName, array $values): void
919
    {
920
        /** @var ProductOptionInterface $option */
921
        $option = $this->productOptionFactory->createNew();
922
923
        $option->setName($optionName);
924
        $option->setCode(StringInflector::nameToUppercaseCode($optionName));
925
926
        $this->sharedStorage->set(sprintf('%s_option', $optionName), $option);
927
928
        foreach ($values as $key => $value) {
929
            $optionValue = $this->addProductOption($option, $value, StringInflector::nameToUppercaseCode($value));
930
            $this->sharedStorage->set(sprintf('%s_option_%s_value', $value, strtolower($optionName)), $optionValue);
931
        }
932
933
        $product->addOption($option);
934
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
935
936
        $this->objectManager->persist($option);
937
        $this->objectManager->flush();
938
    }
939
}
940