Completed
Push — locale-in-url ( c8ed20...ec55a8 )
by Kamil
46:19 queued 21:47
created

ProductContext::thisProductHasAnImageWithACode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 8
nc 1
nop 3
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
        if (null === $channel && $this->sharedStorage->has('channel')) {
186
            $channel = $this->sharedStorage->get('channel');
187
        }
188
        $product = $this->createProduct($productName, $price, null, $channel);
189
        $product->setDescription('Awesome '.$productName);
190
191
        if (null !== $channel) {
192
            $product->addChannel($channel);
193
        }
194
195
        $this->saveProduct($product);
196
    }
197
198
    /**
199
     * @Given /^(this product) is also priced at ("[^"]+") in ("[^"]+" channel)$/
200
     */
201
    public function thisProductIsAlsoPricedAtInChannel(ProductInterface $product, $price, ChannelInterface $channel)
202
    {
203
        $product->addChannel($channel);
204
205
        /** @var ProductVariantInterface $productVariant */
206
        $productVariant = $this->defaultVariantResolver->getVariant($product);
207
208
        /** @var ChannelPricingInterface $channelPricing */
209
        $channelPricing = $this->channelPricingFactory->createNew();
210
        $channelPricing->setPrice($price);
211
        $channelPricing->setChannel($channel);
212
213
        $productVariant->addChannelPricing($channelPricing);
214
215
        $this->objectManager->flush();
216
    }
217
218
    /**
219
     * @Given the store( also) has a product :productName with code :code
220
     * @Given the store( also) has a product :productName with code :code, created at :date
221
     */
222
    public function storeHasProductWithCode($productName, $code, $date = null)
223
    {
224
        $product = $this->createProduct($productName, 0, $date);
225
226
        $product->setCode($code);
227
228
        if ($this->sharedStorage->has('channel')) {
229
            $product->addChannel($this->sharedStorage->get('channel'));
230
        }
231
232
        $this->saveProduct($product);
233
    }
234
235
    /**
236
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") available in (channel "[^"]+") and (channel "[^"]+")$/
237
     */
238
    public function storeHasAProductPricedAtAvailableInChannels($productName, $price = 100, ...$channels)
239
    {
240
        $product = $this->createProduct($productName, $price);
241
        /** @var ProductVariantInterface $productVariant */
242
        $productVariant = $this->defaultVariantResolver->getVariant($product);
243
244
        $product->setDescription('Awesome '.$productName);
245
246
        foreach ($channels as $channel) {
247
            $product->addChannel($channel);
248
            if (!$productVariant->hasChannelPricingForChannel($channel)) {
249
                $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
250
            }
251
        }
252
253
        $this->saveProduct($product);
254
    }
255
256
    /**
257
     * @Given /^(this product) is named "([^"]+)" (in the "([^"]+)" locale)$/
258
     * @Given /^the (product "[^"]+") is named "([^"]+)" (in the "([^"]+)" locale)$/
259
     */
260
    public function thisProductIsNamedIn(ProductInterface $product, $name, $locale)
261
    {
262
        $this->addProductTranslation($product, $name, $locale);
263
264
        $this->objectManager->flush();
265
    }
266
267
    /**
268
     * @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
269
     */
270
    public function theStoreHasProductNamedInAndIn($firstName, $firstLocale, $secondName, $secondLocale)
271
    {
272
        $product = $this->createProduct($firstName);
273
274
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
275
        foreach ($names as $name => $locale) {
276
            $this->addProductTranslation($product, $name, $locale);
277
        }
278
279
        $this->saveProduct($product);
280
    }
281
282
    /**
283
     * @Given /^the store has(?:| a| an) "([^"]+)" configurable product$/
284
     * @Given /^the store has(?:| a| an) "([^"]+)" configurable product with "([^"]+)" slug$/
285
     */
286
    public function storeHasAConfigurableProduct($productName, $slug = null)
287
    {
288
        /** @var ProductInterface $product */
289
        $product = $this->productFactory->createNew();
290
291
        $product->setName($productName);
292
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
293
        $product->setSlug($slug?:$this->slugGenerator->generate($productName));
294
295
        $product->setDescription('Awesome '.$productName);
296
297
        if ($this->sharedStorage->has('channel')) {
298
            $channel = $this->sharedStorage->get('channel');
299
            $product->addChannel($channel);
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);
324
            $product->addChannel($channel);
325
326
            $this->saveProduct($product);
327
        }
