Completed
Push — unused-definitions ( d9908f )
by Kamil
18:33
created

itHasDifferentPricesForDifferentChannelsAndCurrencies()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
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\Component\Attribute\Factory\AttributeFactoryInterface;
19
use Sylius\Component\Core\Formatter\StringInflector;
20
use Sylius\Component\Core\Model\ChannelInterface;
21
use Sylius\Component\Core\Model\ChannelPricingInterface;
22
use Sylius\Component\Core\Model\ImageInterface;
23
use Sylius\Component\Core\Model\ProductInterface;
24
use Sylius\Component\Core\Model\ProductTranslationInterface;
25
use Sylius\Component\Core\Model\ProductVariantInterface;
26
use Sylius\Component\Core\Pricing\Calculators;
27
use Sylius\Component\Core\Repository\ProductRepositoryInterface;
28
use Sylius\Behat\Service\SharedStorageInterface;
29
use Sylius\Component\Core\Uploader\ImageUploaderInterface;
30
use Sylius\Component\Product\Factory\ProductFactoryInterface;
31
use Sylius\Component\Product\Generator\SlugGeneratorInterface;
32
use Sylius\Component\Product\Model\ProductAttributeInterface;
33
use Sylius\Component\Product\Model\ProductAttributeValueInterface;
34
use Sylius\Component\Product\Model\ProductOptionInterface;
35
use Sylius\Component\Product\Model\ProductOptionValueInterface;
36
use Sylius\Component\Product\Model\ProductVariantTranslationInterface;
37
use Sylius\Component\Resource\Factory\FactoryInterface;
38
use Sylius\Component\Shipping\Model\ShippingCategoryInterface;
39
use Sylius\Component\Resource\Model\TranslationInterface;
40
use Sylius\Component\Taxation\Model\TaxCategoryInterface;
41
use Sylius\Component\Product\Resolver\ProductVariantResolverInterface;
42
use Symfony\Component\HttpFoundation\File\UploadedFile;
43
use Webmozart\Assert\Assert;
44
45
/**
46
 * @author Arkadiusz Krakowiak <[email protected]>
47
 * @author Mateusz Zalewski <[email protected]>
48
 * @author Magdalena Banasiak <[email protected]>
49
 */
