Completed
Push — 1.3 ( 97f34f...f86edd )
by Kamil
10:36
created

thisProductDoesNotRequireShipping()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.9666
c 0
b 0
f 0
cc 2
nc 2
nop 1
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)
507
    {
508
        /** @var ProductOptionInterface $option */
509
        $option = $this->productOptionFactory->createNew();
510
511
        $option->setName($optionName);
512
        $option->setCode(StringInflector::nameToUppercaseCode($optionName));
513
514
        $this->sharedStorage->set(sprintf('%s_option', $optionName), $option);
515
516
        foreach ($values as $key => $value) {
517
            $optionValue = $this->addProductOption($option, $value, StringInflector::nameToUppercaseCode($value));
518
            $this->sharedStorage->set(sprintf('%s_option_%s_value', $value, strtolower($optionName)), $optionValue);
519
        }
520
521
        $product->addOption($option);
522
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
523
524
        $this->objectManager->persist($option);
525
        $this->objectManager->flush();
526
    }
527
528
    /**
529
     * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/
530
     */
531
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
532
    {
533
        /** @var ProductVariantInterface $productVariant */
534
        $productVariant = $this->defaultVariantResolver->getVariant($product);
535
        $productVariant->setOnHand((int) $quantity);
536
537
        $this->objectManager->flush();
538
    }
539
540
    /**
541
     * @Given /^the (product "([^"]+)") is out of stock$/
542
     */
543
    public function theProductIsOutOfStock(ProductInterface $product)
544
    {
545
        /** @var ProductVariantInterface $productVariant */
546
        $productVariant = $this->defaultVariantResolver->getVariant($product);
547
        $productVariant->setTracked(true);
548
        $productVariant->setOnHand(0);
549
550
        $this->objectManager->flush();
551
    }
552
553
    /**
554
     * @When other customer has bought :quantity :product products by this time
555
     */
556
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
557
    {
558
        /** @var ProductVariantInterface $productVariant */
559
        $productVariant = $this->defaultVariantResolver->getVariant($product);
560
        $productVariant->setOnHand($productVariant->getOnHand() - $quantity);
561
562
        $this->objectManager->flush();
563
    }
564
565
    /**
566
     * @Given /^(this product) is tracked by the inventory$/
567
     * @Given /^(?:|the )("[^"]+" product) is(?:| also) tracked by the inventory$/
568
     */
569
    public function thisProductIsTrackedByTheInventory(ProductInterface $product)
570
    {
571
        /** @var ProductVariantInterface $productVariant */
572
        $productVariant = $this->defaultVariantResolver->getVariant($product);
573
        $productVariant->setTracked(true);
574
575
        $this->objectManager->flush();
576
    }
577
578
    /**
579
     * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/
580
     */
581
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, int $price)
582
    {
583
        /** @var ProductVariantInterface $variant */
584
        $variant = $this->productVariantFactory->createNew();
585
586
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_%s_value', $optionValueName, $optionName));
587
588
        $variant->addOptionValue($optionValue);
589
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $this->sharedStorage->get('channel')));
590
        $variant->setCode(sprintf('%s_%s', $product->getCode(), $optionValueName));
591
        $variant->setName($product->getName());
592
593
        $product->addVariant($variant);
594
        $this->objectManager->flush();
595
    }
596
597
    /**
598
     * @Given the :product product's :optionValueName size belongs to :shippingCategory shipping category
599
     */
600
    public function thisProductSizeBelongsToShippingCategory(ProductInterface $product, $optionValueName, ShippingCategoryInterface $shippingCategory)
601
    {
602
        $code = sprintf('%s_%s', $product->getCode(), $optionValueName);
603
        /** @var ProductVariantInterface $productVariant */
604
        $productVariant = $product->getVariants()->filter(function ($variant) use ($code) {
605
            return $code === $variant->getCode();
606
        })->first();
607
608
        Assert::notNull($productVariant, sprintf('Product variant with given code %s not exists!', $code));
609
610
        $productVariant->setShippingCategory($shippingCategory);
611
        $this->objectManager->flush();
612
    }
613
614
    /**
615
     * @Given /^(this product) has (this product option)$/
616
     * @Given /^(this product) has (?:a|an) ("[^"]+" option)$/
617
     */
618
    public function thisProductHasThisProductOption(ProductInterface $product, ProductOptionInterface $option)
619
    {
620
        $product->addOption($option);
621
622
        $this->objectManager->flush();
623
    }
624
625
    /**
626
     * @Given /^(this product) has all possible variants$/
627
     */
628
    public function thisProductHasAllPossibleVariants(ProductInterface $product)