328
    }
329
330
    /**
331
     * @Given /^the (product "[^"]+") has(?:| a) "([^"]+)" variant priced at ("[^"]+")$/
332
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+")$/
333
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") in ("([^"]+)" channel)$/
334
     */
335
    public function theProductHasVariantPricedAt(
336
        ProductInterface $product,
337
        $productVariantName,
338
        $price,
339
        ChannelInterface $channel = null
340
    ) {
341
        $this->createProductVariant(
342
            $product,
343
            $productVariantName,
344
            $price,
345
            StringInflector::nameToUppercaseCode($productVariantName),
346
            (null !== $channel) ? $channel : $this->sharedStorage->get('channel')
347
        );
348
    }
349
350
    /**
351
     * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant$/
352
     * @Given /^the (product "[^"]+") has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/
353
     * @Given /^(this product) has(?:| also)(?:| a| an) "([^"]+)" variant at position ([^"]+)$/
354
     */
355
    public function theProductHasVariantAtPosition(
356
        ProductInterface $product,
357
        $productVariantName,
358
        $position = null
359
    ) {
360
        $this->createProductVariant(
361
            $product,
362
            $productVariantName,
363
            0,
364
            StringInflector::nameToUppercaseCode($productVariantName),
365
            $this->sharedStorage->get('channel'),
366
            $position
367
        );
368
    }
369
370
    /**
371
     * @Given /^(this variant) is also priced at ("[^"]+") in ("([^"]+)" channel)$/
372
     */
373
    public function thisVariantIsAlsoPricedAtInChannel(ProductVariantInterface $productVariant, $price, ChannelInterface $channel)
374
    {
375
        $productVariant->addChannelPricing($this->createChannelPricingForChannel(
376
            $this->getPriceFromString(str_replace(['$', '€', '£'], '', $price)),
377
            $channel
378
        ));
379
380
        $this->objectManager->flush();
381
    }
382
383
    /**
384
     * @Given /^(it|this product) has(?:| also) variant named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
385
     */
386
    public function itHasVariantNamedInAndIn(ProductInterface $product, $firstName, $firstLocale, $secondName, $secondLocale)
387
    {
388
        $productVariant = $this->createProductVariant(
389
            $product,
390
            $firstName,
391
            100,
392
            StringInflector::nameToUppercaseCode($firstName),
393
            $this->sharedStorage->get('channel')
394
        );
395
396
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
397
        foreach ($names as $name => $locale) {
398
            $this->addProductVariantTranslation($productVariant, $name, $locale);
399
        }
400
401
        $this->objectManager->flush();
402
    }
403
404
    /**
405
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") identified by "([^"]+)"$/
406
     */
407
    public function theProductHasVariantPricedAtIdentifiedBy(
408
        ProductInterface $product,
409
        $productVariantName,
410
        $price,
411
        $code
412
    ) {
413
        $this->createProductVariant($product, $productVariantName, $price, $code, $this->sharedStorage->get('channel'));
414
    }
415
416
    /**
417
     * @Given /^there is product "([^"]+)" available in ((?:this|that|"[^"]+") channel)$/
418
     * @Given /^the store has a product "([^"]+)" available in ("([^"]+)" channel)$/
419
     */
420
    public function thereIsProductAvailableInGivenChannel($productName, ChannelInterface $channel)
421
    {
422
        $product = $this->createProduct($productName, 0, null, $channel);
423
424
        $product->setDescription('Awesome ' . $productName);
425
        $product->addChannel($channel);
426
427
        $this->saveProduct($product);
428
    }
429
430
    /**
431
     * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/
432
     */
433
    public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory)
434
    {
435
        /** @var ProductVariantInterface $variant */
436
        $variant = $this->defaultVariantResolver->getVariant($product);
437
        $variant->setTaxCategory($taxCategory);
438
439
        $this->objectManager->flush();
440
    }
441
442
    /**
443
     * @Given /^(it) comes in the following variations:$/
444
     */
445
    public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
