Completed
Push — master ( 32d5c7...38a92b )
by Kamil
103:05 queued 77:43
created

ProductContext::itHasVariantNamedInAndIn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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