Completed
Push — product-variant ( 7e1caf )
by Kamil
20:29
created

ProductContext::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 35
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

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

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sylius\Behat\Context\Setup;
13
14
use Behat\Behat\Context\Context;
15
use Behat\Gherkin\Node\TableNode;
16
use Behat\Mink\Element\NodeElement;
17
use Doctrine\Common\Persistence\ObjectManager;
18
use Sylius\Behat\Service\SharedStorageInterface;
19
use Sylius\Component\Attribute\Factory\AttributeFactoryInterface;
20
use Sylius\Component\Core\Formatter\StringInflector;
21
use Sylius\Component\Core\Model\ChannelInterface;
22
use Sylius\Component\Core\Model\ChannelPricingInterface;
23
use Sylius\Component\Core\Model\ImageInterface;
24
use Sylius\Component\Core\Model\ProductInterface;
25
use Sylius\Component\Core\Model\ProductTranslationInterface;
26
use Sylius\Component\Core\Model\ProductVariantInterface;
27
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
28
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
29
use Sylius\Component\Product\Factory\ProductFactoryInterface;
30
use Sylius\Component\Product\Generator\ProductVariantGeneratorInterface;
31
use Sylius\Component\Product\Generator\SlugGeneratorInterface;
32
use Sylius\Component\Product\Model\ProductOptionInterface;
33
use Sylius\Component\Product\Model\ProductOptionValueInterface;
34
use Sylius\Component\Product\Model\ProductVariantTranslationInterface;
35
use Sylius\Component\Product\Resolver\ProductVariantResolverInterface;
36
use Sylius\Component\Resource\Factory\FactoryInterface;
37
use Sylius\Component\Resource\Model\TranslationInterface;
38
use Sylius\Component\Resource\Repository\RepositoryInterface;
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 the store( also) has a product :productName with code :code
215
     * @Given the store( also) has a product :productName with code :code, created at :date
216
     */
217
    public function storeHasProductWithCode($productName, $code, $date = null)
218
    {
219
        $product = $this->createProduct($productName);
220
        $product->setCreatedAt(new \DateTime($date));
221
        $product->setCode($code);
222
223
        $this->saveProduct($product);
224
    }
225
226
    /**
227
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") available in (channel "[^"]+") and (channel "[^"]+")$/
228
     */
229
    public function storeHasAProductPricedAtAvailableInChannels($productName, $price = 100, ...$channels)
230
    {
231
        $product = $this->createProduct($productName, $price);
232
        /** @var ProductVariantInterface $productVariant */
233
        $productVariant = $this->defaultVariantResolver->getVariant($product);
234
235
        foreach ($channels as $channel) {
236
            $product->addChannel($channel);
237
            if (!$productVariant->hasChannelPricingForChannel($channel)) {
238
                $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
239
            }
240
        }
241
242
        $this->saveProduct($product);
243
    }
244
245
    /**
246
     * @Given /^(this product) is named "([^"]+)" (in the "([^"]+)" locale)$/
247
     * @Given /^the (product "[^"]+") is named "([^"]+)" (in the "([^"]+)" locale)$/
248
     */
249
    public function thisProductIsNamedIn(ProductInterface $product, $name, $locale)
250
    {
251
        $this->addProductTranslation($product, $name, $locale);
252
253
        $this->objectManager->flush();
254
    }
255
256
    /**
257
     * @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
258
     */
259
    public function theStoreHasProductNamedInAndIn($firstName, $firstLocale, $secondName, $secondLocale)
260
    {
261
        $product = $this->createProduct($firstName);
262
263
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
264
        foreach ($names as $name => $locale) {
265
            $this->addProductTranslation($product, $name, $locale);
266
        }
267
268
        $this->saveProduct($product);
269
    }
270
271
    /**
272
     * @Given /^the store has(?:| a| an) "([^"]+)" configurable product$/
273
     * @Given /^the store has(?:| a| an) "([^"]+)" configurable product with "([^"]+)" slug$/
274
     */