50
final class ProductContext implements Context
51
{
52
    /**
53
     * @var SharedStorageInterface
54
     */
55
    private $sharedStorage;
56
57
    /**
58
     * @var ProductRepositoryInterface
59
     */
60
    private $productRepository;
61
62
    /**
63
     * @var ProductFactoryInterface
64
     */
65
    private $productFactory;
66
67
    /**
68
     * @var FactoryInterface
69
     */
70
    private $productTranslationFactory;
71
72
    /**
73
     * @var AttributeFactoryInterface
74
     */
75
    private $productAttributeFactory;
76
77
    /**
78
     * @var FactoryInterface
79
     */
80
    private $productVariantFactory;
81
82
    /**
83
     * @var FactoryInterface
84
     */
85
    private $productVariantTranslationFactory;
86
87
    /**
88
     * @var FactoryInterface
89
     */
90
    private $channelPricingFactory;
91
92
    /**
93
     * @var FactoryInterface
94
     */
95
    private $attributeValueFactory;
96
97
    /**
98
     * @var FactoryInterface
99
     */
100
    private $productOptionFactory;
101
102
    /**
103
     * @var FactoryInterface
104
     */
105
    private $productOptionValueFactory;
106
107
    /**
108
     * @var FactoryInterface
109
     */
110
    private $productImageFactory;
111
112
    /**
113
     * @var ObjectManager
114
     */
115
    private $objectManager;
116
117
    /**
118
     * @var ProductVariantResolverInterface
119
     */
120
    private $defaultVariantResolver;
121
122
    /**
123
     * @var ImageUploaderInterface
124
     */
125
    private $imageUploader;
126
127
    /**
128
     * @var SlugGeneratorInterface
129
     */
130
    private $slugGenerator;
131
132
    /**
133
     * @var array
134
     */
135
    private $minkParameters;
136
137
    /**
138
     * @param SharedStorageInterface $sharedStorage
139
     * @param ProductRepositoryInterface $productRepository
140
     * @param ProductFactoryInterface $productFactory
141
     * @param FactoryInterface $productTranslationFactory
142
     * @param AttributeFactoryInterface $productAttributeFactory
143
     * @param FactoryInterface $attributeValueFactory
144
     * @param FactoryInterface $productVariantFactory
145
     * @param FactoryInterface $productVariantTranslationFactory
146
     * @param FactoryInterface $channelPricingFactory
147
     * @param FactoryInterface $productOptionFactory
148
     * @param FactoryInterface $productOptionValueFactory
149
     * @param FactoryInterface $productImageFactory
150
     * @param ObjectManager $objectManager
151
     * @param ProductVariantResolverInterface $defaultVariantResolver
152
     * @param ImageUploaderInterface $imageUploader
153
     * @param SlugGeneratorInterface $slugGenerator
154
     * @param array $minkParameters
155
     */
156
    public function __construct(
157
        SharedStorageInterface $sharedStorage,
158
        ProductRepositoryInterface $productRepository,
159
        ProductFactoryInterface $productFactory,
160
        FactoryInterface $productTranslationFactory,
161
        AttributeFactoryInterface $productAttributeFactory,
162
        FactoryInterface $attributeValueFactory,
163
        FactoryInterface $productVariantFactory,
164
        FactoryInterface $productVariantTranslationFactory,
165
        FactoryInterface $channelPricingFactory,
166
        FactoryInterface $productOptionFactory,
167
        FactoryInterface $productOptionValueFactory,
168
        FactoryInterface $productImageFactory,
169
        ObjectManager $objectManager,
170
        ProductVariantResolverInterface $defaultVariantResolver,
171
        ImageUploaderInterface $imageUploader,
172
        SlugGeneratorInterface $slugGenerator,
173
        array $minkParameters
174
    ) {
175
        $this->sharedStorage = $sharedStorage;
176
        $this->productRepository = $productRepository;
177
        $this->productFactory = $productFactory;
178
        $this->productTranslationFactory = $productTranslationFactory;
179
        $this->productAttributeFactory = $productAttributeFactory;
180
        $this->attributeValueFactory = $attributeValueFactory;
181
        $this->productVariantFactory = $productVariantFactory;
182
        $this->productVariantTranslationFactory = $productVariantTranslationFactory;
183
        $this->channelPricingFactory = $channelPricingFactory;
184
        $this->productOptionFactory = $productOptionFactory;
185
        $this->productOptionValueFactory = $productOptionValueFactory;
186
        $this->productImageFactory = $productImageFactory;
187
        $this->objectManager = $objectManager;
188
        $this->defaultVariantResolver = $defaultVariantResolver;
189
        $this->imageUploader = $imageUploader;
190
        $this->slugGenerator = $slugGenerator;
191
        $this->minkParameters = $minkParameters;
192
    }
193
194
    /**
195
     * @Given the store has a product :productName
196
     * @Given the store has a :productName product
197
     * @Given I added a product :productName
198
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+")$/
199
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") in ("[^"]+" channel)$/
200
     */
201
    public function storeHasAProductPricedAt($productName, $price = 100, ChannelInterface $channel = null)
202
    {
203
        if (null === $channel && $this->sharedStorage->has('channel')) {
204
            $channel = $this->sharedStorage->get('channel');
205
        }
206
        $product = $this->createProduct($productName, $price, null, $channel);
207
        $product->setDescription('Awesome '.$productName);
208
209
        if (null !== $channel) {
210
            $product->addChannel($channel);
211
        }
212
213
        $this->saveProduct($product);
214
    }
215
216
    /**
217
     * @Given /^(this product) is also priced at ("[^"]+") in ("[^"]+" channel)$/
218
     */
219
    public function thisProductIsAlsoPricedAtInChannel(ProductInterface $product, $price, ChannelInterface $channel)
220
    {
221
        $product->addChannel($channel);
222
223
        /** @var ProductVariantInterface $productVariant */
224
        $productVariant = $this->defaultVariantResolver->getVariant($product);
225
226
        /** @var ChannelPricingInterface $channelPricing */
227
        $channelPricing = $this->channelPricingFactory->createNew();
228
        $channelPricing->setPrice($price);
229
        $channelPricing->setChannel($channel);
230
231
        $productVariant->addChannelPricing($channelPricing);
232
233
        $this->objectManager->flush();
234
    }
235
236
    /**
237
     * @Given the store( also) has a product :productName with code :code
238
     * @Given the store( also) has a product :productName with code :code, created at :date
239
     */
240
    public function storeHasProductWithCode($productName, $code, $date = null)
241
    {
242
        $product = $this->createProduct($productName, 0, $date);
243
244
        $product->setCode($code);
245
246
        if ($this->sharedStorage->has('channel')) {
247
            $product->addChannel($this->sharedStorage->get('channel'));
248
        }
249
250
        $this->saveProduct($product);
251
    }
252
253
    /**
254
     * @Given /^the store(?:| also) has a product "([^"]+)" priced at ("[^"]+") available in (channel "[^"]+") and (channel "[^"]+")$/
255
     */
256
    public function storeHasAProductPricedAtAvailableInChannels($productName, $price = 100, ...$channels)
257
    {
258
        $product = $this->createProduct($productName, $price);
259
        /** @var ProductVariantInterface $productVariant */
260
        $productVariant = $this->defaultVariantResolver->getVariant($product);
261
262
        $product->setDescription('Awesome '.$productName);
263
264
        foreach ($channels as $channel) {
265
            $product->addChannel($channel);
266
            if (!$productVariant->hasChannelPricingForChannel($channel)) {
267
                $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
268
            }
269
        }
270
271
        $this->saveProduct($product);
272
    }
273
274
    /**
275
     * @Given /^(this product) is named "([^"]+)" (in the "([^"]+)" locale)$/
276
     * @Given /^the (product "[^"]+") is named "([^"]+)" (in the "([^"]+)" locale)$/
277
     */
278
    public function thisProductIsNamedIn(ProductInterface $product, $name, $locale)
279
    {
280
        $this->addProductTranslation($product, $name, $locale);
281
282
        $this->objectManager->flush();
283
    }
284
285
    /**
286
     * @Given /^the store has a product named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
287
     */
288
    public function theStoreHasProductNamedInAndIn($firstName, $firstLocale, $secondName, $secondLocale)
289
    {
290
        $product = $this->createProduct($firstName);
291
292
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
293
        foreach ($names as $name => $locale) {
294
            $this->addProductTranslation($product, $name, $locale);
295
        }
296
297
        $this->saveProduct($product);
298
    }
299
300
    /**
301
     * @Given the store has a :productName configurable product
302
     */
303
    public function storeHasAConfigurableProduct($productName)
304
    {
305
        /** @var ProductInterface $product */
306
        $product = $this->productFactory->createNew();
307
308
        $product->setName($productName);
309
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
310
        $product->setSlug($this->slugGenerator->generate($productName));
311
312
        $product->setDescription('Awesome '.$productName);
313
314
        if ($this->sharedStorage->has('channel')) {
315
            $channel = $this->sharedStorage->get('channel');
316
            $product->addChannel($channel);
317
        }
318
319
        $this->saveProduct($product);
320
    }
321
322
    /**
323
     * @Given the store has( also) :firstProductName and :secondProductName products
324
     * @Given the store has( also) :firstProductName, :secondProductName and :thirdProductName products
325
     * @Given the store has( also) :firstProductName, :secondProductName, :thirdProductName and :fourthProductName products
326
     */
327
    public function theStoreHasProducts(...$productsNames)
328
    {
329
        foreach ($productsNames as $productName) {
330
            $this->saveProduct($this->createProduct($productName));
331
        }
332
    }
333
334
    /**
335
     * @Given /^(this channel) has "([^"]+)", "([^"]+)", "([^"]+)" and "([^"]+)" products$/
336
     */
337
    public function thisChannelHasProducts(ChannelInterface $channel, ...$productsNames)
338
    {
339
        foreach ($productsNames as $productName) {
340
            $product = $this->createProduct($productName);
341
            $product->addChannel($channel);
342
343
            $this->saveProduct($product);
344
        }
345
    }
346
347
    /**
348
     * @Given /^the (product "[^"]+") has(?:| a) "([^"]+)" variant priced at ("[^"]+")$/
349
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+")$/
350
     */
351
    public function theProductHasVariantPricedAt(
352
        ProductInterface $product,
353
        $productVariantName,
354
        $price,
355
        ChannelInterface $channel = null
356
    ) {
357
        $this->createProductVariant(
358
            $product,
359
            $productVariantName,
360
            $price,
361
            StringInflector::nameToUppercaseCode($productVariantName),
362
            (null !== $channel) ? $channel : $this->sharedStorage->get('channel')
363
        );
364
    }
365
366
    /**
367
     * @Given /^(it|this product) has(?:| also) variant named "([^"]+)" in ("[^"]+" locale) and "([^"]+)" in ("[^"]+" locale)$/
368
     */
369
    public function itHasVariantNamedInAndIn(ProductInterface $product, $firstName, $firstLocale, $secondName, $secondLocale)
370
    {
371
        $productVariant = $this->createProductVariant(
372
            $product,
373
            $firstName,
374
            100,
375
            StringInflector::nameToUppercaseCode($firstName),
376
            $this->sharedStorage->get('channel')
377
        );
378
379
        $names = [$firstName => $firstLocale, $secondName => $secondLocale];
380
        foreach ($names as $name => $locale) {
381
            $this->addProductVariantTranslation($productVariant, $name, $locale);
382
        }
383
384
        $this->objectManager->flush();
385
    }
386
387
    /**
388
     * @Given /^(this product) has "([^"]+)" variant priced at ("[^"]+") identified by "([^"]+)"$/
389
     */
390
    public function theProductHasVariantPricedAtIdentifiedBy(
391
        ProductInterface $product,
392
        $productVariantName,
393
        $price,
394
        $code
395
    ) {
396
        $this->createProductVariant($product, $productVariantName, $price, $code, $this->sharedStorage->get('channel'));
397
    }
398
399
    /**
400
     * @Given /^there is product "([^"]+)" available in ((?:this|that|"[^"]+") channel)$/
401
     * @Given /^the store has a product "([^"]+)" available in ("([^"]+)" channel)$/
402
     */
403
    public function thereIsProductAvailableInGivenChannel($productName, ChannelInterface $channel)
404
    {
405
        $product = $this->createProduct($productName, 0, null, $channel);
406
407
        $product->setDescription('Awesome ' . $productName);
408
        $product->addChannel($channel);
409
410
        $this->saveProduct($product);
411
    }
412
413
    /**
414
     * @Given /^([^"]+) belongs to ("[^"]+" tax category)$/
415
     */
416
    public function productBelongsToTaxCategory(ProductInterface $product, TaxCategoryInterface $taxCategory)
417
    {
418
        /** @var ProductVariantInterface $variant */
419
        $variant = $this->defaultVariantResolver->getVariant($product);
420
        $variant->setTaxCategory($taxCategory);
421
422
        $this->objectManager->flush();
423
    }
424
425
    /**
426
     * @Given /^(it) comes in the following variations:$/
427
     */
428
    public function itComesInTheFollowingVariations(ProductInterface $product, TableNode $table)
429
    {
430
        foreach ($table->getHash() as $variantHash) {
431
            /** @var ProductVariantInterface $variant */
432
            $variant = $this->productVariantFactory->createNew();
433
434
            $variant->setName($variantHash['name']);
435
            $variant->setCode(StringInflector::nameToUppercaseCode($variantHash['name']));
436
            $variant->addChannelPricing($this->createChannelPricingForChannel(
437
                $this->getPriceFromString(str_replace(['$', '€', '£'], '', $variantHash['price'])),
438
                $this->sharedStorage->get('channel')
439
            ));
440
            $variant->setProduct($product);
441
            $product->addVariant($variant);
442
        }
443
444
        $this->objectManager->flush();
445
    }
446
447
    /**
448
     * @Given /^("[^"]+" variant of product "[^"]+") belongs to ("[^"]+" tax category)$/
449
     */
450
    public function productVariantBelongsToTaxCategory(
451
        ProductVariantInterface $productVariant,
452
        TaxCategoryInterface $taxCategory
453
    ) {
454
        $productVariant->setTaxCategory($taxCategory);
455
        $this->objectManager->flush($productVariant);
456
    }
457
458
    /**
459
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" with value "([^"]+)"$/
460
     */
461
    public function thisProductHasAttributeWithValue(ProductInterface $product, $productAttributeType, $productAttributeName, $value)
462
    {
463
        $attribute = $this->createProductAttribute($productAttributeType, $productAttributeName);
464
        $attributeValue = $this->createProductAttributeValue($value, $attribute);
465
        $product->addAttribute($attributeValue);
466
467
        $this->objectManager->flush();
468
    }
469
470
    /**
471
     * @Given /^(this product) has percent attribute "([^"]+)" with value ([^"]+)%$/
472
     */
473
    public function thisProductHasPercentAttributeWithValue(ProductInterface $product, $productAttributeName, $value)
474
    {
475
        $attribute = $this->createProductAttribute('percent', $productAttributeName);
476
        $attributeValue = $this->createProductAttributeValue($value/100, $attribute);
477
        $product->addAttribute($attributeValue);
478
479
        $this->objectManager->flush();
480
    }
481
482
    /**
483
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" set to "([^"]+)"$/
484
     */
485
    public function thisProductHasCheckboxAttributeWithValue(ProductInterface $product, $productAttributeType, $productAttributeName, $value)
486
    {
487
        $attribute = $this->createProductAttribute($productAttributeType, $productAttributeName);
488
        $booleanValue = ('Yes' === $value);
489
        $attributeValue = $this->createProductAttributeValue($booleanValue, $attribute);
0 ignored issues
show
Documentation introduced by
$booleanValue is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
490
        $product->addAttribute($attributeValue);
491
492
        $this->objectManager->flush();
493
    }
494
495
    /**
496
     * @Given /^(this product) has percent attribute "([^"]+)" at position (\d+)$/
497
     */
498
    public function thisProductHasPercentAttributeWithValueAtPosition(ProductInterface $product, $productAttributeName, $position)
499
    {
500
        $attribute = $this->createProductAttribute('percent', $productAttributeName);
501
        $attribute->setPosition($position);
502
        $attributeValue = $this->createProductAttributeValue(rand(1, 100)/100, $attribute);
503
504
        $product->addAttribute($attributeValue);
505
506
        $this->objectManager->flush();
507
    }
508
509
    /**
510
     * @Given /^(this product) has ([^"]+) attribute "([^"]+)" with date "([^"]+)"$/
511
     */
512
    public function thisProductHasDateTimeAttributeWithDate(ProductInterface $product, $productAttributeType, $productAttributeName, $date)
513
    {
514
        $attribute = $this->createProductAttribute($productAttributeType, $productAttributeName);
515
        $attributeValue = $this->createProductAttributeValue(new \DateTime($date), $attribute);
0 ignored issues
show
Documentation introduced by
new \DateTime($date) is of type object<DateTime>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
516
517
        $product->addAttribute($attributeValue);
518
519
        $this->objectManager->flush();
520
    }
521
522
    /**
523
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)" and "([^"]+)"$/
524
     * @Given /^(this product) has option "([^"]+)" with values "([^"]+)", "([^"]+)" and "([^"]+)"$/
525
     */
526
    public function thisProductHasOptionWithValues(ProductInterface $product, $optionName, ...$values)
527
    {
528
        /** @var ProductOptionInterface $option */
529
        $option = $this->productOptionFactory->createNew();
530
531
        $option->setName($optionName);
532
        $option->setCode(StringInflector::nameToUppercaseCode($optionName));
533
534
        $this->sharedStorage->set(sprintf('%s_option', $optionName), $option);
535
536
        foreach ($values as $key => $value) {
537
            $optionValue = $this->addProductOption($option, $value, StringInflector::nameToUppercaseCode($value));
538
            $this->sharedStorage->set(sprintf('%s_option_%s_value', $value, strtolower($optionName)), $optionValue);
539
        }
540
541
        $product->addOption($option);
542
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_MATCH);
543
544
        $this->objectManager->persist($option);
545
        $this->objectManager->flush();
546
    }
