Completed
Push — queries ( 779e6b...06f228 )
by Kamil
25:25
created

ProductContext::storeHasAConfigurableProduct()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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