275
    public function storeHasAConfigurableProduct($productName, $slug = null)
276
    {
277
        /** @var ChannelInterface|null $channel */
278
        $channel = null;
279
        if ($this->sharedStorage->has('channel')) {
280
            $channel = $this->sharedStorage->get('channel');
281
        }
282
283
        /** @var ProductInterface $product */
284
        $product = $this->productFactory->createNew();
285
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
286
287
        if (null !== $channel) {
288
            $product->addChannel($channel);
289
290
            foreach ($channel->getLocales() as $locale) {
291
                $product->setFallbackLocale($locale->getCode());
292
                $product->setCurrentLocale($locale->getCode());
293
294
                $product->setName($productName);
295
                $product->setSlug($slug ?: $this->slugGenerator->generate($productName));
296
            }
297
        }
298
299
        $this->saveProduct($product);
300
    }
301
302
    /**
303
     * @Given the store has( also) :firstProductName and :secondProductName products
304
     * @Given the store has( also) :firstProductName, :secondProductName and :thirdProductName products
305
     * @Given the store has( also) :firstProductName, :secondProductName, :thirdProductName and :fourthProductName products
306
     */
307
    public function theStoreHasProducts(...$productsNames)
308
    {
309
        foreach ($productsNames as $productName) {
310
            $this->saveProduct($this->createProduct($productName));
311
        }
312
    }
313
314
    /**
315
     * @Given /^(this channel) has "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" products$/
316
     */
317
    public function thisChannelHasProducts(ChannelInterface $channel, ...$productsNames)
318
    {
319
        foreach ($productsNames as $productName) {
320
            $product = $this->createProduct($productName, 0, $channel);
321
322
            $this->saveProduct($product);
323
        }
324
    }
325
326
    /**
327
     * @Given /^the (product "[^"]+") has(?:| a) "([^"]+)" variant priced at ("[^"]+")$/
328
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+")$/
329
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") in ("([^"]+)" channel)$/
330
     */
331
    public function theProductHasVariantPricedAt(
332
        ProductInterface $product,
333
        $productVariantName,
334
        $price,
335
        ChannelInterface $channel = null
336
    ) {
337
        $this->createProductVariant(
338
            $product,
339
            $productVariantName,
340
            $price,
341
            StringInflector::nameToUppercaseCode($productVariantName),
342
            (null !== $channel) ? $channel : $this->sharedStorage->get('channel')
343
        );
344
    }
345
346
    /**
347
     * @Given /^the (product "[^"]+") has(?:| a| an) "([^"]+)" variant$/
348
     * @Given /^(this product) has(?:| a| an) "([^"]+)" variant$/
349
     * @Given /^(this product) has "([^"]+)", "([^"]+)" and "([^"]+)" variants$/
350
     */
351
    public function theProductHasVariants(ProductInterface $product, ...$variantNames)
352
    {
353
        $channel = $this->sharedStorage->get('channel');
354
355
        foreach ($variantNames as $name) {
356
            $this->createProductVariant(
357
                $product,
358
                $name,
359
                0,
360
                StringInflector::nameToUppercaseCode($name),
361
                $channel
362
            );
363
        }
364
    }
365
366
    /**
367
     * @Given /^the (product "[^"]+")(?:| also) has a nameless variant with code "([^"]+)"$/
368
     * @Given /^(this product)(?:| also) has a nameless variant with code "([^"]+)"$/
369
     * @Given /^(it)(?:| also) has a nameless variant with code "([^"]+)"$/
370
     */
371
    public function theProductHasNamelessVariantWithCode(ProductInterface $product, $variantCode)
372
    {
373
        $channel = $this->sharedStorage->get('channel');
374
375
        $this->createProductVariant($product, null, 0, $variantCode, $channel);
376
    }
377
378
    /**
379
     * @Given /^the (product "[^"]+")(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
380
     * @Given /^(this product)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
381
     * @Given /^(it)(?:| also) has(?:| a| an) "([^"]+)" variant with code "([^"]+)"$/
382
     */
