Completed
Push — master ( d81c19...f57266 )
by Kamil
20s
created

src/Sylius/Behat/Context/Setup/ProductContext.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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