446
    {
447
        foreach ($table->getHash() as $variantHash) {
448
            /** @var ProductVariantInterface $variant */
449
            $variant = $this->productVariantFactory->createNew();
450
451
            $variant->setName($variantHash['name']);
452
            $variant->setCode(StringInflector::nameToUppercaseCode($variantHash['name']));
453
            $variant->addChannelPricing($this->createChannelPricingForChannel(
454
                $this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])),
455
                $this->sharedStorage->get('channel')
456
            ));
457
            $variant->setProduct($product);
458
            $product->addVariant($variant);
459
        }
460
461
        $this->objectManager->flush();
462
    }
463
464
    /**
465
     * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/
466
     */
467
    public function productVariantBelongsToTaxCategory(
468
        ProductVariantInterface $productVariant,
469
        TaxCategoryInterface $taxCategory
470
    ) {
471
        $productVariant->setTaxCategory($taxCategory);
472
        $this->objectManager->flush($productVariant);
473
    }
474
475
    /**
476
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)" and "([^"]+)"$/
477
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)", "([^"]+)" and "([^"]+)"$/
478
     */
479
    public function thisProductHasOptionWithValues(ProductInterface $product, $optionName, ...$values)
480
    {
481
        /** @var ProductOptionInterface $option */
482
        $option = $this->productOptionFactory->createNew();
483
484
        $option->setName($optionName);
485
        $option->setCode(StringInflector::nameToUppercaseCode($optionName));
486
487
        $this->sharedStorage->set(sprintf('%s_option', $optionName), $option);
488
489
        foreach ($values as $key => $value) {
490
            $optionValue = $this->addProductOption($option, $value, StringInflector::nameToUppercaseCode($value));
491
            $this->sharedStorage->set(sprintf('%s_option_%s_value', $value, strtolower($optionName)), $optionValue);
492
        }
493
494
        $product->addOption($option);
495
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
496
497
        $this->objectManager->persist($option);
498
        $this->objectManager->flush();
499
    }
500
501
    /**
502
     * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/
503
     */
504
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
505
    {
506
        /** @var ProductVariantInterface $productVariant */
507
        $productVariant = $this->defaultVariantResolver->getVariant($product);
508
        $productVariant->setOnHand($quantity);
509
510
        $this->objectManager->flush();
511
    }
512
513
    /**
514
     * @Given /^the (product "([^"]+)") is out of stock$/
515
     */
516
    public function theProductIsOutOfStock(ProductInterface $product)
517
    {
518
        /** @var ProductVariantInterface $productVariant */
519
        $productVariant = $this->defaultVariantResolver->getVariant($product);
520
        $productVariant->setTracked(true);
521
        $productVariant->setOnHand(0);
522
523
        $this->objectManager->flush();
524
    }
525
526
    /**
527
     * @When other customer has bought :quantity :product products by this time
528
     */
529
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
530
    {
531
        /** @var ProductVariantInterface $productVariant */
532
        $productVariant = $this->defaultVariantResolver->getVariant($product);
533
        $productVariant->setOnHand($productVariant->getOnHand() - $quantity);
534
535
        $this->objectManager->flush();
536
    }
537
538
    /**
539
     * @Given /^(this product) is tracked by the inventory$/
540
     * @Given /^(?:|the )("[^"]+" product) is(?:| also) tracked by the inventory$/
541
     */
542
    public function thisProductIsTrackedByTheInventory(ProductInterface $product)
543
    {
544
        /** @var ProductVariantInterface $productVariant */
545
        $productVariant = $this->defaultVariantResolver->getVariant($product);
546
        $productVariant->setTracked(true);
547
548
        $this->objectManager->flush();
549
    }
550
551
    /**
552
     * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/
553
     */
554
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, $price)
555
    {
556
        /** @var ProductVariantInterface $variant */
557
        $variant = $this->productVariantFactory->createNew();
558
559
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_%s_value', $optionValueName, $optionName));
560
561
        $variant->addOptionValue($optionValue);
562
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $this->sharedStorage->get('channel')));
563
        $variant->setCode(sprintf('%s_%s', $product->getCode(), $optionValueName));
564
        $variant->setName($product->getName());
565
566
        $product->addVariant($variant);
567
        $this->objectManager->flush();
568
    }
569
570
    /**
571
     * @Given the :product product's :optionValueName size belongs to :shippingCategory shipping category
572
     */
573
    public function thisProductSizeBelongsToShippingCategory(ProductInterface $product, $optionValueName, ShippingCategoryInterface $shippingCategory)