383
    public function theProductHasVariantWithCode(ProductInterface $product, $variantName, $variantCode)
384
    {
385
        $channel = $this->sharedStorage->get('channel');
386
387
        $this->createProductVariant($product, $variantName, 0, $variantCode, $channel);
388
    }
389
390
    /**
391
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") which does not require shipping$/
392
     */
393
    public function theProductHasVariantWhichDoesNotRequireShipping(
394
        ProductInterface $product,
395
        $productVariantName,
396
        $price
397
    ) {
398
        $this->createProductVariant(
399
            $product,
400
            $productVariantName,
401
            $price,
402
            StringInflector::nameToUppercaseCode($productVariantName),
403
            $this->sharedStorage->get('channel'),
404
            null,
405
            false
406
        );
407
    }
408
409
    /**
410
     * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant$/
411
     * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/
412
     * @Given /^(this product) has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/
413
     */
414
    public function theProductHasVariantAtPosition(
415
        ProductInterface $product,
416
        $productVariantName,
417
        $position = null
418
    ) {
419
        $this->createProductVariant(
420
            $product,
421
            $productVariantName,
422
            0,
423
            StringInflector::nameToUppercaseCode($productVariantName),
424
            $this->sharedStorage->get('channel'),
425
            $position
426
        );
427
    }
428
429
    /**
430
     * @Given /^(this variant) is also priced at ("[^"]+") in ("([^"]+)" channel)$/
431
     */
432
    public function thisVariantIsAlsoPricedAtInChannel(ProductVariantInterface $productVariant, $price, ChannelInterface $channel)
433
    {
434
        $productVariant->addChannelPricing($this->createChannelPricingForChannel(
435
            $this->getPriceFromString(str_replace(['$', '€', '£'], '', $price)),
436
            $channel
437
        ));
438
439
        $this->objectManager->flush();
440
    }
441
442
    /**
443
     * @Given /^(it|this product) has(?:| also) variant named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
444
     */
445
    public function itHasVariantNamedInAndIn(ProductInterface $product, $firstName, $firstLocale, $secondName, $secondLocale)
446
    {
447
        $productVariant = $this->createProductVariant(
448
            $product,
449
            $firstName,
450
            100,
451
            StringInflector::nameToUppercaseCode($firstName),
452
            $this->sharedStorage->get('channel')
453
        );
454
455
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
456
        foreach ($names as $name => $locale) {
457
            $this->addProductVariantTranslation($productVariant, $name, $locale);
458
        }
459
460
        $this->objectManager->flush();
461
    }
462
463
    /**
464
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") identified by "([^"]+)"$/
465
     */
466
    public function theProductHasVariantPricedAtIdentifiedBy(
467
        ProductInterface $product,
468
        $productVariantName,
469
        $price,
470
        $code
471
    ) {
472
        $this->createProductVariant($product, $productVariantName, $price, $code, $this->sharedStorage->get('channel'));
473
    }
474
475
    /**
476
     * @Given /^(this product) only variant was renamed to "([^"]+)"$/
477
     */
478
    public function productOnlyVariantWasRenamed(ProductInterface $product, $variantName)
479
    {
480
        Assert::true($product->isSimple());
481
482
        /** @var ProductVariantInterface $productVariant */
483
        $productVariant = $product->getVariants()->first();
484
        $productVariant->setName($variantName);
485
486
        $this->objectManager->flush();
487
    }
488
489
    /**
490
     * @Given /^there is product "([^"]+)" available in ((?:this|that|"[^"]+") channel)$/
491
     * @Given /^the store has a product "([^"]+)" available in ("([^"]+)" channel)$/
492
     */
493
    public function thereIsProductAvailableInGivenChannel($productName, ChannelInterface $channel)
494
    {
495
        $product = $this->createProduct($productName, 0, $channel);
496
497
        $this->saveProduct($product);
498
    }
