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

storeHasAProductPricedAtAvailableInChannels()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.7666
c 0
b 0
f 0
cc 3
nc 3
nop 3
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