547
548
    /**
549
     * @Given /^there (?:is|are) (\d+) unit(?:|s) of (product "([^"]+)") available in the inventory$/
550
     */
551
    public function thereIsQuantityOfProducts($quantity, ProductInterface $product)
552
    {
553
        /** @var ProductVariantInterface $productVariant */
554
        $productVariant = $this->defaultVariantResolver->getVariant($product);
555
        $productVariant->setOnHand($quantity);
556
557
        $this->objectManager->flush();
558
    }
559
560
    /**
561
     * @Given /^the (product "([^"]+)") is out of stock$/
562
     */
563
    public function theProductIsOutOfStock(ProductInterface $product)
564
    {
565
        /** @var ProductVariantInterface $productVariant */
566
        $productVariant = $this->defaultVariantResolver->getVariant($product);
567
        $productVariant->setTracked(true);
568
        $productVariant->setOnHand(0);
569
570
        $this->objectManager->flush();
571
    }
572
573
    /**
574
     * @When other customer has bought :quantity :product products by this time
575
     */
576
    public function otherCustomerHasBoughtProductsByThisTime($quantity, ProductInterface $product)
577
    {
578
        /** @var ProductVariantInterface $productVariant */
579
        $productVariant = $this->defaultVariantResolver->getVariant($product);
580
        $productVariant->setOnHand($productVariant->getOnHand() - $quantity);
581
582
        $this->objectManager->flush();
583
    }