499
500
    /**
501
     * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/
502
     */
503
    public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory)
504
    {
505
        /** @var ProductVariantInterface $variant */
506
        $variant = $this->defaultVariantResolver->getVariant($product);
507
        $variant->setTaxCategory($taxCategory);
508
509
        $this->objectManager->flush();
510
    }
511
512
    /**
513
     * @Given /^(it) comes in the following variations:$/
514
     */
515
    public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
516
    {
517
        $channel = $this->sharedStorage->get('channel');
518
519
        foreach ($table->getHash() as $variantHash) {
520
            /** @var ProductVariantInterface $variant */
521
            $variant = $this->productVariantFactory->createNew();
522
523
            $variant->setName($variantHash['name']);
524
            $variant->setCode(StringInflector::nameToUppercaseCode($variantHash['name']));
525
            $variant->addChannelPricing($this->createChannelPricingForChannel(
526
                $this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])),
527
                $channel
528
            ));
529
530
            $variant->setProduct($product);
531
            $product->addVariant($variant);
532
        }
533
534
        $this->objectManager->flush();
535
    }
536
537
    /**
538
     * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/
539
     */
540
    public function productVariantBelongsToTaxCategory(
541
        ProductVariantInterface $productVariant,
542
        TaxCategoryInterface $taxCategory
543
    ) {
544
        $productVariant->setTaxCategory($taxCategory);
545
        $this->objectManager->flush($productVariant);
546
    }
547
548
    /**
549
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)" and "([^"]+)"$/
550
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)", "([^"]+)" and "([^"]+)"$/
551
     */
552
    public function thisProductHasOptionWithValues(ProductInterface $product, $optionName, ...$values)
553
    {
554
        /** @var ProductOptionInterface $option */
555
        $option = $this->productOptionFactory->createNew();
556
557
        $option->setName($optionName);
558
        $option->setCode(StringInflector::nameToUppercaseCode($optionName));
559
560
        $this->sharedStorage->set(sprintf('%s_option', $optionName), $option);
561
562
        foreach ($values as $key => $value) {
563
            $optionValue = $this->addProductOption($option, $value, StringInflector::nameToUppercaseCode($value));
564
            $this->sharedStorage->set(sprintf('%s_option_%s_value', $value, strtolower($optionName)), $optionValue);
565
        }
566
567
        $product->addOption($option);
568
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
569
570
        $this->objectManager->persist($option);
571
        $this->objectManager->flush();
572
    }
573
574
    /**
575
     * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/
576
     */
577
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
578
    {
579
        /** @var ProductVariantInterface $productVariant */
580
        $productVariant = $this->defaultVariantResolver->getVariant($product);
581
        $productVariant->setOnHand($quantity);
582
583
        $this->objectManager->flush();
584
    }
585
586
    /**
587
     * @Given /^the (product "([^"]+)") is out of stock$/
588
     */
589
    public function theProductIsOutOfStock(ProductInterface $product)
590
    {
591
        /** @var ProductVariantInterface $productVariant */
592
        $productVariant = $this->defaultVariantResolver->getVariant($product);
593
        $productVariant->setTracked(true);
594
        $productVariant->setOnHand(0);
595
596
        $this->objectManager->flush();
597
    }
598
599
    /**
600
     * @When other customer has bought :quantity :product products by this time
601
     */
602
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
603
    {
604
        /** @var ProductVariantInterface $productVariant */
605
        $productVariant = $this->defaultVariantResolver->getVariant($product);
606
        $productVariant->setOnHand($productVariant->getOnHand() - $quantity);
607
608
        $this->objectManager->flush();
609
    }
610
611
    /**
612
     * @Given /^(this product) is tracked by the inventory$/
613
     * @Given /^(?:|the )("[^"]+" product) is(?:| also) tracked by the inventory$/
614
     */
615
    public function thisProductIsTrackedByTheInventory(ProductInterface $product)