629
    {
630
        try {
631
            foreach ($product->getVariants() as $productVariant) {
632
                $product->removeVariant($productVariant);
633
            }
634
635
            $this->productVariantGenerator->generate($product);
636
        } catch (\InvalidArgumentException $exception) {
637
            /** @var ProductVariantInterface $productVariant */
638
            $productVariant = $this->productVariantFactory->createNew();
639
640
            $product->addVariant($productVariant);
641
        }
642
643
        $i = 0;
644
        /** @var ProductVariantInterface $productVariant */
645
        foreach ($product->getVariants() as $productVariant) {
646
            $productVariant->setCode(sprintf('%s-variant-%d', $product->getCode(), $i));
647
648
            foreach ($product->getChannels() as $channel) {
649
                $productVariant->addChannelPricing($this->createChannelPricingForChannel(1000, $channel));
650
            }
651
652
            ++$i;
653
        }
654
655
        $this->objectManager->flush();
656
    }
657
658
    /**
659
     * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/
660
     */
661
    public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant)
662
    {
663
        $productVariant->setTracked(true);
664
        $productVariant->setOnHand((int) $quantity);
665
666
        $this->objectManager->flush();
667
    }
668
669
    /**
670
     * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/
671
     */
672
    public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant)
673
    {
674
        $productVariant->setTracked(true);
675
676
        $this->objectManager->flush();
677
    }
678
679
    /**
680
     * @Given /^(this product)'s price is ("[^"]+")$/
681
     * @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/
682
     * @Given /^(this product) price has been changed to ("[^"]+")$/
683
     */
684
    public function theProductChangedItsPriceTo(ProductInterface $product, int $price)
685
    {
686
        /** @var ProductVariantInterface $productVariant */
687
        $productVariant = $this->defaultVariantResolver->getVariant($product);
688
        $channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
689
        $channelPricing->setPrice($price);
690
691
        $this->objectManager->flush();
692
    }
693
694
    /**
695
     * @Given /^(this product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
696
     * @Given /^the ("[^"]+" product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
697
     * @Given /^(it)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
698
     */
699
    public function thisProductHasAnImageWithType(ProductInterface $product, $imagePath, $imageType)
700
    {
701
        $filesPath = $this->getParameter('files_path');
702
703
        /** @var ImageInterface $productImage */
704
        $productImage = $this->productImageFactory->createNew();
705
        $productImage->setFile(new UploadedFile($filesPath . $imagePath, basename($imagePath)));
706
        $productImage->setType($imageType);
707
        $this->imageUploader->upload($productImage);
708
709
        $product->addImage($productImage);
710
711
        $this->objectManager->persist($product);
712
        $this->objectManager->flush();
713
    }
714
715
    /**
716
     * @Given /^(this product) belongs to ("([^"]+)" shipping category)$/
717
     * @Given product :product shipping category has been changed to :shippingCategory
718
     */
719
    public function thisProductBelongsToShippingCategory(ProductInterface $product, ShippingCategoryInterface $shippingCategory)
720
    {
721
        $product->getVariants()->first()->setShippingCategory($shippingCategory);
722
        $this->objectManager->flush();
723
    }
724
725
    /**
726
     * @Given /^(this product) has been disabled$/
727
     */
728
    public function thisProductHasBeenDisabled(ProductInterface $product)
729
    {
730
        $product->disable();
731
        $this->objectManager->flush();
732
    }
733
734
    /**
735
     * @Given the product :product was renamed to :productName
736
     */
737
    public function theProductWasRenamedTo(ProductInterface $product, string $productName): void
738
    {
739
        $product->setName($productName);
740
741
        $this->objectManager->flush();
742
    }
743
744
    /**
745
     * @Given /^(this product) does not require shipping$/
746
     */
747
    public function thisProductDoesNotRequireShipping(ProductInterface $product): void
748
    {
749
        /** @var ProductVariantInterface $variant */
750
        foreach ($product->getVariants() as $variant) {
751
            $variant->setShippingRequired(false);
752
        }
753
754
        $this->objectManager->flush();
755
    }
756
757
    private function getPriceFromString(string $price): int
758
    {
759
        return (int) round((float) str_replace(['€', '£', '$'], '', $price) * 100, 2);
760
    }
761
762
    /**
763
     * @param string $productName
764
     *
765
     * @return ProductInterface
766
     */
767
    private function createProduct($productName, int $price = 100, ChannelInterface $channel = null)