584
585
    /**
586
     * @Given /^(this product) is tracked by the inventory$/
587
     * @Given /^(?:|the )("[^"]+" product) is(?:| also) tracked by the inventory$/
588
     */
589
    public function thisProductIsTrackedByTheInventory(ProductInterface $product)
590
    {
591
        /** @var ProductVariantInterface $productVariant */
592
        $productVariant = $this->defaultVariantResolver->getVariant($product);
593
        $productVariant->setTracked(true);
594
595
        $this->objectManager->flush();
596
    }
597
598
    /**
599
     * @Given /^(this product) is available in "([^"]+)" ([^"]+) priced at ("[^"]+")$/
600
     */
601
    public function thisProductIsAvailableInSize(ProductInterface $product, $optionValueName, $optionName, $price)
602
    {
603
        /** @var ProductVariantInterface $variant */
604
        $variant = $this->productVariantFactory->createNew();
605
606
        $optionValue = $this->sharedStorage->get(sprintf('%s_option_%s_value', $optionValueName, $optionName));
607
608
        $variant->addOptionValue($optionValue);
609
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $this->sharedStorage->get('channel')));
610
        $variant->setCode(sprintf("%s_%s", $product->getCode(), $optionValueName));
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal %s_%s does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
611
        $variant->setName($product->getName());