616
    {
617
        /** @var ProductVariantInterface $productVariant */
618
        $productVariant = $this->defaultVariantResolver->getVariant($product);
619
        $productVariant->setTracked(true);
620
621
        $this->objectManager->flush();
622
    }
623
624
    /**
625
     * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/
626
     */
627
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, $price)
628
    {
629
        /** @var ProductVariantInterface $variant */
630
        $variant = $this->productVariantFactory->createNew();
631
632
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_%s_value', $optionValueName, $optionName));
633
634
        $variant->addOptionValue($optionValue);
635
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $this->sharedStorage->get('channel')));
636
        $variant->setCode(sprintf('%s_%s', $product->getCode(), $optionValueName));
637
        $variant->setName($product->getName());
638
639
        $product->addVariant($variant);
640
        $this->objectManager->flush();
641
    }
642
643
    /**
644
     * @Given the :product product's :optionValueName size belongs to :shippingCategory shipping category
645
     */
646
    public function thisProductSizeBelongsToShippingCategory(ProductInterface $product, $optionValueName, ShippingCategoryInterface $shippingCategory)
647
    {
648
        $code = sprintf('%s_%s', $product->getCode(), $optionValueName);
649
        /** @var ProductVariantInterface $productVariant */
650
        $productVariant = $product->getVariants()->filter(function ($variant) use ($code) {
651
            return $code === $variant->getCode();
652
        })->first();
653
654
        Assert::notNull($productVariant, sprintf('Product variant with given code %s not exists!', $code));
655
656
        $productVariant->setShippingCategory($shippingCategory);
657
        $this->objectManager->flush();
658
    }
659
660
    /**
661
     * @Given /^(this product) has (this product option)$/
662
     * @Given /^(this product) has (?:a|an) ("[^"]+" option)$/
663
     */
664
    public function thisProductHasThisProductOption(ProductInterface $product, ProductOptionInterface $option)
665
    {
666
        $product->addOption($option);
667
668
        $this->objectManager->flush();
669
    }
670
671
    /**
672
     * @Given /^(this product) has all possible variants$/
673
     */
674
    public function thisProductHasAllPossibleVariants(ProductInterface $product)
675
    {
676
        try {
677
            foreach ($product->getVariants() as $productVariant) {
678
                $product->removeVariant($productVariant);
679
            }
680
681
            $this->productVariantGenerator->generate($product);
682
        } catch (\InvalidArgumentException $exception) {
683
            /** @var ProductVariantInterface $productVariant */
684
            $productVariant = $this->productVariantFactory->createNew();
685
686
            $product->addVariant($productVariant);
687
        }
688
689
        $i = 0;
690
        /** @var ProductVariantInterface $productVariant */
691
        foreach ($product->getVariants() as $productVariant) {
692
            $productVariant->setCode(sprintf('%s-variant-%d', $product->getCode(), $i));
693
694
            foreach ($product->getChannels() as $channel) {
695
                $productVariant->addChannelPricing($this->createChannelPricingForChannel(1000, $channel));
696
            }
697
698
            ++$i;
699
        }
700
701
        $this->objectManager->flush();
702
    }
703
704
    /**
705
     * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/
706
     */
707
    public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant)
708
    {
709
        $productVariant->setTracked(true);
710
        $productVariant->setOnHand($quantity);
711
712
        $this->objectManager->flush();
713
    }
714
715
    /**
716
     * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/
717
     */
718
    public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant)
719
    {
720
        $productVariant->setTracked(true);
721
722
        $this->objectManager->flush();
723
    }
724
725
    /**
726
     * @Given /^(this product)'s price is ("[^"]+")$/
727
     * @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/
728
     * @Given /^(this product) price has been changed to ("[^"]+")$/
729
     */
730
    public function theProductChangedItsPriceTo(ProductInterface $product, $price)
731
    {
732
        /** @var ProductVariantInterface $productVariant */
733
        $productVariant = $this->defaultVariantResolver->getVariant($product);
734
        $channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
735
        $channelPricing->setPrice($price);
736
737
        $this->objectManager->flush();
738
    }