768
    {
769
        if (null === $channel && $this->sharedStorage->has('channel')) {
770
            $channel = $this->sharedStorage->get('channel');
771
        }
772
773
        /** @var ProductInterface $product */
774
        $product = $this->productFactory->createWithVariant();
775
776
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
777
        $product->setName($productName);
778
        $product->setSlug($this->slugGenerator->generate($productName));
779
780
        if (null !== $channel) {
781
            $product->addChannel($channel);
782
783
            foreach ($channel->getLocales() as $locale) {
784
                $product->setFallbackLocale($locale->getCode());
785
                $product->setCurrentLocale($locale->getCode());
786
787
                $product->setName($productName);
788
                $product->setSlug($this->slugGenerator->generate($productName));
789
            }
790
        }
791
792
        /** @var ProductVariantInterface $productVariant */
793
        $productVariant = $this->defaultVariantResolver->getVariant($product);
794
795
        if (null !== $channel) {
796
            $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
797
        }
798
799
        $productVariant->setCode($product->getCode());
800
        $productVariant->setName($product->getName());
801
802
        return $product;
803
    }
804
805
    /**
806
     * @param string $value
807
     * @param string $code
808
     *
809
     * @return ProductOptionValueInterface
810
     */
811
    private function addProductOption(ProductOptionInterface $option, $value, $code)
812
    {
813
        /** @var ProductOptionValueInterface $optionValue */
814
        $optionValue = $this->productOptionValueFactory->createNew();
815
816
        $optionValue->setValue($value);
817
        $optionValue->setCode($code);
818
        $optionValue->setOption($option);
819
820
        $option->addValue($optionValue);
821
822
        return $optionValue;
823
    }
824
825
    private function saveProduct(ProductInterface $product)
826
    {
827
        $this->productRepository->add($product);
828
        $this->sharedStorage->set('product', $product);
829
    }
830
831
    /**
832
     * @param string $name
833
     *
834
     * @return NodeElement
835
     */
836
    private function getParameter($name)
837
    {
838
        return $this->minkParameters[$name] ?? null;
839
    }
840
841
    /**
842
     * @param string $productVariantName
843
     * @param int $price
844
     * @param string $code
845
     * @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...
846
     * @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...
847
     * @param bool $shippingRequired
848
     *
849
     * @return ProductVariantInterface
850
     */
851
    private function createProductVariant(
852
        ProductInterface $product,
853
        $productVariantName,
854
        $price,
855
        $code,
856
        ChannelInterface $channel = null,
857
        $position = null,
858
        $shippingRequired = true
859
    ) {
860
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_CHOICE);
861
862
        /** @var ProductVariantInterface $variant */
863
        $variant = $this->productVariantFactory->createNew();
864
865
        $variant->setName($productVariantName);
866
        $variant->setCode($code);
867
        $variant->setProduct($product);
868
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
869
        $variant->setPosition((null === $position) ? null : (int) $position);
870
        $variant->setShippingRequired($shippingRequired);
871
872
        $product->addVariant($variant);
873
874
        $this->objectManager->flush();
875
        $this->sharedStorage->set('variant', $variant);
876
877
        return $variant;
878
    }
879
880
    /**
881
     * @param string $name
882
     * @param string $locale
883
     */
884
    private function addProductTranslation(ProductInterface $product, $name, $locale)
885
    {
886
        /** @var ProductTranslationInterface $translation */
887
        $translation = $product->getTranslation($locale);
888
        if ($translation->getLocale() !== $locale) {
889
            /** @var ProductTranslationInterface $translation */
890
            $translation = $this->productTranslationFactory->createNew();
891
        }
892
893
        $translation->setLocale($locale);
894
        $translation->setName($name);
895
        $translation->setSlug($this->slugGenerator->generate($name));
896
897
        $product->addTranslation($translation);
898
    }
899
900
    /**
901
     * @param string $name
902
     * @param string $locale
903
     */
904
    private function addProductVariantTranslation(ProductVariantInterface $productVariant, $name, $locale)
905
    {
906
        /** @var ProductVariantTranslationInterface $translation */
907
        $translation = $this->productVariantTranslationFactory->createNew();
908
        $translation->setLocale($locale);
909
        $translation->setName($name);
910
911
        $productVariant->addTranslation($translation);
912
    }
913
914
    /**
915
     * @return ChannelPricingInterface
916
     */
917
    private function createChannelPricingForChannel(int $price, ChannelInterface $channel = null)
918
    {
919
        /** @var ChannelPricingInterface $channelPricing */
920
        $channelPricing = $this->channelPricingFactory->createNew();
921
        $channelPricing->setPrice($price);
922
        $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...
923
924
        return $channelPricing;
925
    }
926
}
927