612
613
        $product->addVariant($variant);
614
        $this->objectManager->flush();
615
    }
616
617
    /**
618
     * @Given the :product product's :optionValueName size belongs to :shippingCategory shipping category
619
     */
620
    public function thisProductSizeBelongsToShippingCategory(ProductInterface $product, $optionValueName, ShippingCategoryInterface $shippingCategory)
621
    {
622
        $code = sprintf("%s_%s", $product->getCode(), $optionValueName);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal %s_%s does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
623
        /** @var ProductVariantInterface $productVariant */
624
        $productVariant = $product->getVariants()->filter(function ($variant) use ($code) {
625
            return $code === $variant->getCode();
626
        })->first();
627
628
        Assert::notNull($productVariant, sprintf('Product variant with given code %s not exists!', $code));
629
630
        $productVariant->setShippingCategory($shippingCategory);
631
        $this->objectManager->flush();
632
    }
633
634
    /**
635
     * @Given /^(this product) has (this product option)$/
636
     * @Given /^(this product) has (?:a|an) ("[^"]+" option)$/
637
     */
638
    public function thisProductHasThisProductOption(ProductInterface $product, ProductOptionInterface $option)
639
    {
640
        $product->addOption($option);
641
642
        $this->objectManager->flush();
643
    }
644
645
    /**
646
     * @Given /^there are ([^"]+) units of ("[^"]+" variant of product "[^"]+") available in the inventory$/
647
     */