739
740
    /**
741
     * @Given /^(this product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
742
     * @Given /^the ("[^"]+" product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
743
     * @Given /^(it)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
744
     */
745
    public function thisProductHasAnImageWithType(ProductInterface $product, $imagePath, $imageType)
746
    {
747
        $filesPath = $this->getParameter('files_path');
748
749
        /** @var ImageInterface $productImage */
750
        $productImage = $this->productImageFactory->createNew();
751
        $productImage->setFile(new UploadedFile($filesPath.$imagePath, basename($imagePath)));
752
        $productImage->setType($imageType);
753
        $this->imageUploader->upload($productImage);
754
755
        $product->addImage($productImage);
756
757
        $this->objectManager->flush($product);
758
    }
759
760
    /**
761
     * @Given /^(this product) belongs to ("([^"]+)" shipping category)$/
762
     * @Given product :product shipping category has been changed to :shippingCategory
763
     */
764
    public function thisProductBelongsToShippingCategory(ProductInterface $product, ShippingCategoryInterface $shippingCategory)
765
    {
766
        $product->getVariants()->first()->setShippingCategory($shippingCategory);
767
        $this->objectManager->flush();
768
    }
769
770
    /**
771
     * @Given /^(this product) has been disabled$/
772
     */
773
    public function thisProductHasBeenDisabled(ProductInterface $product)
774
    {
775
        $product->disable();
776
        $this->objectManager->flush();
777
    }
778
779
    /**
780
     * @param string $price
781
     *
782
     * @return int
783
     */
784
    private function getPriceFromString($price)
785
    {
786
        return (int) round($price * 100, 2);
787
    }
788
789
    /**
790
     * @param string $productName
791
     * @param int $price
792
     * @param ChannelInterface|null $channel
793
     *
794
     * @return ProductInterface
795
     */
796
    private function createProduct($productName, $price = 100, ChannelInterface $channel = null)
797
    {
798
        if (null === $channel && $this->sharedStorage->has('channel')) {
799
            $channel = $this->sharedStorage->get('channel');
800
        }
801
802
        /** @var ProductInterface $product */
803
        $product = $this->productFactory->createWithVariant();
804
805
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
806
        $product->setName($productName);
807
        $product->setSlug($this->slugGenerator->generate($productName));
808
809
        if (null !== $channel) {
810
            $product->addChannel($channel);
811
812
            foreach ($channel->getLocales() as $locale) {
813
                $product->setFallbackLocale($locale->getCode());
814
                $product->setCurrentLocale($locale->getCode());
815
816
                $product->setName($productName);
817
                $product->setSlug($this->slugGenerator->generate($productName));
818
            }
819
        }
820
821
        /** @var ProductVariantInterface $productVariant */
822
        $productVariant = $this->defaultVariantResolver->getVariant($product);
823
824
        if (null !== $channel) {
825
            $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
826
        }
827
828
        $productVariant->setCode($product->getCode());
829
        $productVariant->setName($product->getName());
830
831
        return $product;
832
    }
833
834
    /**
835
     * @param ProductOptionInterface $option
836
     * @param string $value
837
     * @param string $code
838
     *
839
     * @return ProductOptionValueInterface
840
     */
841
    private function addProductOption(ProductOptionInterface $option, $value, $code)
842
    {
843
        /** @var ProductOptionValueInterface $optionValue */
844
        $optionValue = $this->productOptionValueFactory->createNew();
845
846
        $optionValue->setValue($value);
847
        $optionValue->setCode($code);
848
        $optionValue->setOption($option);
849
850
        $option->addValue($optionValue);
851
852
        return $optionValue;
853
    }
854
855
    /**
856
     * @param ProductInterface $product
857
     */
858
    private function saveProduct(ProductInterface $product)
859
    {
860
        $this->productRepository->add($product);
861
        $this->sharedStorage->set('product', $product);
862
    }