574
    {
575
        $code = sprintf('%s_%s', $product->getCode(), $optionValueName);
576
        /** @var ProductVariantInterface $productVariant */
577
        $productVariant = $product->getVariants()->filter(function ($variant) use ($code) {
578
            return $code === $variant->getCode();
579
        })->first();
580
581
        Assert::notNull($productVariant, sprintf('Product variant with given code %s not exists!', $code));
582
583
        $productVariant->setShippingCategory($shippingCategory);
584
        $this->objectManager->flush();
585
    }
586
587
    /**
588
     * @Given /^(this product) has (this product option)$/
589
     * @Given /^(this product) has (?:a|an) ("[^"]+" option)$/
590
     */
591
    public function thisProductHasThisProductOption(ProductInterface $product, ProductOptionInterface $option)
592
    {
593
        $product->addOption($option);
594
595
        $this->objectManager->flush();
596
    }
597
598
    /**
599
     * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/
600
     */
601
    public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant)
602
    {
603
        $productVariant->setTracked(true);
604
        $productVariant->setOnHand($quantity);
605
606
        $this->objectManager->flush();
607
    }
608
609
    /**
610
     * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/
611
     */
612
    public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant)
613
    {
614
        $productVariant->setTracked(true);
615
616
        $this->objectManager->flush();
617
    }
618
619
    /**
620
     * @Given /^(this product)'s price is ("[^"]+")$/
621
     * @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/
622
     */
623
    public function theProductChangedItsPriceTo(ProductInterface $product, $price)
624
    {
625
        /** @var ProductVariantInterface $productVariant */
626
        $productVariant = $this->defaultVariantResolver->getVariant($product);
627
        $channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
628
        $channelPricing->setPrice($price);
629
630
        $this->objectManager->flush();
631
    }
632
633
    /**
634
     * @Given /^(this product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
635
     * @Given /^the ("[^"]+" product)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
636
     * @Given /^(it)(?:| also) has an image "([^"]+)" with "([^"]+)" type$/
637
     */
638
    public function thisProductHasAnImageWithType(ProductInterface $product, $imagePath, $imageType)
639
    {
640
        $filesPath = $this->getParameter('files_path');
641
642
        /** @var ImageInterface $productImage */
643
        $productImage = $this->productImageFactory->createNew();
644
        $productImage->setFile(new UploadedFile($filesPath.$imagePath, basename($imagePath)));
645
        $productImage->setType($imageType);
646
        $this->imageUploader->upload($productImage);
647
648
        $product->addImage($productImage);
649
650
        $this->objectManager->flush($product);
651
    }
652
653
    /**
654
     * @Given /^(this product) belongs to ("([^"]+)" shipping category)$/
655
     */
656
    public function thisProductBelongsToShippingCategory(ProductInterface $product, ShippingCategoryInterface $shippingCategory)
657
    {
658
        $product->getVariants()->first()->setShippingCategory($shippingCategory);
659
        $this->objectManager->flush();
660
    }
661
662
    /**
663
     * @param string $price
664
     *
665
     * @return int
666
     */
667
    private function getPriceFromString($price)
668
    {
669
        return (int) round($price * 100, 2);
670
    }
671
672
    /**
673
     * @param string $productName
674
     * @param int $price
675
     * @param string|null $date
676
     * @param ChannelInterface|null $channel
677
     *
678
     * @return ProductInterface
679
     */
680
    private function createProduct($productName, $price = 100, $date = null, ChannelInterface $channel = null)
681
    {
682
        /** @var ProductInterface $product */
683
        $product = $this->productFactory->createWithVariant();
684
685
        $product->setName($productName);
686
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
687
        $product->setSlug($this->slugGenerator->generate($productName));
688
        $product->setCreatedAt(new \DateTime($date));
689
690
        /** @var ProductVariantInterface $productVariant */
691
        $productVariant = $this->defaultVariantResolver->getVariant($product);
692
693
        if (null === $channel && $this->sharedStorage->has('channel')) {
694
            $channel = $this->sharedStorage->get('channel');
695
        }
696
697
        if (null !== $channel) {
698
            $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
699
        }
700
        $productVariant->setCode($product->getCode());
701
        $productVariant->setName($product->getName());
702
703
        return $product;
704
    }