648
    public function thereAreItemsOfProductInVariantAvailableInTheInventory($quantity, ProductVariantInterface $productVariant)
649
    {
650
        $productVariant->setTracked(true);
651
        $productVariant->setOnHand($quantity);
652
653
        $this->objectManager->flush();
654
    }
655
656
    /**
657
     * @Given /^the ("[^"]+" product variant) is tracked by the inventory$/
658
     */
659
    public function theProductVariantIsTrackedByTheInventory(ProductVariantInterface $productVariant)
660
    {
661
        $productVariant->setTracked(true);
662
663
        $this->objectManager->flush();
664
    }
665
666
    /**
667
     * @Given /^(this product)'s price is ("[^"]+")$/
668
     * @Given /^the (product "[^"]+") changed its price to ("[^"]+")$/
669
     */
670
    public function theProductChangedItsPriceTo(ProductInterface $product, $price)
671
    {
672
        /** @var ProductVariantInterface $productVariant */
673
        $productVariant = $this->defaultVariantResolver->getVariant($product);
674
        $channelPricing = $productVariant->getChannelPricingForChannel($this->sharedStorage->get('channel'));
675
        $channelPricing->setPrice($price);
676
677
        $this->objectManager->flush();
678
    }
679
680
    /**
681
     * @Given /^(this product) has(?:| also) an image "([^"]+)" with a code "([^"]+)"$/
682
     * @Given /^the ("[^"]+" product) has(?:| also) an image "([^"]+)" with a code "([^"]+)"$/
683
     */