863
864
    /**
865
     * @param string $name
866
     *
867
     * @return NodeElement
868
     */
869
    private function getParameter($name)
870
    {
871
        return isset($this->minkParameters[$name]) ? $this->minkParameters[$name] : null;
872
    }
873
874
    /**
875
     * @param ProductInterface $product
876
     * @param $productVariantName
877
     * @param int $price
878
     * @param string $code
879
     * @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...
880
     * @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...
881
     * @param bool $shippingRequired
882
     *
883
     * @return ProductVariantInterface
884
     */
885
    private function createProductVariant(
886
        ProductInterface $product,
887
        $productVariantName,
888
        $price,
889
        $code,
890
        ChannelInterface $channel = null,
891
        $position = null,
892
        $shippingRequired = true
893
    ) {
894
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_CHOICE);
895
896
        /** @var ProductVariantInterface $variant */
897
        $variant = $this->productVariantFactory->createNew();
898
899
        $variant->setName($productVariantName);
900
        $variant->setCode($code);
901
        $variant->setProduct($product);
902
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
903
        $variant->setPosition($position);
904
        $variant->setShippingRequired($shippingRequired);
905
906
        $product->addVariant($variant);
907
908
        $this->objectManager->flush();
909
        $this->sharedStorage->set('variant', $variant);
910
911
        return $variant;
912
    }
913
914
    /**
915
     * @param ProductInterface $product
916
     * @param string $name
917
     * @param string $locale
918
     */
919
    private function addProductTranslation(ProductInterface $product, $name, $locale)
920
    {
921
        /** @var ProductTranslationInterface|TranslationInterface $translation */
922
        $translation = $product->getTranslation($locale);
923
        if ($translation->getLocale() !== $locale) {
0 ignored issues
show
Bug introduced by
The method getLocale does only exist in Sylius\Component\Resourc...el\TranslationInterface, but not in Sylius\Component\Core\Mo...uctTranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
924
            $translation = $this->productTranslationFactory->createNew();
925
        }
926
927
        $translation->setLocale($locale);
928
        $translation->setName($name);
929
        $translation->setSlug($this->slugGenerator->generate($name));
930
931
        $product->addTranslation($translation);
932
    }
933
934
    /**
935
     * @param ProductVariantInterface $productVariant
936
     * @param string $name
937
     * @param string $locale
938
     */
939
    private function addProductVariantTranslation(ProductVariantInterface $productVariant, $name, $locale)
940
    {
941
        /** @var ProductVariantTranslationInterface|TranslationInterface $translation */
942
        $translation = $this->productVariantTranslationFactory->createNew();
943
        $translation->setLocale($locale);
0 ignored issues
show
Bug introduced by
The method setLocale does only exist in Sylius\Component\Resourc...el\TranslationInterface, but not in Sylius\Component\Product...antTranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
944
        $translation->setName($name);
0 ignored issues
show
Bug introduced by
The method setName does only exist in Sylius\Component\Product...antTranslationInterface, but not in Sylius\Component\Resourc...el\TranslationInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
945
946
        $productVariant->addTranslation($translation);
0 ignored issues
show
Bug introduced by
It seems like $translation defined by $this->productVariantTra...ionFactory->createNew() on line 942 can also be of type object<Sylius\Component\...ntTranslationInterface>; however, Sylius\Component\Resourc...rface::addTranslation() does only seem to accept object<Sylius\Component\...l\TranslationInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
947
    }
948
949
    /**
950
     * @param int $price
951
     * @param ChannelInterface|null $channel
952
     *
953
     * @return ChannelPricingInterface
954
     */
955
    private function createChannelPricingForChannel($price, ChannelInterface $channel = null)
956
    {
957
        /** @var ChannelPricingInterface $channelPricing */
958
        $channelPricing = $this->channelPricingFactory->createNew();
959
        $channelPricing->setPrice($price);
960
        $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...
961
962
        return $channelPricing;
963
    }
964
}
965