705
706
    /**
707
     * @param ProductOptionInterface $option
708
     * @param string $value
709
     * @param string $code
710
     *
711
     * @return ProductOptionValueInterface
712
     */
713
    private function addProductOption(ProductOptionInterface $option, $value, $code)
714
    {
715
        /** @var ProductOptionValueInterface $optionValue */
716
        $optionValue = $this->productOptionValueFactory->createNew();
717
718
        $optionValue->setValue($value);
719
        $optionValue->setCode($code);
720
        $optionValue->setOption($option);
721
722
        $option->addValue($optionValue);
723
724
        return $optionValue;
725
    }
726
727
    /**
728
     * @param ProductInterface $product
729
     */
730
    private function saveProduct(ProductInterface $product)
731
    {
732
        $this->productRepository->add($product);
733
        $this->sharedStorage->set('product', $product);
734
    }
735
736
    /**
737
     * @param string $name
738
     *
739
     * @return NodeElement
740
     */
741
    private function getParameter($name)
742
    {
743
        return isset($this->minkParameters[$name]) ? $this->minkParameters[$name] : null;
744
    }
745
746
    /**
747
     * @param ProductInterface $product
748
     * @param $productVariantName
749
     * @param int $price
750
     * @param string $code
751
     * @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...
752
     * @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...
753
     *
754
     * @return ProductVariantInterface
755
     */
756
    private function createProductVariant(
757
        ProductInterface $product,
758
        $productVariantName,
759
        $price,
760
        $code,
761
        ChannelInterface $channel = null,
762
        $position = null
763
    ) {
764
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_CHOICE);
765
766
        /** @var ProductVariantInterface $variant */
767
        $variant = $this->productVariantFactory->createNew();
768
769
        $variant->setName($productVariantName);
770
        $variant->setCode($code);
771
        $variant->setProduct($product);
772
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
773
        $variant->setPosition($position);
774
775
        $product->addVariant($variant);
776
777
        $this->objectManager->flush();
778
        $this->sharedStorage->set('variant', $variant);
779
780
        return $variant;
781
    }
782
783
    /**
784
     * @param ProductInterface $product
785
     * @param string $name
786
     * @param string $locale
787
     */
788
    private function addProductTranslation(ProductInterface $product, $name, $locale)
789
    {
790
        /** @var ProductTranslationInterface|TranslationInterface $translation */
791
        $translation = $this->productTranslationFactory->createNew();
792
        $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\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...
793
        $translation->setName($name);
0 ignored issues
show
Bug introduced by
The method setName does only exist in Sylius\Component\Core\Mo...uctTranslationInterface, 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...
794
        $translation->setSlug($this->slugGenerator->generate($name));
0 ignored issues
show
Bug introduced by
The method setSlug does only exist in Sylius\Component\Core\Mo...uctTranslationInterface, 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...
795
796
        $product->addTranslation($translation);
0 ignored issues
show
Bug introduced by
It seems like $translation defined by $this->productTranslationFactory->createNew() on line 791 can also be of type object<Sylius\Component\...ctTranslationInterface>; 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...
797
    }
798
799
    /**
800
     * @param ProductVariantInterface $productVariant
801
     * @param string $name
802
     * @param string $locale
803
     */
804
    private function addProductVariantTranslation(ProductVariantInterface $productVariant, $name, $locale)
805
    {
806
        /** @var ProductVariantTranslationInterface|TranslationInterface $translation */
807
        $translation = $this->productVariantTranslationFactory->createNew();
808
        $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...
809
        $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...
810
811
        $productVariant->addTranslation($translation);
0 ignored issues
show
Bug introduced by
It seems like $translation defined by $this->productVariantTra...ionFactory->createNew() on line 807 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...
812
    }
813
814
    /**
815
     * @param int $price
816
     * @param ChannelInterface|null $channel
817
     *
818
     * @return ChannelPricingInterface
819
     */
820
    private function createChannelPricingForChannel($price, ChannelInterface $channel = null)
821
    {
822
        /** @var ChannelPricingInterface $channelPricing */
823
        $channelPricing = $this->channelPricingFactory->createNew();
824
        $channelPricing->setPrice($price);
825
        $channelPricing->setChannel($channel);
826
827
        return $channelPricing;
828
    }
829
}
830