684
    public function thisProductHasAnImageWithACode(ProductInterface $product, $imagePath, $imageCode)
685
    {
686
        $filesPath = $this->getParameter('files_path');
687
688
        /** @var ImageInterface $productImage */
689
        $productImage = $this->productImageFactory->createNew();
690
        $productImage->setFile(new UploadedFile($filesPath.$imagePath, basename($imagePath)));
691
        $productImage->setCode($imageCode);
692
        $this->imageUploader->upload($productImage);
693
694
        $product->addImage($productImage);
695
696
        $this->objectManager->flush($product);
697
    }
698
699
    /**
700
     * @Given /^(this product) belongs to ("([^"]+)" shipping category)$/
701
     */
702
    public function thisProductBelongsToShippingCategory(ProductInterface $product, ShippingCategoryInterface $shippingCategory)
703
    {
704
        $product->getVariants()->first()->setShippingCategory($shippingCategory);
705
        $this->objectManager->flush();
706
    }
707
708
    /**
709
     * @param string $type
710
     * @param string $name
711
     * @param string|null $code
712
     *
713
     * @return ProductAttributeInterface
714
     */
715
    private function createProductAttribute($type, $name, $code = null)
716
    {
717
        $productAttribute = $this->productAttributeFactory->createTyped($type);
718
719
        if (null === $code) {
720
            $code = StringInflector::nameToCode($name);
721
        }
722
723
        $productAttribute->setCode($code);
724
        $productAttribute->setName($name);
725
726
        $this->objectManager->persist($productAttribute);
727
728
        return $productAttribute;
729
    }
730
731
    /**
732
     * @param string $value
733
     * @param ProductAttributeInterface $attribute
734
     *
735
     * @return ProductAttributeValueInterface
736
     */
737
    private function createProductAttributeValue($value, ProductAttributeInterface $attribute)
738
    {
739
        /** @var ProductAttributeValueInterface $attributeValue */
740
        $attributeValue = $this->attributeValueFactory->createNew();
741
        $attributeValue->setAttribute($attribute);
742
        $attributeValue->setValue($value);
743
744
        $this->objectManager->persist($attributeValue);
745
746
        return $attributeValue;
747
    }
748
749
    /**
750
     * @param string $price
751
     *
752
     * @return int
753
     */
754
    private function getPriceFromString($price)
755
    {
756
        return (int) round(($price * 100), 2);
757
    }
758
759
    /**
760
     * @param string $productName
761
     * @param int $price
762
     * @param string|null $date
763
     * @param ChannelInterface|null $channel
764
     *
765
     * @return ProductInterface
766
     */
767
    private function createProduct($productName, $price = 100, $date = null, ChannelInterface $channel = null)
768
    {
769
        /** @var ProductInterface $product */
770
        $product = $this->productFactory->createWithVariant();
771
772
        $product->setName($productName);
773
        $product->setCode(StringInflector::nameToUppercaseCode($productName));
774
        $product->setSlug($this->slugGenerator->generate($productName));
775
        $product->setCreatedAt(new \DateTime($date));
776
777
        /** @var ProductVariantInterface $productVariant */
778
        $productVariant = $this->defaultVariantResolver->getVariant($product);
779
780
        if (null === $channel && $this->sharedStorage->has('channel')) {
781
            $channel = $this->sharedStorage->get('channel');
782
        }
783
784
        if (null !== $channel) {
785
            $productVariant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
786
        }
787
        $productVariant->setCode($product->getCode());
788
        $productVariant->setName($product->getName());
789
790
        return $product;
791
    }
792
793
    /**
794
     * @param ProductOptionInterface $option
795
     * @param string $value
796
     * @param string $code
797
     *
798
     * @return ProductOptionValueInterface
799
     */
800
    private function addProductOption(ProductOptionInterface $option, $value, $code)
801
    {
802
        /** @var ProductOptionValueInterface $optionValue */
803
        $optionValue = $this->productOptionValueFactory->createNew();
804
805
        $optionValue->setValue($value);
806
        $optionValue->setCode($code);
807
        $optionValue->setOption($option);
808
809
        $option->addValue($optionValue);
810
811
        return $optionValue;
812
    }
813
814
    /**
815
     * @param ProductInterface $product
816
     */
817
    private function saveProduct(ProductInterface $product)
818
    {
819
        $this->productRepository->add($product);
820
        $this->sharedStorage->set('product', $product);
821
    }
822
823
    /**
824
     * @param string $name
825
     *
826
     * @return NodeElement
827
     */
828
    private function getParameter($name)
829
    {
830
        return isset($this->minkParameters[$name]) ? $this->minkParameters[$name] : null;
831
    }
832
833
    /**
834
     * @param ProductInterface $product
835
     * @param $productVariantName
836
     * @param int $price
837
     * @param string $code
838
     * @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...
839
     *
840
     * @return ProductVariantInterface
841
     */
842
    private function createProductVariant(
843
        ProductInterface $product,
844
        $productVariantName,
845
        $price,
846
        $code,
847
        ChannelInterface $channel = null
848
    ) {
849
        $product->setVariantSelectionMethod(ProductInterface::VARIANT_SELECTION_CHOICE);
850
851
        /** @var ProductVariantInterface $variant */
852
        $variant = $this->productVariantFactory->createNew();
853
854
        $variant->setName($productVariantName);
855
        $variant->setCode($code);
856
        $variant->setProduct($product);
857
        $variant->addChannelPricing($this->createChannelPricingForChannel($price, $channel));
858
859
        $product->addVariant($variant);
860
861
        $this->objectManager->flush();
862
        $this->sharedStorage->set('variant', $variant);
863
864
        return $variant;
865
    }
866
867
    /**
868
     * @param ProductInterface $product
869
     * @param string $name
870
     * @param string $locale
871
     */
872
    private function addProductTranslation(ProductInterface $product, $name, $locale)
873
    {
874
        /** @var ProductTranslationInterface|TranslationInterface $translation */
875
        $translation = $this->productTranslationFactory->createNew();
876
        $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...
877
        $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...
878
        $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...
879
880
        $product->addTranslation($translation);
0 ignored issues
show
Bug introduced by
It seems like $translation defined by $this->productTranslationFactory->createNew() on line 875 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...
881
    }
882
883
    /**
884
     * @param ProductVariantInterface $productVariant
885
     * @param string $name
886
     * @param string $locale
887
     */
888
    private function addProductVariantTranslation(ProductVariantInterface $productVariant, $name, $locale)
889
    {
890
        /** @var ProductVariantTranslationInterface|TranslationInterface $translation */
891
        $translation = $this->productVariantTranslationFactory->createNew();
892
        $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...
893
        $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...
894
895
        $productVariant->addTranslation($translation);
0 ignored issues
show
Bug introduced by
It seems like $translation defined by $this->productVariantTra...ionFactory->createNew() on line 891 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...
896
    }
897
898
    /**
899
     * @param int $price
900
     * @param ChannelInterface|null $channel
901
     *
902
     * @return ChannelPricingInterface
903
     */
904
    private function createChannelPricingForChannel($price, ChannelInterface $channel = null)
905
    {
906
        /** @var ChannelPricingInterface $channelPricing */
907
        $channelPricing = $this->channelPricingFactory->createNew();
908
        $channelPricing->setPrice($price);
909
        $channelPricing->setChannel($channel);
910
911
        return $channelPricing;
912
    }
913
}
914